This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
regexec.c: Replace loop by memchr()
[perl5.git] / regexec.c
index bf9809a..5c9d7b6 100644 (file)
--- a/regexec.c
+++ b/regexec.c
@@ -96,6 +96,12 @@ static const char* const non_utf8_target_but_utf8_required
                 = "Can't match, because target string needs to be in UTF-8\n";
 #endif
 
+/* Returns a boolean as to whether the input unsigned number is a power of 2
+ * (2**0, 2**1, etc).  In other words if it has just a single bit set.
+ * If not, subtracting 1 would leave the uppermost bit set, so the & would
+ * yield non-zero */
+#define isPOWER_OF_2(n) ((n & (n-1)) == 0)
+
 #define NON_UTF8_TARGET_BUT_UTF8_REQUIRED(target) STMT_START {           \
     DEBUG_EXECUTE_r(Perl_re_printf( aTHX_  "%s", non_utf8_target_but_utf8_required));\
     goto target;                                                         \
@@ -126,13 +132,16 @@ static const char* const non_utf8_target_but_utf8_required
                     (U8*)(off >= 0 ? reginfo->strend : reginfo->strbeg)) \
            : (U8*)(pos + off))
 
-#define HOPBACKc(pos, off) \
-       (char*)(reginfo->is_utf8_target \
-           ? reghopmaybe3((U8*)pos, (SSize_t)0-off, (U8*)(reginfo->strbeg)) \
-           : (pos - off >= reginfo->strbeg)    \
-               ? (U8*)pos - off                \
+/* like HOPMAYBE3 but backwards. lim must be +ve. Returns NULL on overshoot */
+#define HOPBACK3(pos, off, lim) \
+       (reginfo->is_utf8_target                          \
+           ? reghopmaybe3((U8*)pos, (SSize_t)0-off, (U8*)(lim)) \
+           : (pos - off >= lim)                                 \
+               ? (U8*)pos - off                                 \
                : NULL)
 
+#define HOPBACKc(pos, off) ((char*)HOPBACK3(pos, off, reginfo->strbeg))
+
 #define HOP3(pos,off,lim) (reginfo->is_utf8_target  ? reghop3((U8*)(pos), off, (U8*)(lim)) : (U8*)(pos + off))
 #define HOP3c(pos,off,lim) ((char*)HOP3(pos,off,lim))
 
@@ -543,6 +552,116 @@ S_isFOO_utf8_lc(pTHX_ const U8 classnum, const U8* character)
     return FALSE; /* Things like CNTRL are always below 256 */
 }
 
+STATIC char *
+S_find_next_ascii(char * s, const char * send, const bool utf8_target)
+{
+    /* Returns the position of the first ASCII byte in the sequence between 's'
+     * and 'send-1' inclusive; returns 'send' if none found */
+
+    PERL_ARGS_ASSERT_FIND_NEXT_ASCII;
+
+#ifndef EBCDIC
+
+    if ((STRLEN) (send - s) >= PERL_WORDSIZE
+
+                            /* This term is wordsize if subword; 0 if not */
+                          + PERL_WORDSIZE * PERL_IS_SUBWORD_ADDR(s)
+
+                            /* 'offset' */
+                          - (PTR2nat(s) & PERL_WORD_BOUNDARY_MASK))
+    {
+
+        /* Process per-byte until reach word boundary.  XXX This loop could be
+         * eliminated if we knew that this platform had fast unaligned reads */
+        while (PTR2nat(s) & PERL_WORD_BOUNDARY_MASK) {
+            if (isASCII(*s)) {
+                return s;
+            }
+            s++;    /* khw didn't bother creating a separate loop for
+                       utf8_target */
+        }
+
+        /* Here, we know we have at least one full word to process.  Process
+         * per-word as long as we have at least a full word left */
+        do {
+            if ((* (PERL_UINTMAX_T *) s) & ~ PERL_VARIANTS_WORD_MASK)  {
+                break;
+            }
+            s += PERL_WORDSIZE;
+        } while (s + PERL_WORDSIZE <= send);
+    }
+
+#endif
+
+    /* Process per-character */
+    if (utf8_target) {
+        while (s < send) {
+            if (isASCII(*s)) {
+                return s;
+            }
+            s += UTF8SKIP(s);
+        }
+    }
+    else {
+        while (s < send) {
+            if (isASCII(*s)) {
+                return s;
+            }
+            s++;
+        }
+    }
+
+    return s;
+}
+
+STATIC char *
+S_find_next_non_ascii(char * s, const char * send, const bool utf8_target)
+{
+    /* Returns the position of the first non-ASCII byte in the sequence between
+     * 's' and 'send-1' inclusive; returns 'send' if none found */
+
+#ifdef EBCDIC
+
+    PERL_ARGS_ASSERT_FIND_NEXT_NON_ASCII;
+
+    if (utf8_target) {
+        while (s < send) {
+            if ( ! isASCII(*s)) {
+                return s;
+            }
+            s += UTF8SKIP(s);
+        }
+    }
+    else {
+        while (s < send) {
+            if ( ! isASCII(*s)) {
+                return s;
+            }
+            s++;
+        }
+    }
+
+    return s;
+
+#else
+
+    const U8 * next_non_ascii = NULL;
+
+    PERL_ARGS_ASSERT_FIND_NEXT_NON_ASCII;
+    PERL_UNUSED_ARG(utf8_target);
+
+    /* On ASCII platforms invariants and ASCII are identical, so if the string
+     * is entirely invariants, there is no non-ASCII character */
+    return (is_utf8_invariant_string_loc((U8 *) s,
+                                         (STRLEN) (send - s),
+                                         &next_non_ascii))
+            ? (char *) send
+            : (char *) next_non_ascii;
+
+#endif
+
+}
+
 /*
  * pregexec and friends
  */
@@ -704,7 +823,7 @@ Perl_re_intuit_start(pTHX_
        goto fail;
     }
 
