1/* stringlib: fastsearch implementation */ 2 3#define STRINGLIB_FASTSEARCH_H 4 5/* fast search/count implementation, based on a mix between boyer- 6 moore and horspool, with a few more bells and whistles on the top. 7 for some more background, see: 8 https://web.archive.org/web/20201107074620/http://effbot.org/zone/stringlib.htm */ 9 10/* note: fastsearch may access s[n], which isn't a problem when using 11 Python's ordinary string types, but may cause problems if you're 12 using this code in other contexts. also, the count mode returns -1 13 if there cannot possibly be a match in the target string, and 0 if 14 it has actually checked for matches, but didn't find any. callers 15 beware! */ 16 17/* If the strings are long enough, use Crochemore and Perrin's Two-Way 18 algorithm, which has worst-case O(n) runtime and best-case O(n/k). 19 Also compute a table of shifts to achieve O(n/k) in more cases, 20 and often (data dependent) deduce larger shifts than pure C&P can 21 deduce. */ 22 23#define FAST_COUNT 0 24#define FAST_SEARCH 1 25#define FAST_RSEARCH 2 26 27#if LONG_BIT >= 128 28#define STRINGLIB_BLOOM_WIDTH 128 29#elif LONG_BIT >= 64 30#define STRINGLIB_BLOOM_WIDTH 64 31#elif LONG_BIT >= 32 32#define STRINGLIB_BLOOM_WIDTH 32 33#else 34#error "LONG_BIT is smaller than 32" 35#endif 36 37#define STRINGLIB_BLOOM_ADD(mask, ch) \ 38 ((mask |= (1UL << ((ch) & (STRINGLIB_BLOOM_WIDTH -1))))) 39#define STRINGLIB_BLOOM(mask, ch) \ 40 ((mask & (1UL << ((ch) & (STRINGLIB_BLOOM_WIDTH -1))))) 41 42#if STRINGLIB_SIZEOF_CHAR == 1 43# define MEMCHR_CUT_OFF 15 44#else 45# define MEMCHR_CUT_OFF 40 46#endif 47 48Py_LOCAL_INLINE(Py_ssize_t) 49STRINGLIB(find_char)(const STRINGLIB_CHAR* s, Py_ssize_t n, STRINGLIB_CHAR ch) 50{ 51 const STRINGLIB_CHAR *p, *e; 52 53 p = s; 54 e = s + n; 55 if (n > MEMCHR_CUT_OFF) { 56#if STRINGLIB_SIZEOF_CHAR == 1 57 p = memchr(s, ch, n); 58 if (p != NULL) 59 return (p - s); 60 return -1; 61#else 62 /* use memchr if we can choose a needle without too many likely 63 false positives */ 64 const STRINGLIB_CHAR *s1, *e1; 65 unsigned char needle = ch & 0xff; 66 /* If looking for a multiple of 256, we'd have too 67 many false positives looking for the '\0' byte in UCS2 68 and UCS4 representations. */ 69 if (needle != 0) { 70 do { 71 void *candidate = memchr(p, needle, 72 (e - p) * sizeof(STRINGLIB_CHAR)); 73 if (candidate == NULL) 74 return -1; 75 s1 = p; 76 p = (const STRINGLIB_CHAR *) 77 _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR)); 78 if (*p == ch) 79 return (p - s); 80 /* False positive */ 81 p++; 82 if (p - s1 > MEMCHR_CUT_OFF) 83 continue; 84 if (e - p <= MEMCHR_CUT_OFF) 85 break; 86 e1 = p + MEMCHR_CUT_OFF; 87 while (p != e1) { 88 if (*p == ch) 89 return (p - s); 90 p++; 91 } 92 } 93 while (e - p > MEMCHR_CUT_OFF); 94 } 95#endif 96 } 97 while (p < e) { 98 if (*p == ch) 99 return (p - s); 100 p++; 101 } 102 return -1; 103} 104 105Py_LOCAL_INLINE(Py_ssize_t) 106STRINGLIB(rfind_char)(const STRINGLIB_CHAR* s, Py_ssize_t n, STRINGLIB_CHAR ch) 107{ 108 const STRINGLIB_CHAR *p; 109#ifdef HAVE_MEMRCHR 110 /* memrchr() is a GNU extension, available since glibc 2.1.91. 111 it doesn't seem as optimized as memchr(), but is still quite 112 faster than our hand-written loop below */ 113 114 if (n > MEMCHR_CUT_OFF) { 115#if STRINGLIB_SIZEOF_CHAR == 1 116 p = memrchr(s, ch, n); 117 if (p != NULL) 118 return (p - s); 119 return -1; 120#else 121 /* use memrchr if we can choose a needle without too many likely 122 false positives */ 123 const STRINGLIB_CHAR *s1; 124 Py_ssize_t n1; 125 unsigned char needle = ch & 0xff; 126 /* If looking for a multiple of 256, we'd have too 127 many false positives looking for the '\0' byte in UCS2 128 and UCS4 representations. */ 129 if (needle != 0) { 130 do { 131 void *candidate = memrchr(s, needle, 132 n * sizeof(STRINGLIB_CHAR)); 133 if (candidate == NULL) 134 return -1; 135 n1 = n; 136 p = (const STRINGLIB_CHAR *) 137 _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR)); 138 n = p - s; 139 if (*p == ch) 140 return n; 141 /* False positive */ 142 if (n1 - n > MEMCHR_CUT_OFF) 143 continue; 144 if (n <= MEMCHR_CUT_OFF) 145 break; 146 s1 = p - MEMCHR_CUT_OFF; 147 while (p > s1) { 148 p--; 149 if (*p == ch) 150 return (p - s); 151 } 152 n = p - s; 153 } 154 while (n > MEMCHR_CUT_OFF); 155 } 156#endif 157 } 158#endif /* HAVE_MEMRCHR */ 159 p = s + n; 160 while (p > s) { 161 p--; 162 if (*p == ch) 163 return (p - s); 164 } 165 return -1; 166} 167 168#undef MEMCHR_CUT_OFF 169 170/* Change to a 1 to see logging comments walk through the algorithm. */ 171#if 0 && STRINGLIB_SIZEOF_CHAR == 1 172# define LOG(...) printf(__VA_ARGS__) 173# define LOG_STRING(s, n) printf("\"%.*s\"", (int)(n), s) 174# define LOG_LINEUP() do { \ 175 LOG("> "); LOG_STRING(haystack, len_haystack); LOG("\n> "); \ 176 LOG("%*s",(int)(window_last - haystack + 1 - len_needle), ""); \ 177 LOG_STRING(needle, len_needle); LOG("\n"); \ 178} while(0) 179#else 180# define LOG(...) 181# define LOG_STRING(s, n) 182# define LOG_LINEUP() 183#endif 184 185Py_LOCAL_INLINE(Py_ssize_t) 186STRINGLIB(_lex_search)(const STRINGLIB_CHAR *needle, Py_ssize_t len_needle, 187 Py_ssize_t *return_period, int invert_alphabet) 188{ 189 /* Do a lexicographic search. Essentially this: 190 >>> max(needle[i:] for i in range(len(needle)+1)) 191 Also find the period of the right half. */ 192 Py_ssize_t max_suffix = 0; 193 Py_ssize_t candidate = 1; 194 Py_ssize_t k = 0; 195 // The period of the right half. 196 Py_ssize_t period = 1; 197 198 while (candidate + k < len_needle) { 199 // each loop increases candidate + k + max_suffix 200 STRINGLIB_CHAR a = needle[candidate + k]; 201 STRINGLIB_CHAR b = needle[max_suffix + k]; 202 // check if the suffix at candidate is better than max_suffix 203 if (invert_alphabet ? (b < a) : (a < b)) { 204 // Fell short of max_suffix. 205 // The next k + 1 characters are non-increasing 206 // from candidate, so they won't start a maximal suffix. 207 candidate += k + 1; 208 k = 0; 209 // We've ruled out any period smaller than what's 210 // been scanned since max_suffix. 211 period = candidate - max_suffix; 212 } 213 else if (a == b) { 214 if (k + 1 != period) { 215 // Keep scanning the equal strings 216 k++; 217 } 218 else { 219 // Matched a whole period. 220 // Start matching the next period. 221 candidate += period; 222 k = 0; 223 } 224 } 225 else { 226 // Did better than max_suffix, so replace it. 227 max_suffix = candidate; 228 candidate++; 229 k = 0; 230 period = 1; 231 } 232 } 233 *return_period = period; 234 return max_suffix; 235} 236 237Py_LOCAL_INLINE(Py_ssize_t) 238STRINGLIB(_factorize)(const STRINGLIB_CHAR *needle, 239 Py_ssize_t len_needle, 240 Py_ssize_t *return_period) 241{ 242 /* Do a "critical factorization", making it so that: 243 >>> needle = (left := needle[:cut]) + (right := needle[cut:]) 244 where the "local period" of the cut is maximal. 245 246 The local period of the cut is the minimal length of a string w 247 such that (left endswith w or w endswith left) 248 and (right startswith w or w startswith left). 249 250 The Critical Factorization Theorem says that this maximal local 251 period is the global period of the string. 252 253 Crochemore and Perrin (1991) show that this cut can be computed 254 as the later of two cuts: one that gives a lexicographically 255 maximal right half, and one that gives the same with the 256 with respect to a reversed alphabet-ordering. 257 258 This is what we want to happen: 259 >>> x = "GCAGAGAG" 260 >>> cut, period = factorize(x) 261 >>> x[:cut], (right := x[cut:]) 262 ('GC', 'AGAGAG') 263 >>> period # right half period 264 2 265 >>> right[period:] == right[:-period] 266 True 267 268 This is how the local period lines up in the above example: 269 GC | AGAGAG 270 AGAGAGC = AGAGAGC 271 The length of this minimal repetition is 7, which is indeed the 272 period of the original string. */ 273 274 Py_ssize_t cut1, period1, cut2, period2, cut, period; 275 cut1 = STRINGLIB(_lex_search)(needle, len_needle, &period1, 0); 276 cut2 = STRINGLIB(_lex_search)(needle, len_needle, &period2, 1); 277 278 // Take the later cut. 279 if (cut1 > cut2) { 280 period = period1; 281 cut = cut1; 282 } 283 else { 284 period = period2; 285 cut = cut2; 286 } 287 288 LOG("split: "); LOG_STRING(needle, cut); 289 LOG(" + "); LOG_STRING(needle + cut, len_needle - cut); 290 LOG("\n"); 291 292 *return_period = period; 293 return cut; 294} 295 296 297#define SHIFT_TYPE uint8_t 298#define MAX_SHIFT UINT8_MAX 299 300#define TABLE_SIZE_BITS 6u 301#define TABLE_SIZE (1U << TABLE_SIZE_BITS) 302#define TABLE_MASK (TABLE_SIZE - 1U) 303 304typedef struct STRINGLIB(_pre) { 305 const STRINGLIB_CHAR *needle; 306 Py_ssize_t len_needle; 307 Py_ssize_t cut; 308 Py_ssize_t period; 309 Py_ssize_t gap; 310 int is_periodic; 311 SHIFT_TYPE table[TABLE_SIZE]; 312} STRINGLIB(prework); 313 314 315static void 316STRINGLIB(_preprocess)(const STRINGLIB_CHAR *needle, Py_ssize_t len_needle, 317 STRINGLIB(prework) *p) 318{ 319 p->needle = needle; 320 p->len_needle = len_needle; 321 p->cut = STRINGLIB(_factorize)(needle, len_needle, &(p->period)); 322 assert(p->period + p->cut <= len_needle); 323 p->is_periodic = (0 == memcmp(needle, 324 needle + p->period, 325 p->cut * STRINGLIB_SIZEOF_CHAR)); 326 if (p->is_periodic) { 327 assert(p->cut <= len_needle/2); 328 assert(p->cut < p->period); 329 p->gap = 0; // unused 330 } 331 else { 332 // A lower bound on the period 333 p->period = Py_MAX(p->cut, len_needle - p->cut) + 1; 334 // The gap between the last character and the previous 335 // occurrence of an equivalent character (modulo TABLE_SIZE) 336 p->gap = len_needle; 337 STRINGLIB_CHAR last = needle[len_needle - 1] & TABLE_MASK; 338 for (Py_ssize_t i = len_needle - 2; i >= 0; i--) { 339 STRINGLIB_CHAR x = needle[i] & TABLE_MASK; 340 if (x == last) { 341 p->gap = len_needle - 1 - i; 342 break; 343 } 344 } 345 } 346 // Fill up a compressed Boyer-Moore "Bad Character" table 347 Py_ssize_t not_found_shift = Py_MIN(len_needle, MAX_SHIFT); 348 for (Py_ssize_t i = 0; i < (Py_ssize_t)TABLE_SIZE; i++) { 349 p->table[i] = Py_SAFE_DOWNCAST(not_found_shift, 350 Py_ssize_t, SHIFT_TYPE); 351 } 352 for (Py_ssize_t i = len_needle - not_found_shift; i < len_needle; i++) { 353 SHIFT_TYPE shift = Py_SAFE_DOWNCAST(len_needle - 1 - i, 354 Py_ssize_t, SHIFT_TYPE); 355 p->table[needle[i] & TABLE_MASK] = shift; 356 } 357} 358 359static Py_ssize_t 360STRINGLIB(_two_way)(const STRINGLIB_CHAR *haystack, Py_ssize_t len_haystack, 361 STRINGLIB(prework) *p) 362{ 363 // Crochemore and Perrin's (1991) Two-Way algorithm. 364 // See http://www-igm.univ-mlv.fr/~lecroq/string/node26.html#SECTION00260 365 const Py_ssize_t len_needle = p->len_needle; 366 const Py_ssize_t cut = p->cut; 367 Py_ssize_t period = p->period; 368 const STRINGLIB_CHAR *const needle = p->needle; 369 const STRINGLIB_CHAR *window_last = haystack + len_needle - 1; 370 const STRINGLIB_CHAR *const haystack_end = haystack + len_haystack; 371 SHIFT_TYPE *table = p->table; 372 const STRINGLIB_CHAR *window; 373 LOG("===== Two-way: \"%s\" in \"%s\". =====\n", needle, haystack); 374 375 if (p->is_periodic) { 376 LOG("Needle is periodic.\n"); 377 Py_ssize_t memory = 0; 378 periodicwindowloop: 379 while (window_last < haystack_end) { 380 assert(memory == 0); 381 for (;;) { 382 LOG_LINEUP(); 383 Py_ssize_t shift = table[(*window_last) & TABLE_MASK]; 384 window_last += shift; 385 if (shift == 0) { 386 break; 387 } 388 if (window_last >= haystack_end) { 389 return -1; 390 } 391 LOG("Horspool skip"); 392 } 393 no_shift: 394 window = window_last - len_needle + 1; 395 assert((window[len_needle - 1] & TABLE_MASK) == 396 (needle[len_needle - 1] & TABLE_MASK)); 397 Py_ssize_t i = Py_MAX(cut, memory); 398 for (; i < len_needle; i++) { 399 if (needle[i] != window[i]) { 400 LOG("Right half does not match.\n"); 401 window_last += i - cut + 1; 402 memory = 0; 403 goto periodicwindowloop; 404 } 405 } 406 for (i = memory; i < cut; i++) { 407 if (needle[i] != window[i]) { 408 LOG("Left half does not match.\n"); 409 window_last += period; 410 memory = len_needle - period; 411 if (window_last >= haystack_end) { 412 return -1; 413 } 414 Py_ssize_t shift = table[(*window_last) & TABLE_MASK]; 415 if (shift) { 416 // A mismatch has been identified to the right 417 // of where i will next start, so we can jump 418 // at least as far as if the mismatch occurred 419 // on the first comparison. 420 Py_ssize_t mem_jump = Py_MAX(cut, memory) - cut + 1; 421 LOG("Skip with Memory.\n"); 422 memory = 0; 423 window_last += Py_MAX(shift, mem_jump); 424 goto periodicwindowloop; 425 } 426 goto no_shift; 427 } 428 } 429 LOG("Found a match!\n"); 430 return window - haystack; 431 } 432 } 433 else { 434 Py_ssize_t gap = p->gap; 435 period = Py_MAX(gap, period); 436 LOG("Needle is not periodic.\n"); 437 Py_ssize_t gap_jump_end = Py_MIN(len_needle, cut + gap); 438 windowloop: 439 while (window_last < haystack_end) { 440 for (;;) { 441 LOG_LINEUP(); 442 Py_ssize_t shift = table[(*window_last) & TABLE_MASK]; 443 window_last += shift; 444 if (shift == 0) { 445 break; 446 } 447 if (window_last >= haystack_end) { 448 return -1; 449 } 450 LOG("Horspool skip"); 451 } 452 window = window_last - len_needle + 1; 453 assert((window[len_needle - 1] & TABLE_MASK) == 454 (needle[len_needle - 1] & TABLE_MASK)); 455 for (Py_ssize_t i = cut; i < gap_jump_end; i++) { 456 if (needle[i] != window[i]) { 457 LOG("Early right half mismatch: jump by gap.\n"); 458 assert(gap >= i - cut + 1); 459 window_last += gap; 460 goto windowloop; 461 } 462 } 463 for (Py_ssize_t i = gap_jump_end; i < len_needle; i++) { 464 if (needle[i] != window[i]) { 465 LOG("Late right half mismatch.\n"); 466 assert(i - cut + 1 > gap); 467 window_last += i - cut + 1; 468 goto windowloop; 469 } 470 } 471 for (Py_ssize_t i = 0; i < cut; i++) { 472 if (needle[i] != window[i]) { 473 LOG("Left half does not match.\n"); 474 window_last += period; 475 goto windowloop; 476 } 477 } 478 LOG("Found a match!\n"); 479 return window - haystack; 480 } 481 } 482 LOG("Not found. Returning -1.\n"); 483 return -1; 484} 485 486 487static Py_ssize_t 488STRINGLIB(_two_way_find)(const STRINGLIB_CHAR *haystack, 489 Py_ssize_t len_haystack, 490 const STRINGLIB_CHAR *needle, 491 Py_ssize_t len_needle) 492{ 493 LOG("###### Finding \"%s\" in \"%s\".\n", needle, haystack); 494 STRINGLIB(prework) p; 495 STRINGLIB(_preprocess)(needle, len_needle, &p); 496 return STRINGLIB(_two_way)(haystack, len_haystack, &p); 497} 498 499 500static Py_ssize_t 501STRINGLIB(_two_way_count)(const STRINGLIB_CHAR *haystack, 502 Py_ssize_t len_haystack, 503 const STRINGLIB_CHAR *needle, 504 Py_ssize_t len_needle, 505 Py_ssize_t maxcount) 506{ 507 LOG("###### Counting \"%s\" in \"%s\".\n", needle, haystack); 508 STRINGLIB(prework) p; 509 STRINGLIB(_preprocess)(needle, len_needle, &p); 510 Py_ssize_t index = 0, count = 0; 511 while (1) { 512 Py_ssize_t result; 513 result = STRINGLIB(_two_way)(haystack + index, 514 len_haystack - index, &p); 515 if (result == -1) { 516 return count; 517 } 518 count++; 519 if (count == maxcount) { 520 return maxcount; 521 } 522 index += result + len_needle; 523 } 524 return count; 525} 526 527#undef SHIFT_TYPE 528#undef NOT_FOUND 529#undef SHIFT_OVERFLOW 530#undef TABLE_SIZE_BITS 531#undef TABLE_SIZE 532#undef TABLE_MASK 533 534#undef LOG 535#undef LOG_STRING 536#undef LOG_LINEUP 537 538static inline Py_ssize_t 539STRINGLIB(default_find)(const STRINGLIB_CHAR* s, Py_ssize_t n, 540 const STRINGLIB_CHAR* p, Py_ssize_t m, 541 Py_ssize_t maxcount, int mode) 542{ 543 const Py_ssize_t w = n - m; 544 Py_ssize_t mlast = m - 1, count = 0; 545 Py_ssize_t gap = mlast; 546 const STRINGLIB_CHAR last = p[mlast]; 547 const STRINGLIB_CHAR *const ss = &s[mlast]; 548 549 unsigned long mask = 0; 550 for (Py_ssize_t i = 0; i < mlast; i++) { 551 STRINGLIB_BLOOM_ADD(mask, p[i]); 552 if (p[i] == last) { 553 gap = mlast - i - 1; 554 } 555 } 556 STRINGLIB_BLOOM_ADD(mask, last); 557 558 for (Py_ssize_t i = 0; i <= w; i++) { 559 if (ss[i] == last) { 560 /* candidate match */ 561 Py_ssize_t j; 562 for (j = 0; j < mlast; j++) { 563 if (s[i+j] != p[j]) { 564 break; 565 } 566 } 567 if (j == mlast) { 568 /* got a match! */ 569 if (mode != FAST_COUNT) { 570 return i; 571 } 572 count++; 573 if (count == maxcount) { 574 return maxcount; 575 } 576 i = i + mlast; 577 continue; 578 } 579 /* miss: check if next character is part of pattern */ 580 if (!STRINGLIB_BLOOM(mask, ss[i+1])) { 581 i = i + m; 582 } 583 else { 584 i = i + gap; 585 } 586 } 587 else { 588 /* skip: check if next character is part of pattern */ 589 if (!STRINGLIB_BLOOM(mask, ss[i+1])) { 590 i = i + m; 591 } 592 } 593 } 594 return mode == FAST_COUNT ? count : -1; 595} 596 597 598static Py_ssize_t 599STRINGLIB(adaptive_find)(const STRINGLIB_CHAR* s, Py_ssize_t n, 600 const STRINGLIB_CHAR* p, Py_ssize_t m, 601 Py_ssize_t maxcount, int mode) 602{ 603 const Py_ssize_t w = n - m; 604 Py_ssize_t mlast = m - 1, count = 0; 605 Py_ssize_t gap = mlast; 606 Py_ssize_t hits = 0, res; 607 const STRINGLIB_CHAR last = p[mlast]; 608 const STRINGLIB_CHAR *const ss = &s[mlast]; 609 610 unsigned long mask = 0; 611 for (Py_ssize_t i = 0; i < mlast; i++) { 612 STRINGLIB_BLOOM_ADD(mask, p[i]); 613 if (p[i] == last) { 614 gap = mlast - i - 1; 615 } 616 } 617 STRINGLIB_BLOOM_ADD(mask, last); 618 619 for (Py_ssize_t i = 0; i <= w; i++) { 620 if (ss[i] == last) { 621 /* candidate match */ 622 Py_ssize_t j; 623 for (j = 0; j < mlast; j++) { 624 if (s[i+j] != p[j]) { 625 break; 626 } 627 } 628 if (j == mlast) { 629 /* got a match! */ 630 if (mode != FAST_COUNT) { 631 return i; 632 } 633 count++; 634 if (count == maxcount) { 635 return maxcount; 636 } 637 i = i + mlast; 638 continue; 639 } 640 hits += j + 1; 641 if (hits > m / 4 && w - i > 2000) { 642 if (mode == FAST_SEARCH) { 643 res = STRINGLIB(_two_way_find)(s + i, n - i, p, m); 644 return res == -1 ? -1 : res + i; 645 } 646 else { 647 res = STRINGLIB(_two_way_count)(s + i, n - i, p, m, 648 maxcount - count); 649 return res + count; 650 } 651 } 652 /* miss: check if next character is part of pattern */ 653 if (!STRINGLIB_BLOOM(mask, ss[i+1])) { 654 i = i + m; 655 } 656 else { 657 i = i + gap; 658 } 659 } 660 else { 661 /* skip: check if next character is part of pattern */ 662 if (!STRINGLIB_BLOOM(mask, ss[i+1])) { 663 i = i + m; 664 } 665 } 666 } 667 return mode == FAST_COUNT ? count : -1; 668} 669 670 671static Py_ssize_t 672STRINGLIB(default_rfind)(const STRINGLIB_CHAR* s, Py_ssize_t n, 673 const STRINGLIB_CHAR* p, Py_ssize_t m, 674 Py_ssize_t maxcount, int mode) 675{ 676 /* create compressed boyer-moore delta 1 table */ 677 unsigned long mask = 0; 678 Py_ssize_t i, j, mlast = m - 1, skip = m - 1, w = n - m; 679 680 /* process pattern[0] outside the loop */ 681 STRINGLIB_BLOOM_ADD(mask, p[0]); 682 /* process pattern[:0:-1] */ 683 for (i = mlast; i > 0; i--) { 684 STRINGLIB_BLOOM_ADD(mask, p[i]); 685 if (p[i] == p[0]) { 686 skip = i - 1; 687 } 688 } 689 690 for (i = w; i >= 0; i--) { 691 if (s[i] == p[0]) { 692 /* candidate match */ 693 for (j = mlast; j > 0; j--) { 694 if (s[i+j] != p[j]) { 695 break; 696 } 697 } 698 if (j == 0) { 699 /* got a match! */ 700 return i; 701 } 702 /* miss: check if previous character is part of pattern */ 703 if (i > 0 && !STRINGLIB_BLOOM(mask, s[i-1])) { 704 i = i - m; 705 } 706 else { 707 i = i - skip; 708 } 709 } 710 else { 711 /* skip: check if previous character is part of pattern */ 712 if (i > 0 && !STRINGLIB_BLOOM(mask, s[i-1])) { 713 i = i - m; 714 } 715 } 716 } 717 return -1; 718} 719 720 721static inline Py_ssize_t 722STRINGLIB(count_char)(const STRINGLIB_CHAR *s, Py_ssize_t n, 723 const STRINGLIB_CHAR p0, Py_ssize_t maxcount) 724{ 725 Py_ssize_t i, count = 0; 726 for (i = 0; i < n; i++) { 727 if (s[i] == p0) { 728 count++; 729 if (count == maxcount) { 730 return maxcount; 731 } 732 } 733 } 734 return count; 735} 736 737 738Py_LOCAL_INLINE(Py_ssize_t) 739FASTSEARCH(const STRINGLIB_CHAR* s, Py_ssize_t n, 740 const STRINGLIB_CHAR* p, Py_ssize_t m, 741 Py_ssize_t maxcount, int mode) 742{ 743 if (n < m || (mode == FAST_COUNT && maxcount == 0)) { 744 return -1; 745 } 746 747 /* look for special cases */ 748 if (m <= 1) { 749 if (m <= 0) { 750 return -1; 751 } 752 /* use special case for 1-character strings */ 753 if (mode == FAST_SEARCH) 754 return STRINGLIB(find_char)(s, n, p[0]); 755 else if (mode == FAST_RSEARCH) 756 return STRINGLIB(rfind_char)(s, n, p[0]); 757 else { 758 return STRINGLIB(count_char)(s, n, p[0], maxcount); 759 } 760 } 761 762 if (mode != FAST_RSEARCH) { 763 if (n < 2500 || (m < 100 && n < 30000) || m < 6) { 764 return STRINGLIB(default_find)(s, n, p, m, maxcount, mode); 765 } 766 else if ((m >> 2) * 3 < (n >> 2)) { 767 /* 33% threshold, but don't overflow. */ 768 /* For larger problems where the needle isn't a huge 769 percentage of the size of the haystack, the relatively 770 expensive O(m) startup cost of the two-way algorithm 771 will surely pay off. */ 772 if (mode == FAST_SEARCH) { 773 return STRINGLIB(_two_way_find)(s, n, p, m); 774 } 775 else { 776 return STRINGLIB(_two_way_count)(s, n, p, m, maxcount); 777 } 778 } 779 else { 780 /* To ensure that we have good worst-case behavior, 781 here's an adaptive version of the algorithm, where if 782 we match O(m) characters without any matches of the 783 entire needle, then we predict that the startup cost of 784 the two-way algorithm will probably be worth it. */ 785 return STRINGLIB(adaptive_find)(s, n, p, m, maxcount, mode); 786 } 787 } 788 else { 789 /* FAST_RSEARCH */ 790 return STRINGLIB(default_rfind)(s, n, p, m, maxcount, mode); 791 } 792} 793 794