@@ -538,18 +538,17 @@ S_trie_list_transition(pTHX_ reg_trie_data *trie, U32 *state, const U32 octet,
538538
539539PERL_STATIC_INLINE U32
540540S_trie_trans_state (const reg_trie_data * trie , const U32 state ,
541- const U32 base , const U32 ucharcount , const U32 octet ,
542- const U32 special , const U32 ubound )
541+ const U32 base , const U32 octet , const U32 special )
543542{
544- const U32 index = base - ucharcount + octet ;
545-
546543 /* The packed table stores a transition at base - alphabet_size + octet.
547544 * The check field confirms that the physical slot belongs to this state.
548545 * During Aho-Corasick construction, special is the fallback transition
549546 * used when the lookup is performed from the root state. */
550- return base + octet >= ucharcount
551- && base + octet < ubound
552- && state == trie -> trans [index ].check
547+ if (!base )
548+ return state == 1 ? special : 0 ;
549+
550+ const U32 index = base - TRIE_ALPHABET_SIZE + octet ;
551+ return state == trie -> trans [index ].check
553552 && trie -> trans [index ].next
554553 ? trie -> trans [index ].next
555554 : state == 1 ? special : 0 ;
@@ -638,17 +637,20 @@ Perl_make_trie(pTHX_ RExC_state_t *pRExC_state, regnode *startbranch,
638637 trie -> wordinfo = (reg_trie_wordinfo * ) PerlMemShared_calloc (
639638 trie -> wordcount + 1 , sizeof (reg_trie_wordinfo ));
640639
640+ #ifdef DEBUGGING
641+ /* Allocate the optional word list only while regex debugging is enabled. */
641642 DEBUG_r ({
642643 trie_words = newAV ();
643644 });
644645
645646 DEBUG_TRIE_COMPILE_r ({
646647 re_indentf (
647648 "make_trie start == %d, first == %d, last == %d, tail == %d depth = %d\n" ,
648- depth + 1 ,
649+ depth + 1 ,
649650 REG_NODE_NUM (startbranch ), REG_NODE_NUM (first ),
650651 REG_NODE_NUM (last ), REG_NODE_NUM (tail ), (int )depth );
651652 });
653+ #endif
652654
653655 /* Find the node we are going to overwrite */
654656 if ( first == startbranch && OP ( last ) != BRANCH ) {
@@ -997,21 +999,43 @@ Perl_make_trie(pTHX_ RExC_state_t *pRExC_state, regnode *startbranch,
997999#ifdef DEBUGGING
9981000 const STRLEN ideal_transition_count = transition_count - 1 ;
9991001#endif
1000- STRLEN transition_capacity = transition_count ;
1002+ /* Allow roughly 20% slack. The root state needs one complete octet
1003+ * frame, and sparse frames can still expand the table. */
1004+ STRLEN transition_capacity = transition_count + transition_count / 5 ;
1005+ if (transition_capacity < TRIE_ALPHABET_SIZE + 1 )
1006+ transition_capacity = TRIE_ALPHABET_SIZE + 1 ;
10011007 trie -> trans = (reg_trie_trans * )
10021008 PerlMemShared_calloc ( transition_capacity ,
10031009 sizeof (reg_trie_trans ) );
10041010 {
10051011 U32 state ;
1006- /* table_highwater is one past the packed portion of trie->trans;
1007- * next_hole is the first unused slot within that portion. A
1008- * non-zero next field marks an occupied slot, so holes can be
1009- * reused without a separate occupancy map. */
1010- U32 table_highwater = 0 ;
1011- U32 next_hole = 0 ;
1012-
1012+ /* Slot zero is reserved. The root state starts at slot one, so
1013+ * its complete octet frame occupies slots 1 through 256. Other
1014+ * states can reuse its empty slots. */
1015+ U32 table_highwater = TRIE_ALPHABET_SIZE + 1 ;
1016+ U32 safe_highwater = TRIE_ALPHABET_SIZE + 1 ;
1017+ U32 next_hole = 1 ;
1018+
1019+ if (trie -> states [1 ].trans .list ) {
1020+ const U32 used = TRIE_LIST_USED (1 );
1021+ const U32 base = TRIE_ALPHABET_SIZE + 1 ;
1022+ U32 transition_index ;
1023+
1024+ for (transition_index = 1 ;
1025+ transition_index <= used ;
1026+ transition_index ++ ) {
1027+ const U32 tid = base - TRIE_ALPHABET_SIZE
1028+ + TRIE_LIST_ITEM (1 ,
1029+ transition_index ).octet ;
1030+ trie -> trans [tid ].next = TRIE_LIST_ITEM (1 ,
1031+ transition_index ).newstate ;
1032+ trie -> trans [tid ].check = 1 ;
1033+ }
1034+ Safefree (trie -> states [1 ].trans .list );
1035+ trie -> states [1 ].trans .base = base ;
1036+ }
10131037
1014- for ( state = 1 ; state < next_alloc ; state ++ ) {
1038+ for ( state = 2 ; state < next_alloc ; state ++ ) {
10151039 U32 base = 0 ;
10161040
10171041 /*
@@ -1030,15 +1054,14 @@ Perl_make_trie(pTHX_ RExC_state_t *pRExC_state, regnode *startbranch,
10301054 bool placed = false;
10311055 U32 transition_index ;
10321056
1033- /* A state's transitions form a frame from min_octet
1034- * through max_octet. Sparse frames may fit into holes
1035- * left by earlier frames. Try the first available
1036- * position. The frame may extend beyond table_highwater,
1037- * provided it
1038- * fits in the allocation and its occupied slots do not
1039- * clash. */
1057+ /* next_hole is the physical slot for min_octet. Its
1058+ * octet-zero slot is next_hole - min_octet, which must
1059+ * not be slot zero. Sparse frames may fit into holes
1060+ * left by earlier frames when their occupied slots do
1061+ * not clash. */
10401062 while (next_hole < table_highwater
1041- && trie -> trans [next_hole ].next )
1063+ && (next_hole <= min_octet
1064+ || trie -> trans [next_hole ].next ))
10421065 next_hole ++ ;
10431066 /* Only sparse frames benefit from hole searching. Small
10441067 * frames are cheap to try, while larger frames are tried
@@ -1047,9 +1070,11 @@ Perl_make_trie(pTHX_ RExC_state_t *pRExC_state, regnode *startbranch,
10471070 && next_hole < table_highwater
10481071 && next_hole + frame_width >= next_hole ) {
10491072 const U32 candidate_end = next_hole + frame_width ;
1050- if (transition_capacity < candidate_end ) {
1073+ const U32 candidate_base = TRIE_ALPHABET_SIZE
1074+ + next_hole - min_octet ;
1075+ const U32 needed = MAX (candidate_end , candidate_base );
1076+ if (transition_capacity < needed ) {
10511077 const U32 old_capacity = transition_capacity ;
1052- const U32 needed = candidate_end ;
10531078
10541079 while (transition_capacity < needed )
10551080 transition_capacity *= 2 ;
@@ -1073,7 +1098,7 @@ Perl_make_trie(pTHX_ RExC_state_t *pRExC_state, regnode *startbranch,
10731098 break ;
10741099 }
10751100 if (transition_index > used ) {
1076- base = TRIE_ALPHABET_SIZE + next_hole - min_octet ;
1101+ base = candidate_base ;
10771102 for (transition_index = 1 ;
10781103 transition_index <= used ;
10791104 transition_index ++ ) {
@@ -1087,29 +1112,36 @@ Perl_make_trie(pTHX_ RExC_state_t *pRExC_state, regnode *startbranch,
10871112 }
10881113 if (candidate_end > table_highwater )
10891114 table_highwater = candidate_end ;
1115+ if (candidate_base > safe_highwater )
1116+ safe_highwater = candidate_base ;
10901117 while (next_hole < table_highwater
1091- && trie -> trans [next_hole ].next )
1118+ && (next_hole <= min_octet
1119+ || trie -> trans [next_hole ].next ))
10921120 next_hole ++ ;
10931121 placed = true;
10941122 }
10951123 }
10961124
10971125 /* Grow the physical table when the frame cannot fit at
10981126 * the current high-water mark. */
1099- if (!placed
1100- && transition_capacity < table_highwater + frame_width ) {
1101- const U32 old_capacity = transition_capacity ;
1102- const U32 needed = table_highwater + frame_width ;
1103-
1104- while (transition_capacity < needed )
1105- transition_capacity *= 2 ;
1106- trie -> trans = (reg_trie_trans * )
1107- PerlMemShared_realloc ( trie -> trans ,
1127+ if (!placed ) {
1128+ const U32 base_at_highwater = TRIE_ALPHABET_SIZE
1129+ + table_highwater - min_octet ;
1130+ const U32 needed = MAX (table_highwater + frame_width ,
1131+ base_at_highwater );
1132+ if (transition_capacity < needed ) {
1133+ const U32 old_capacity = transition_capacity ;
1134+
1135+ while (transition_capacity < needed )
1136+ transition_capacity *= 2 ;
1137+ trie -> trans = (reg_trie_trans * )
1138+ PerlMemShared_realloc ( trie -> trans ,
11081139 transition_capacity
11091140 * sizeof (reg_trie_trans ) );
1110- Zero ( trie -> trans + old_capacity ,
1111- transition_capacity - old_capacity ,
1112- reg_trie_trans );
1141+ Zero ( trie -> trans + old_capacity ,
1142+ transition_capacity - old_capacity ,
1143+ reg_trie_trans );
1144+ }
11131145 }
11141146 if (!placed ) {
11151147 base = TRIE_ALPHABET_SIZE + table_highwater - min_octet ;
@@ -1119,7 +1151,8 @@ Perl_make_trie(pTHX_ RExC_state_t *pRExC_state, regnode *startbranch,
11191151 if ( !placed && max_octet == min_octet ) {
11201152 U32 set = 0 ;
11211153 for ( ; next_hole < table_highwater ; next_hole ++ ) {
1122- if ( ! trie -> trans [ next_hole ].next ) {
1154+ if (next_hole > min_octet
1155+ && !trie -> trans [next_hole ].next ) {
11231156 base = TRIE_ALPHABET_SIZE + next_hole - min_octet ;
11241157 trie -> trans [ next_hole ].next = TRIE_LIST_ITEM ( state ,
11251158 1 ).newstate ;
@@ -1136,7 +1169,8 @@ Perl_make_trie(pTHX_ RExC_state_t *pRExC_state, regnode *startbranch,
11361169 next_hole = table_highwater ;
11371170 } else {
11381171 while (next_hole < table_highwater
1139- && trie -> trans [next_hole ].next )
1172+ && (next_hole <= min_octet
1173+ || trie -> trans [next_hole ].next ))
11401174 next_hole ++ ;
11411175 }
11421176 } else if (!placed ) {
@@ -1153,9 +1187,12 @@ Perl_make_trie(pTHX_ RExC_state_t *pRExC_state, regnode *startbranch,
11531187 }
11541188 table_highwater += frame_width ;
11551189 while (next_hole < table_highwater
1156- && trie -> trans [next_hole ].next )
1190+ && (next_hole <= min_octet
1191+ || trie -> trans [next_hole ].next ))
11571192 next_hole ++ ;
11581193 }
1194+ if (base > safe_highwater )
1195+ safe_highwater = base ;
11591196 Safefree (trie -> states [ state ].trans .list );
11601197 }
11611198 /*
@@ -1165,9 +1202,20 @@ Perl_make_trie(pTHX_ RExC_state_t *pRExC_state, regnode *startbranch,
11651202 */
11661203 trie -> states [ state ].trans .base = base ;
11671204 }
1168- /* Slot zero is reserved by the packed-table representation; keep
1169- * it in the recorded range even when table_highwater is zero. */
1170- trie -> lasttrans = table_highwater + 1 ;
1205+ /* Every state's base is one past its highest possible lookup.
1206+ * Keep sufficient zeroed tail space for the complete frame. */
1207+ trie -> lasttrans = safe_highwater ;
1208+ if (transition_capacity < trie -> lasttrans ) {
1209+ const U32 old_capacity = transition_capacity ;
1210+
1211+ trie -> trans = (reg_trie_trans * )
1212+ PerlMemShared_realloc (trie -> trans ,
1213+ trie -> lasttrans
1214+ * sizeof (reg_trie_trans ));
1215+ Zero (trie -> trans + old_capacity ,
1216+ trie -> lasttrans - old_capacity , reg_trie_trans );
1217+ transition_capacity = trie -> lasttrans ;
1218+ }
11711219 assert (next_hole <= table_highwater );
11721220 assert (table_highwater <= transition_capacity );
11731221 DEBUG_TRIE_COMPILE_MORE_r (
@@ -1194,6 +1242,24 @@ Perl_make_trie(pTHX_ RExC_state_t *pRExC_state, regnode *startbranch,
11941242 PerlMemShared_realloc ( trie -> trans , trie -> lasttrans
11951243 * sizeof (reg_trie_trans ) );
11961244
1245+ #ifdef DEBUGGING
1246+ /* Validate the packed trie indexes while regex debugging is enabled.
1247+ * Move this outside DEBUG_r() to validate every full DEBUGGING build. */
1248+ DEBUG_r ({
1249+ for (U32 state = 1 ; state < trie -> statecount ; state ++ ) {
1250+ assert (!trie -> states [state ].trans .base
1251+ || (trie -> states [state ].trans .base > TRIE_ALPHABET_SIZE
1252+ && trie -> states [state ].trans .base <= trie -> lasttrans ));
1253+ }
1254+ for (U32 transition = 1 ; transition < trie -> lasttrans ; transition ++ ) {
1255+ assert (!trie -> trans [transition ].next
1256+ || (trie -> trans [transition ].check
1257+ && trie -> trans [transition ].check < trie -> statecount
1258+ && trie -> trans [transition ].next < trie -> statecount ));
1259+ }
1260+ });
1261+ #endif
1262+
11971263 { /* Modify the program and insert the new TRIE node */
11981264 U8 nodetype = (U8 ) flags ;
11991265 U8 trie_op = TRIE ;
@@ -1661,9 +1727,7 @@ Perl_construct_ahocorasick_from_trie(pTHX_ RExC_state_t *pRExC_state, regnode *s
16611727 const U32 trie_offset = TRIE_DATA_SLOT (source );
16621728 reg_trie_data * trie = (reg_trie_data * )RExC_rxi -> data -> data [trie_offset ];
16631729 U32 * q ;
1664- const U32 ucharcount = TRIE_ALPHABET_SIZE ;
16651730 const U32 numstates = trie -> statecount ;
1666- const U32 ubound = trie -> lasttrans + ucharcount ;
16671731 U32 q_read = 0 ;
16681732 U32 q_write = 0 ;
16691733 U32 octet ;
@@ -1716,8 +1780,7 @@ Perl_construct_ahocorasick_from_trie(pTHX_ RExC_state_t *pRExC_state, regnode *s
17161780 if (base ) {
17171781 for ( octet = trie -> states [1 ].min_octet ;
17181782 octet <= trie -> states [1 ].max_octet ; octet ++ ) {
1719- const U32 newstate = S_trie_trans_state (trie , 1 , base , ucharcount ,
1720- octet , 0 , ubound );
1783+ const U32 newstate = S_trie_trans_state (trie , 1 , base , octet , 0 );
17211784 if ( newstate ) {
17221785 q [ q_write ] = newstate ;
17231786 /* set to point at the root */
@@ -1733,19 +1796,18 @@ Perl_construct_ahocorasick_from_trie(pTHX_ RExC_state_t *pRExC_state, regnode *s
17331796 for ( octet = aho -> states [cur ].min_octet ;
17341797 octet <= aho -> states [cur ].max_octet ; octet ++ ) {
17351798 const U32 ch_state = S_trie_trans_state (trie , cur , base ,
1736- ucharcount , octet , 1 ,
1737- ubound );
1799+ octet , 1 );
17381800 if (ch_state ) {
17391801 U32 fail_state = cur ;
17401802 U32 fail_base ;
17411803 do {
17421804 fail_state = fail [ fail_state ];
17431805 fail_base = aho -> states [ fail_state ].trans .base ;
17441806 } while ( !S_trie_trans_state (trie , fail_state , fail_base ,
1745- ucharcount , octet , 1 , ubound ) );
1807+ octet , 1 ) );
17461808
17471809 fail_state = S_trie_trans_state (trie , fail_state , fail_base ,
1748- ucharcount , octet , 1 , ubound );
1810+ octet , 1 );
17491811 fail [ ch_state ] = fail_state ;
17501812 if ( !aho -> states [ ch_state ].wordnum && aho -> states [ fail_state ].wordnum )
17511813 {
0 commit comments