-    RX_MATCH_UTF8_set(rx,utf8_target);
+    RXp_MATCH_UTF8_set(prog, utf8_target);
     reginfo->is_utf8_target = cBOOL(utf8_target);
     reginfo->info_aux = NULL;
     reginfo->strbeg = strbeg;
@@ -838,7 +957,7 @@ Perl_re_intuit_start(pTHX_
 #ifdef DEBUGGING       /* 7/99: reports of failure (with the older version) */
     if (end_shift < 0)
        Perl_croak(aTHX_ "panic: end_shift: %" IVdf " pattern:\n%s\n ",
-                  (IV)end_shift, RX_PRECOMP(prog));
+                  (IV)end_shift, RX_PRECOMP(rx));
 #endif
 
   restart:
@@ -884,7 +1003,9 @@ Perl_re_intuit_start(pTHX_
                 (IV)prog->check_end_shift);
         });
         
-        end_point = HOP3(strend, -end_shift, strbeg);
+        end_point = HOPBACK3(strend, end_shift, rx_origin);
+        if (!end_point)
+            goto fail_finish;
         start_point = HOPMAYBE3(rx_origin, start_shift, end_point);
         if (!start_point)
             goto fail_finish;
@@ -902,19 +1023,28 @@ Perl_re_intuit_start(pTHX_
             && prog->intflags & PREGf_ANCH
             && prog->check_offset_max != SSize_t_MAX)
         {
-            SSize_t len = SvCUR(check) - !!SvTAIL(check);
+            SSize_t check_len = SvCUR(check) - !!SvTAIL(check);
             const char * const anchor =
                         (prog->intflags & PREGf_ANCH_GPOS ? strpos : strbeg);
+            SSize_t targ_len = (char*)end_point - anchor;
+
+            if (check_len > targ_len) {
+                DEBUG_EXECUTE_r(Perl_re_printf( aTHX_
+                             "Anchored string too short...\n"));
+                goto fail_finish;
+            }
 
             /* do a bytes rather than chars comparison. It's conservative;
              * so it skips doing the HOP if the result can't possibly end
              * up earlier than the old value of end_point.
              */
-            if ((char*)end_point - anchor > prog->check_offset_max) {
+            assert(anchor + check_len <= (char *)end_point);
+            if (prog->check_offset_max + check_len < targ_len) {
                 end_point = HOP3lim((U8*)anchor,
                                 prog->check_offset_max,
-                                end_point -len)
-                            + len;
+                                end_point - check_len
+                            )
+                            + check_len;
             }
         }
 
@@ -2359,6 +2489,22 @@ S_find_byclass(pTHX_ regexp * prog, const regnode *c, char *s,
         );
         break;
 
+    case ASCII:
+        s = find_next_ascii(s, strend, utf8_target);
+        if (s < strend && (reginfo->intuit || regtry(reginfo, &s))) {
+            goto got_it;
+        }
+
+        break;
+
+    case NASCII:
+        s = find_next_non_ascii(s, strend, utf8_target);
+        if (s < strend && (reginfo->intuit || regtry(reginfo, &s))) {
+            goto got_it;
+        }
+
+        break;
+
     /* The argument to all the POSIX node types is the class number to pass to
      * _generic_isCC() to build a mask for searching in PL_charclass[] */
 
@@ -2392,12 +2538,19 @@ S_find_byclass(pTHX_ regexp * prog, const regnode *c, char *s,
         }
 
         to_complement = 1;
-        /* FALLTHROUGH */
+        goto posixa;
 
     case POSIXA:
-      posixa:
         /* Don't need to worry about utf8, as it can match only a single
-         * byte invariant character. */
+         * byte invariant character.  But we do anyway for performance reasons,
+         * as otherwise we would have to examine all the continuation
+         * characters */
+        if (utf8_target) {
+            REXEC_FBC_UTF8_CLASS_SCAN(_generic_isCC_A(*s, FLAGS(c)));
+            break;
+        }
+
+      posixa:
         REXEC_FBC_CLASS_SCAN(
                         to_complement ^ cBOOL(_generic_isCC_A(*s, FLAGS(c))));
         break;
@@ -2766,7 +2919,7 @@ S_reg_set_capture_string(pTHX_ REGEXP * const rx,
             }
             else {
                 /* create new COW SV to share string */
-                RX_MATCH_COPY_FREE(rx);
+                RXp_MATCH_COPY_FREE(prog);
                 prog->saved_copy = sv_setsv_cow(prog->saved_copy, sv);
             }
             prog->subbeg = (char *)SvPVX_const(prog->saved_copy);
@@ -2829,7 +2982,7 @@ S_reg_set_capture_string(pTHX_ REGEXP * const rx,
             assert(min >= 0 && min <= max && min <= strend - strbeg);
             sublen = max - min;
 
-            if (RX_MATCH_COPIED(rx)) {
+            if (RXp_MATCH_COPIED(prog)) {
                 if (sublen > prog->sublen)
                     prog->subbeg =
                             (char*)saferealloc(prog->subbeg, sublen+1);
@@ -2840,7 +2993,7 @@ S_reg_set_capture_string(pTHX_ REGEXP * const rx,
             prog->subbeg[sublen] = '\0';
             prog->suboffset = min;
             prog->sublen = sublen;
-            RX_MATCH_COPIED_on(rx);
+            RXp_MATCH_COPIED_on(prog);
         }
         prog->subcoffset = prog->suboffset;
         if (prog->suboffset && utf8_target) {
@@ -2867,7 +3020,7 @@ S_reg_set_capture_string(pTHX_ REGEXP * const rx,
         }
     }
     else {
-        RX_MATCH_COPY_FREE(rx);
+        RXp_MATCH_COPY_FREE(prog);
         prog->subbeg = strbeg;
         prog->suboffset = 0;
         prog->subcoffset = 0;
@@ -3024,7 +3177,7 @@ Perl_regexec_flags(pTHX_ REGEXP * const rx, char *stringarg, char *strend,
             /* match via INTUIT shouldn't have any captures.
              * Let @-, @+, $^N know */
             prog->lastparen = prog->lastcloseparen = 0;
-            RX_MATCH_UTF8_set(rx, utf8_target);
+            RXp_MATCH_UTF8_set(prog, utf8_target);
             prog->offs[0].start = s - strbeg;
             prog->offs[0].end = utf8_target
                 ? (char*)utf8_hop((U8*)s, prog->minlenret) - strbeg
@@ -3051,8 +3204,8 @@ Perl_regexec_flags(pTHX_ REGEXP * const rx, char *stringarg, char *strend,
        Perl_croak(aTHX_ "corrupted regexp program");
     }
 
-    RX_MATCH_TAINTED_off(rx);
-    RX_MATCH_UTF8_set(rx, utf8_target);
+    RXp_MATCH_TAINTED_off(prog);
+    RXp_MATCH_UTF8_set(prog, utf8_target);
 
     reginfo->prog = rx;         /* Yes, sorry that this is confusing.  */
     reginfo->intuit = 0;
@@ -3374,7 +3527,7 @@ Perl_regexec_flags(pTHX_ REGEXP * const rx, char *stringarg, char *strend,
             regprop(prog, prop, c, reginfo, NULL);
            {
                RE_PV_QUOTED_DECL(quoted,utf8_target,PERL_DEBUG_PAD_ZERO(1),
-                   s,strend-s,60);
+                   s,strend-s,PL_dump_re_max_len);
                 Perl_re_printf( aTHX_
                    "Matching stclass %.*s against %s (%d bytes)\n",
                    (int)SvCUR(prop), SvPVX_const(prop),
@@ -3894,10 +4047,10 @@ S_debug_start_match(pTHX_ const REGEXP *prog, const bool utf8_target,
             reginitcolors();    
     {
         RE_PV_QUOTED_DECL(s0, utf8_pat, PERL_DEBUG_PAD_ZERO(0), 
-            RX_PRECOMP_const(prog), RX_PRELEN(prog), 60);   
+            RX_PRECOMP_const(prog), RX_PRELEN(prog), PL_dump_re_max_len);
         
         RE_PV_QUOTED_DECL(s1, utf8_target, PERL_DEBUG_PAD_ZERO(1),
-            start, end - start, 60); 
+            start, end - start, PL_dump_re_max_len);
         
         Perl_re_printf( aTHX_
             "%s%s REx%s %s against %s\n", 
@@ -3953,11 +4106,11 @@ S_dump_exec_pos(pTHX_ const char *locinput,
        const int is_uni = utf8_target ? 1 : 0;
 
        RE_PV_COLOR_DECL(s0,len0,is_uni,PERL_DEBUG_PAD(0),
-           (locinput - pref_len),pref0_len, 60, 4, 5);
+           (locinput - pref_len),pref0_len, PL_dump_re_max_len, 4, 5);
        
        RE_PV_COLOR_DECL(s1,len1,is_uni,PERL_DEBUG_PAD(1),
                    (locinput - pref_len + pref0_len),
-                   pref_len - pref0_len, 60, 2, 3);
+                   pref_len - pref0_len, PL_dump_re_max_len, 2, 3);
        
        RE_PV_COLOR_DECL(s2,len2,is_uni,PERL_DEBUG_PAD(2),
                    locinput, loc_regeol - locinput, 10, 0, 1);
@@ -4340,7 +4493,7 @@ S_isGCB(pTHX_ const GCB_enum before, const GCB_enum after, const U8 * const strb
                 /* Do not break within emoji flag sequences. That is, do not
                  * break between regional indicator (RI) symbols if there is an
                  * odd number of RI characters before the break point.
-                 *  GB12     ^ (RI RI)* RI × RI
+                 *  GB12   sot (RI RI)* RI × RI
                  *  GB13 [^RI] (RI RI)* RI × RI */
 
                 while (backup_one_GCB(strbeg,
@@ -4632,7 +4785,7 @@ S_isLB(pTHX_ LB_enum before,
                  * only if there are an even number of regional indicators
                  * preceding the position of the break.
                  *
-                 *  sot (RI RI)* RI × RI
+                 *    sot (RI RI)* RI × RI
                  *  [^RI] (RI RI)* RI × RI */
 
                 while (backup_one_LB(strbeg,
@@ -5162,7 +5315,7 @@ S_isWB(pTHX_ WB_enum previous,
                  * odd number of RI characters before the potential break
                  * point.
                  *
-                 * WB15     ^ (RI RI)* RI × RI
+                 * WB15   sot (RI RI)* RI × RI
                  * WB16 [^RI] (RI RI)* RI × RI */
 
                 while (backup_one_WB(&previous,
@@ -5370,7 +5523,7 @@ S_regmatch(pTHX_ regmatch_info *reginfo, char *startpos, regnode *prog)
     SV *sv_yes_mark = NULL; /* last mark name we have seen 
                                during a successful match */
     U32 lastopen = 0;       /* last open we saw */
-    bool has_cutgroup = RX_HAS_CUTGROUP(rex) ? 1 : 0;   
+    bool has_cutgroup = RXp_HAS_CUTGROUP(rex) ? 1 : 0;
     SV* const oreplsv = GvSVn(PL_replgv);
     /* these three flags are set by various ops to signal information to
      * the very next op. They have a useful lifetime of exactly one loop
@@ -5391,13 +5544,13 @@ S_regmatch(pTHX_ regmatch_info *reginfo, char *startpos, regnode *prog)
     U8 gimme = G_SCALAR;
     CV *caller_cv = NULL;      /* who called us */
     CV *last_pushed_cv = NULL; /* most recently called (?{}) CV */
-    CHECKPOINT runops_cp;      /* savestack position before executing EVAL */
     U32 maxopenparen = 0;       /* max '(' index seen so far */
     int to_complement;  /* Invert the result? */
     _char_class_number classnum;
     bool is_utf8_pat = reginfo->is_utf8_pat;
     bool match = FALSE;
     I32 orig_savestack_ix = PL_savestack_ix;
+    char * script_run_begin = NULL;
 
 /* Solaris Studio 12.3 messes up fetching PL_charclass['\n'] */
 #if (defined(__SUNPRO_C) && (__SUNPRO_C == 0x5120) && defined(__x86_64) && defined(USE_64_BIT_ALL))
@@ -5431,8 +5584,6 @@ S_regmatch(pTHX_ regmatch_info *reginfo, char *startpos, regnode *prog)
     }));
 
     while (scan != NULL) {
-
-
        next = scan + NEXT_OFF(scan);
        if (next == scan)
            next = NULL;
@@ -5594,6 +5745,7 @@ S_regmatch(pTHX_ regmatch_info *reginfo, char *startpos, regnode *prog)
                 if (scan->flags == EXACTL || scan->flags == EXACTFLU8) {
                     _CHECK_AND_WARN_PROBLEMATIC_LOCALE;
                     if (utf8_target
+                        && nextchr >= 0 /* guard against negative EOS value in nextchr */
                         && UTF8_IS_ABOVE_LATIN1(nextchr)
                         && scan->flags == EXACTL)
                     {
@@ -5737,6 +5889,11 @@ S_regmatch(pTHX_ regmatch_info *reginfo, char *startpos, regnode *prog)
         {
             U8 *uc;
             if ( ST.jump ) {
+                /* undo any captures done in the tail part of a branch,
+                 * e.g.
+                 *    /(?:X(.)(.)|Y(.)).../
+                 * where the trie just matches X then calls out to do the
+                 * rest of the branch */
                 REGCP_UNWIND(ST.cp);
                 UNWIND_PAREN(ST.lastparen, ST.lastcloseparen);
            }
@@ -6351,6 +6508,22 @@ S_regmatch(pTHX_ regmatch_info *reginfo, char *startpos, regnode *prog)
            }
            break;
 
+        case ASCII:
+            if (NEXTCHR_IS_EOS || ! isASCII(UCHARAT(locinput))) {
+                sayNO;
+            }
+
+            locinput++;     /* ASCII is always single byte */
+            break;
+
+        case NASCII:
+            if (NEXTCHR_IS_EOS || isASCII(UCHARAT(locinput))) {
+                sayNO;
+            }
+
+            goto increment_locinput;
+            break;
+
         /* The argument (FLAGS) to all the POSIX node types is the class number
          * */
 
@@ -6787,7 +6960,7 @@ S_regmatch(pTHX_ regmatch_info *reginfo, char *startpos, regnode *prog)
             goto eval_recurse_doit;
             /* NOTREACHED */
 
-        case EVAL:  /*   /(?{A})B/   /(??{A})B/  and /(?(?{A})X|Y)B/   */        
+        case EVAL:  /*   /(?{...})B/   /(??{A})B/  and  /(?(?{...})X|Y)B/   */
             if (cur_eval && cur_eval->locinput==locinput) {
                if ( ++nochange_depth > max_nochange_depth )
                     Perl_croak(aTHX_ "EVAL without pos change exceeded limit in regex");
@@ -6806,7 +6979,7 @@ S_regmatch(pTHX_ regmatch_info *reginfo, char *startpos, regnode *prog)
 
                /* save *all* paren positions */
                 regcppush(rex, 0, maxopenparen);
-               REGCP_SET(runops_cp);
+                REGCP_SET(ST.lastcp);
 
                if (!caller_cv)
                    caller_cv = find_runcv(NULL);
@@ -6834,7 +7007,7 @@ S_regmatch(pTHX_ regmatch_info *reginfo, char *startpos, regnode *prog)
                 /* Some notes about MULTICALL and the context and save stacks.
                  *
                  * In something like
-                 *   /...(?{ my $x)}...(?{ my $z)}...(?{ my $z)}.../
+                 *   /...(?{ my $x)}...(?{ my $y)}...(?{ my $z)}.../
                  * since codeblocks don't introduce a new scope (so that
                  * local() etc accumulate), at the end of a successful
                  * match there will be a SAVEt_CLEARSV on the savestack
@@ -6869,7 +7042,7 @@ S_regmatch(pTHX_ regmatch_info *reginfo, char *startpos, regnode *prog)
                  * *everything* SAVEd by this function is undone and then
                  * the context popped, rather than e.g., popping the
                  * context (and restoring the original PL_comppad) then
-                 * popping more of the savestack and restoiring a bad
+                 * popping more of the savestack and restoring a bad
                  * PL_comppad.
                  */
 
@@ -6973,7 +7146,7 @@ S_regmatch(pTHX_ regmatch_info *reginfo, char *startpos, regnode *prog)
                if (logical == 0)        /*   (?{})/   */
                    sv_setsv(save_scalar(PL_replgv), ret); /* $^R */
                else if (logical == 1) { /*   /(?(?{...})X|Y)/    */
-                   sw = cBOOL(SvTRUE(ret));
+                   sw = cBOOL(SvTRUE_NN(ret));
                    logical = 0;
                }
                else {                   /*  /(??{})  */
@@ -7008,12 +7181,14 @@ S_regmatch(pTHX_ regmatch_info *reginfo, char *startpos, regnode *prog)
                 * in the regexp code uses the pad ! */
                PL_op = oop;
                PL_curcop = ocurcop;
-                regcp_restore(rex, runops_cp, &maxopenparen);
+                regcp_restore(rex, ST.lastcp, &maxopenparen);
                 PL_curpm_under = PL_curpm;
                 PL_curpm = PL_reg_curpm;
 
-               if (logical != 2)
-                   break;
+               if (logical != 2) {
+                    PUSH_STATE_GOTO(EVAL_B, next, locinput);
+                   /* NOTREACHED */
+                }
            }
 
                /* only /(??{})/  from now on */
@@ -7111,11 +7286,11 @@ S_regmatch(pTHX_ regmatch_info *reginfo, char *startpos, regnode *prog)
                ST.prev_eval = cur_eval;
                cur_eval = st;
                /* now continue from first node in postoned RE */
-               PUSH_YES_STATE_GOTO(EVAL_AB, startpoint, locinput);
+               PUSH_YES_STATE_GOTO(EVAL_postponed_AB, startpoint, locinput);
                NOT_REACHED; /* NOTREACHED */
        }
 
-       case EVAL_AB: /* cleanup after a successful (??{A})B */
+       case EVAL_postponed_AB: /* cleanup after a successful (??{A})B */
             /* note: this is called twice; first after popping B, then A */
             DEBUG_STACK_r({
                 Perl_re_exec_indentf( aTHX_  "EVAL_AB cur_eval=%p prev_eval=%p\n",
@@ -7161,7 +7336,11 @@ S_regmatch(pTHX_ regmatch_info *reginfo, char *startpos, regnode *prog)
            sayYES;
 
 
-       case EVAL_AB_fail: /* unsuccessfully ran A or B in (??{A})B */
+       case EVAL_B_fail: /* unsuccessful B in (?{...})B */
+           REGCP_UNWIND(ST.lastcp);
+            sayNO;
+
+       case EVAL_postponed_AB_fail: /* unsuccessfully ran A or B in (??{A})B */
            /* note: this is called twice; first after popping B, then A */
             DEBUG_STACK_r({
                 Perl_re_exec_indentf( aTHX_  "EVAL_AB_fail cur_eval=%p prev_eval=%p\n",
@@ -7207,6 +7386,10 @@ S_regmatch(pTHX_ regmatch_info *reginfo, char *startpos, regnode *prog)
             lastopen = n;
            break;
 
+        case SROPEN: /*  (*SCRIPT_RUN:  */
+            script_run_begin = locinput;
+            break;
+
 /* XXX really need to log other places start/end are set too */
 #define CLOSE_CAPTURE                                                      \
     rex->offs[n].start = rex->offs[n].start_tmp;                           \
@@ -7232,6 +7415,362 @@ S_regmatch(pTHX_ regmatch_info *reginfo, char *startpos, regnode *prog)
 
            break;
 
+        case SRCLOSE:  /*  (*SCRIPT_RUN: ... )   */
+          {
+            /* Checks that every character in the sequence started by SROPEN
+             * and ending here is one of three scripts: Common, Inherited, and
+             * possibly one other.  Additionally all decimal digits must come
+             * from the same consecutive sequence of 10.
+             *
+             * Basically, it looks at each character in the sequence to see if
+             * the above conditions are met; if not it fails.  It uses an
+             * inversion map to find the enum corresponding to the script of
+             * each character.  But this is complicated by the fact that a few
+             * code points can be in any of several scripts.  The data has been
+             * constructed so that there are additional enum values (all
+             * negative) for these situations.  The absolute value of those is
+             * an index into another table which contains pointers to auxiliary
+             * tables for each such situation.  Each aux array lists all the
+             * scripts for the given situation.  There is another, parallel,
+             * table that gives the number of entries in each aux table.  These
+             * are all defined in charclass_invlists.h */
+
+            /* XXX Here are the additional things UTS 39 says could be done:
+             * Mark Chinese strings as “mixed script” if they contain both
+             * simplified (S) and traditional (T) Chinese characters, using the
+             * Unihan data in the Unicode Character Database [UCD].  The
+             * criterion can only be applied if the language of the string is
+             * known to be Chinese. So, for example, the string
+             * “写真だけの結婚式 ” is Japanese, and should not be marked as
+             * mixed script because of a mixture of S and T characters.
+             * Testing for whether a character is S or T needs to be based not
+             * on whether the character has a S or T variant , but whether the
+             * character is an S or T variant. khw notes that the sample
+             * contains a Hiragana character, and it is unclear if absence of
+             * any foreign script marks the script as "Chinese"
+             *
+             * Forbid sequences of the same nonspacing mark
+             *
+             * Check to see that all the characters are in the sets of exemplar
+             * characters for at least one language in the Unicode Common
+             * Locale Data Repository [CLDR]. */
+
+
+            /* Things that match /\d/u */
+            SV * decimals_invlist = PL_XPosix_ptrs[_CC_DIGIT];
+            UV * decimals_array = invlist_array(decimals_invlist);
+
+            /* What code point is the digit '0' of the script run? */
+            UV zero_of_run = 0;
+            SCX_enum script_of_run  = SCX_INVALID;   /* Illegal value */
+            SCX_enum script_of_char = SCX_INVALID;
+
+            /* If the script remains not fully determined from iteration to
+             * iteration, this is the current intersection of the possiblities.
+             * */
+            SCX_enum * intersection = NULL;
+            PERL_UINT_FAST8_T intersection_len = 0;
+
+            const char * s = script_run_begin;
+            const char * strend = locinput;
+
+            assert(s);
+
+            /* Look at each character in the sequence */
+            while (s < strend) {
+                UV cp;
+
+                /* The code allows all scripts to use the ASCII digits.  This
+                 * is because they are used in commerce even in scripts that
+                 * have their own set.  Hence any ASCII ones found are ok,
+                 * unless a digit from another set has already been
+                 * encountered.  (The other digit ranges in Common are not
+                 * similarly blessed */
+                if (UNLIKELY(isDIGIT(*s))) {
+                    if (zero_of_run > 0) {
+                        if (zero_of_run != '0') {
+                            Safefree(intersection);
+                            sayNO;
+                        }
+                    }
+                    else {
+                        zero_of_run = '0';
+                    }
+                    s++;
+                    continue;
+                }
+
+                /* Here, isn't an ASCII digit.  Find the code point of the
+                 * character */
+                if (utf8_target && ! UTF8_IS_INVARIANT(*s)) {
+                    Size_t len;
+                    cp = valid_utf8_to_uvchr((U8 *) s, &len);
+                    s += len;
+                }
+                else {
+                    cp = *(s++);
+                }
+
+                /* If is within the range [+0 .. +9] of the script's zero, it
+                 * also is a digit in that script.  We can skip the rest of
+                 * this code for this character. */
+                if (UNLIKELY(   zero_of_run > 0
+                             && cp >= zero_of_run
+                             && cp - zero_of_run <= 9))
+                {
+                    continue;
+                }
+
+                /* Find the character's script.  The correct values are
+                 * hard-coded here for small-enough code points. */
+                if (cp < 0x2B9) {   /* From inspection of Unicode db; extremely
+                                       unlikely to change */
+                    if (       cp > 255
+                        || (   isALPHA_L1(cp)
+                            && LIKELY(cp != MICRO_SIGN_NATIVE)))
+                    {
+                        script_of_char = SCX_Latin;
+                    }
+                    else {
+                        script_of_char = SCX_Common;
+                    }
+                }
+                else {
+                    script_of_char = _Perl_SCX_invmap[
+                           _invlist_search(PL_SCX_invlist, cp)];
+                }
+
+                /* We arbitrarily accept a single unassigned character, but not
+                 * in combination with anything else, and not a run of them. */
+                if (   UNLIKELY(script_of_run == SCX_Unknown)
+                    || UNLIKELY(   script_of_run != SCX_INVALID
+                                && script_of_char == SCX_Unknown))
+                {
+                    Safefree(intersection);
+                    sayNO;
+                }
+
+                if (UNLIKELY(script_of_char == SCX_Unknown)) {
+                        script_of_run = SCX_Unknown;
+                        continue;
+                }
+
+                /* We accept 'inherited' script characters currently even at
+                 * the beginning.  (We know that no characters in Inherited
+                 * are digits, or we'd have to check for that) */
+                if (UNLIKELY(script_of_char == SCX_Inherited)) {
+                    continue;
+                }
+
+                /* If unknown, the run's script is set to the char's */
+                if (UNLIKELY(script_of_run == SCX_INVALID)) {
+                    script_of_run = script_of_char;
+                }
+
+                /* All decimal digits must be from the same sequence of 10.
+                 * Above, we handled any ASCII digits without descending to
+                 * here.  We also handled the case where we already knew what
+                 * digit sequence is the one to use, and the character is in
+                 * that sequence.  Now that we know the script, we can use
+                 * script_zeros[] to directly find which sequence the script
+                 * uses, except in a few cases it returns 0 */
+                if (UNLIKELY(zero_of_run == 0) && script_of_char >= 0) {
+                    zero_of_run = script_zeros[script_of_char];
+                }
+
+                /* Now we can see if the script of the character is the same as
+                 * that of the run, or 'Common' which is considered to be in
+                 * every script */
+                if (LIKELY(   script_of_char == script_of_run
+                           || script_of_char == SCX_Common))
+                {   /* By far the most common case */
+                    goto scripts_match;
+                }
+
+#ifndef HAS_SCX_AUX_TABLES
+
+                /* Too early a Unicode version to have a code point belonging
+                 * to more than one script, so, if the scripts don't exactly
+                 * match, fail */
+                Safefree(intersection);
+                sayNO;
+
+#else
+
+                /* Here there is no exact match between the character's script
+                 * and the run's.  Negative script numbers signify that the
+                 * value may be any of several scripts, and we need to look at
+                 * auxiliary information to make our deterimination.  But if
+                 * both are non-negative, we can fail now */
+                if (LIKELY(script_of_char >= 0)) {
+                    const SCX_enum * search_in;
+                    PERL_UINT_FAST8_T search_in_len;
+                    PERL_UINT_FAST8_T i;
+
+                    if (LIKELY(script_of_run >= 0)) {
+                        Safefree(intersection);
+                        sayNO;
+                    }
+
+                    /* Use any previously constructed set of possible scripts.
+                     * */
+                    if (intersection) {
+                        search_in = intersection;
+                        search_in_len = intersection_len;
+                    }
+                    else {
+                        search_in = SCX_AUX_TABLE_ptrs[-script_of_run];
+                        search_in_len = SCX_AUX_TABLE_lengths[-script_of_run];
+                    }
+
+                    for (i = 0; i < search_in_len; i++) {
+                        if (search_in[i] == script_of_char) {
+                            script_of_run = script_of_char;
+                            goto scripts_match;
+                        }
+                    }
+
+                    Safefree(intersection);
+                    sayNO;
+                }
+                else if (LIKELY(script_of_run >= 0)) {
+                    /* script of character could be one of several, but run is
+                     * a single script */
+                    const SCX_enum * search_in
+                                        = SCX_AUX_TABLE_ptrs[-script_of_char];
+                    const PERL_UINT_FAST8_T search_in_len
+                                     = SCX_AUX_TABLE_lengths[-script_of_char];
+                    PERL_UINT_FAST8_T i;
+
+                    for (i = 0; i < search_in_len; i++) {
+                        if (search_in[i] == script_of_run) {
+                            script_of_char = script_of_run;
+                            goto scripts_match;
+                        }
+                    }
+
+                    Safefree(intersection);
+                    sayNO;
+                }
+                else {
+                    /* Both run and char could be in one of several scripts.
+                     * If the intersection is empty, then this character isn't
+                     * in this script run.  Otherwise, we need to calculate the
+                     * intersection to use for future iterations of the loop,
+                     * unless we are already at the final character */
+                    const SCX_enum * search_char
+                                        = SCX_AUX_TABLE_ptrs[-script_of_char];
+                    const PERL_UINT_FAST8_T char_len
+                                     = SCX_AUX_TABLE_lengths[-script_of_char];
+                    const SCX_enum * search_run;
+                    PERL_UINT_FAST8_T run_len;
+
+                    SCX_enum * new_overlap = NULL;
+                    PERL_UINT_FAST8_T i, j;
+
+                    if (intersection) {
+                        search_run = intersection;
+                        run_len = intersection_len;
+                    }
+                    else {
+                        search_run = SCX_AUX_TABLE_ptrs[-script_of_run];
+                        run_len = SCX_AUX_TABLE_lengths[-script_of_run];
+                    }
+
+                    intersection_len = 0;
+
+                    for (i = 0; i < run_len; i++) {
+                        for (j = 0; j < char_len; j++) {
+                            if (search_run[i] == search_char[j]) {
+
+                                /* Here, the script at i,j matches.  That means
+                                 * this character is in the run.  But continue
+                                 * on to find the complete intersection, for
+                                 * the next loop iteration, and for the digit
+                                 * check after it.
+                                 *
+                                 * On the first found common script, we malloc
+                                 * space for the intersection list for the worst
+                                 * case of the intersection, which is the
+                                 * minimum of the number of scripts remaining
+                                 * in each set. */
+                                if (intersection_len == 0) {
+                                    Newx(new_overlap,
+                                         MIN(run_len - i, char_len - j),
+                                         SCX_enum);
+                                }
+                                new_overlap[intersection_len++] = search_run[i];
+                            }
+                        }
+                    }
+
+                    /* Here we've looked through everything.  If they have no
+                     * scripts in common, not a run */
+                    if (intersection_len == 0) {
+                        Safefree(intersection);
+                        sayNO;
+                    }
+
+
+                    /* If there is only a single script in common, set to that.
+                     * Otherwise, use the intersection going forward */
+                    Safefree(intersection);
+                    if (intersection_len == 1) {
+                        script_of_run = script_of_char = new_overlap[0];
+                        Safefree(new_overlap);
+                    }
+                    else {
+                        intersection = new_overlap;
+                    }
+                }
+
+#endif
+
+          scripts_match: ;
+
+                /* Here, the script of the character is compatible with that of
+                 * the run.  Either they match exactly, or one or both can be
+                 * any of several scripts, and the intersection is not empty.
+                 * If the character is not a decimal digit, we are done with
+                 * it.  Otherwise, it could still fail if it is from a
+                 * different set of 10 than seen already (or we may not have
+                 * seen any, and we need to set the sequence).  If we have
+                 * determined a single script and that script only has one set
+                 * of digits (almost all scripts are like that), then this
+                 * isn't a problem, as any digit must come from the same
+                 * sequence.  The only scripts that have multiple sequences
+                 * have been constructed to be 0 in 'script_zeros[]'.
+                 *
+                 * Here we check if it is a digit. */
+                if (    cp >= FIRST_NON_ASCII_DECIMAL_DIGIT
+                    && (   (          zero_of_run == 0
+                            || (  (   script_of_char >= 0
+                                   && script_zeros[script_of_char] == 0)
+                                ||    intersection))))
+                {
+                    SSize_t range_zero_index;
+                    range_zero_index = _invlist_search(decimals_invlist, cp);
+                    if (   LIKELY(range_zero_index >= 0)
+                        && ELEMENT_RANGE_MATCHES_INVLIST(range_zero_index))
+                    {
+                        UV range_zero = decimals_array[range_zero_index];
+                        if (zero_of_run) {
+                            if (zero_of_run != range_zero) {
+                                Safefree(intersection);
+                                sayNO;
+                            }
+                        }
+                        else {
+                            zero_of_run = range_zero;
+                        }
+                    }
+                }
+            } /* end of looping through CLOSESR text */
+
+            Safefree(intersection);
+            break;
+          }
+
         case ACCEPT:  /*  (*ACCEPT)  */
             if (scan->flags)
                 sv_yes_mark = MUTABLE_SV(rexi->data->data[ ARG( scan ) ]);
@@ -7561,9 +8100,6 @@ NULL
            if (cur_curlyx->u.curlyx.minmod) {
                ST.save_curlyx = cur_curlyx;
                cur_curlyx = cur_curlyx->u.curlyx.prev_curlyx;
-                ST.cp = regcppush(rex, ST.save_curlyx->u.curlyx.parenfloor,
-                            maxopenparen);
-               REGCP_SET(ST.lastcp);
                PUSH_YES_STATE_GOTO(WHILEM_B_min, ST.save_curlyx->u.curlyx.B,
                                     locinput);
                NOT_REACHED; /* NOTREACHED */
@@ -7596,11 +8132,11 @@ NULL
            CACHEsayNO;
            NOT_REACHED; /* NOTREACHED */
 
-       case WHILEM_A_min_fail: /* just failed to match A in a minimal match */
-           /* FALLTHROUGH */
        case WHILEM_A_pre_fail: /* just failed to match even minimal A */
            REGCP_UNWIND(ST.lastcp);
             regcppop(rex, &maxopenparen);
+           /* FALLTHROUGH */
+       case WHILEM_A_min_fail: /* just failed to match A in a minimal match */
            cur_curlyx->u.curlyx.lastloc = ST.save_lastloc;
            cur_curlyx->u.curlyx.count--;
            CACHEsayNO;
@@ -7633,8 +8169,6 @@ NULL
 
        case WHILEM_B_min_fail: /* just failed to match B in a minimal match */
            cur_curlyx = ST.save_curlyx;
-           REGCP_UNWIND(ST.lastcp);
-            regcppop(rex, &maxopenparen);
 
            if (cur_curlyx->u.curlyx.count >= /*max*/ARG2(cur_curlyx->u.curlyx.me)) {
                /* Maximum greed exceeded */
@@ -7656,9 +8190,6 @@ NULL
            );
            /* Try grabbing another A and see if it helps. */
            cur_curlyx->u.curlyx.lastloc = locinput;
-            ST.cp = regcppush(rex, cur_curlyx->u.curlyx.parenfloor,
-                            maxopenparen);
-           REGCP_SET(ST.lastcp);
            PUSH_STATE_GOTO(WHILEM_A_min,
                /*A*/ NEXTOPER(ST.save_curlyx->u.curlyx.me) + EXTRA_STEP_2ARGS,
                 locinput);
@@ -8122,16 +8653,46 @@ NULL
                }
                else {  /* Not utf8_target */
                    if (ST.c1 == ST.c2) {
-                       while (locinput <= ST.maxpos &&
-                              UCHARAT(locinput) != ST.c1)
-                           locinput++;
-                   }
-                   else {
-                       while (locinput <= ST.maxpos
-                              && UCHARAT(locinput) != ST.c1
-                              && UCHARAT(locinput) != ST.c2)
-                           locinput++;
+                        locinput = (char *) memchr(locinput,
+                                                   ST.c1,
+                                                   ST.maxpos + 1 - locinput);
+                        if (! locinput) {
+                            locinput = ST.maxpos + 1;
+                        }
                    }
+                    else {
+                        U8 c1_c2_bits_differing = ST.c1 ^ ST.c2;
+
+                        if (! isPOWER_OF_2(c1_c2_bits_differing)) {
+                            while (   locinput <= ST.maxpos
+                                   && UCHARAT(locinput) != ST.c1
+                                   && UCHARAT(locinput) != ST.c2)
+                            {
+                                locinput++;
+                            }
+                        }
+                        else {
+                            /* If c1 and c2 only differ by a single bit, we can
+                             * avoid a conditional each time through the loop,
+                             * at the expense of a little preliminary setup and
+                             * an extra mask each iteration.  By masking out
+                             * that bit, we match exactly two characters, c1
+                             * and c2, and so we don't have to test for both.
+                             * On both ASCII and EBCDIC platforms, most of the
+                             * ASCII-range and Latin1-range folded equivalents
+                             * differ only in a single bit, so this is actually
+                             * the most common case. (e.g. 'A' 0x41 vs 'a'
+                             * 0x61). */
+                            U8 c1_masked = ST.c1 &~ c1_c2_bits_differing;
+                            U8 c1_c2_mask = ~ c1_c2_bits_differing;
+                            while (   locinput <= ST.maxpos
+                                   && (UCHARAT(locinput) & c1_c2_mask)
+                                                                != c1_masked)
+                            {
+                                locinput++;
+                            }
+                        }
+                    }
                    n = locinput - ST.oldloc;
                }
                if (locinput > ST.maxpos)
@@ -8263,7 +8824,7 @@ NULL
 
                 SET_RECURSE_LOCINPUT("FAKE-END[after]", cur_eval->locinput);
 
-                PUSH_YES_STATE_GOTO(EVAL_AB, st->u.eval.prev_eval->u.eval.B,
+                PUSH_YES_STATE_GOTO(EVAL_postponed_AB, st->u.eval.prev_eval->u.eval.B,
                                     locinput); /* match B */
            }
 
@@ -8923,10 +9484,27 @@ S_regrepeat(pTHX_ regexp *prog, char **startposp, const regnode *p,
                 }
             }
             else {
-                while (scan < loceol &&
-                    (UCHARAT(scan) == c1 || UCHARAT(scan) == c2))
-                {
-                    scan++;
+                /* See comments in regmatch() CURLY_B_min_known_fail.  We avoid
+                 * a conditional each time through the loop if the characters
+                 * differ only in a single bit, as is the usual situation */
+                U8 c1_c2_bits_differing = c1 ^ c2;
+
+                if (isPOWER_OF_2(c1_c2_bits_differing)) {
+                    U8 c1_masked = c1 & ~ c1_c2_bits_differing;
+                    U8 c1_c2_mask = ~ c1_c2_bits_differing;
+
+                    while (   scan < loceol
+                           && (UCHARAT(scan) & c1_c2_mask) == c1_masked)
+                    {
+                        scan++;
+                    }
+                }
+                else {
+                    while (    scan < loceol
+                           && (UCHARAT(scan) == c1 || UCHARAT(scan) == c2))
+                    {
+                        scan++;
+                    }
                 }
             }
        }
@@ -8961,6 +9539,33 @@ S_regrepeat(pTHX_ regexp *prog, char **startposp, const regnode *p,
        }
        break;
 
+    case ASCII:
+        if (utf8_target && loceol - scan > max) {
+
+            /* We didn't adjust <loceol> at the beginning of this routine
+             * because is UTF-8, but it is actually ok to do so, since here, to
+             * match, 1 char == 1 byte. */
+            loceol = scan + max;
+        }
+
+        scan = find_next_non_ascii(scan, loceol, utf8_target);
+       break;
+
+    case NASCII:
+       if (utf8_target) {
+           while (     hardcount < max
+                   &&   scan < loceol
+                  && ! isASCII_utf8_safe(scan, loceol))
+           {
+               scan += UTF8SKIP(scan);
+               hardcount++;
+           }
+       }
+        else {
+            scan = find_next_ascii(scan, loceol, utf8_target);
+       }
+       break;
+
     /* The argument (FLAGS) to all the POSIX node types is the class number */
 
     case NPOSIXL:
@@ -9455,7 +10060,10 @@ S_reghop3(U8 *s, SSize_t off, const U8* lim)
     if (off >= 0) {
        while (off-- && s < lim) {
            /* XXX could check well-formedness here */
-           s += UTF8SKIP(s);
+           U8 *new_s = s + UTF8SKIP(s);
+            if (new_s > lim) /* lim may be in the middle of a long character */
+                return s;
+            s = new_s;
        }
     }
     else {
@@ -9747,6 +10355,8 @@ S_to_byte_substr(pTHX_ regexp *prog)
     return TRUE;
 }
 
+#ifndef PERL_IN_XSUB_RE
+
 bool
 Perl__is_grapheme(pTHX_ const U8 * strbeg, const U8 * s, const U8 * strend, const UV cp)
 {
@@ -9802,6 +10412,7 @@ Perl__is_grapheme(pTHX_ const U8 * strbeg, const U8 * s, const U8 * strend, cons
     return isGCB(cp_gcb_val, next_cp_gcb_val, strbeg, s, TRUE);
 }
 
+#endif