Coverage Report

Created: 2026-03-23 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cpython/Objects/stringlib/fastsearch.h
Line
Count
Source
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. See stringlib_find_two_way_notes.txt in this folder for a
22
   detailed explanation. */
23
24
243M
#define FAST_COUNT 0
25
99.1M
#define FAST_SEARCH 1
26
63.0M
#define FAST_RSEARCH 2
27
28
#if LONG_BIT >= 128
29
#define STRINGLIB_BLOOM_WIDTH 128
30
#elif LONG_BIT >= 64
31
934M
#define STRINGLIB_BLOOM_WIDTH 64
32
#elif LONG_BIT >= 32
33
#define STRINGLIB_BLOOM_WIDTH 32
34
#else
35
#error "LONG_BIT is smaller than 32"
36
#endif
37
38
#define STRINGLIB_BLOOM_ADD(mask, ch) \
39
166M
    ((mask |= (1UL << ((ch) & (STRINGLIB_BLOOM_WIDTH -1)))))
40
#define STRINGLIB_BLOOM(mask, ch)     \
41
767M
    ((mask &  (1UL << ((ch) & (STRINGLIB_BLOOM_WIDTH -1)))))
42
43
#ifdef STRINGLIB_FAST_MEMCHR
44
222M
#  define MEMCHR_CUT_OFF 15
45
#else
46
7.76M
#  define MEMCHR_CUT_OFF 40
47
#endif
48
49
Py_LOCAL_INLINE(Py_ssize_t)
50
STRINGLIB(find_char)(const STRINGLIB_CHAR* s, Py_ssize_t n, STRINGLIB_CHAR ch)
51
229M
{
52
229M
    const STRINGLIB_CHAR *p, *e;
53
54
229M
    p = s;
55
229M
    e = s + n;
56
229M
    if (n > MEMCHR_CUT_OFF) {
57
#ifdef STRINGLIB_FAST_MEMCHR
58
33.1M
        p = STRINGLIB_FAST_MEMCHR(s, ch, n);
59
33.1M
        if (p != NULL)
60
31.4M
            return (p - s);
61
1.69M
        return -1;
62
#else
63
        /* use memchr if we can choose a needle without too many likely
64
           false positives */
65
        const STRINGLIB_CHAR *s1, *e1;
66
        unsigned char needle = ch & 0xff;
67
        /* If looking for a multiple of 256, we'd have too
68
           many false positives looking for the '\0' byte in UCS2
69
           and UCS4 representations. */
70
7.26M
        if (needle != 0) {
71
7.28M
            do {
72
7.28M
                const void *candidate = memchr(p, needle,
73
7.28M
                                               (e - p) * sizeof(STRINGLIB_CHAR));
74
7.28M
                if (candidate == NULL)
75
61.2k
                    return -1;
76
7.21M
                s1 = p;
77
7.21M
                p = (const STRINGLIB_CHAR *)
78
7.21M
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
79
7.21M
                if (*p == ch)
80
7.18M
                    return (p - s);
81
                /* False positive */
82
29.8k
                p++;
83
29.8k
                if (p - s1 > MEMCHR_CUT_OFF)
84
12.1k
                    continue;
85
17.6k
                if (e - p <= MEMCHR_CUT_OFF)
86
1.88k
                    break;
87
15.8k
                e1 = p + MEMCHR_CUT_OFF;
88
545k
                while (p != e1) {
89
533k
                    if (*p == ch)
90
3.11k
                        return (p - s);
91
530k
                    p++;
92
530k
                }
93
15.8k
            }
94
7.25M
            while (e - p > MEMCHR_CUT_OFF);
95
7.25M
        }
96
#endif
97
40.4M
    }
98
989M
    while (p < e) {
99
820M
        if (*p == ch)
100
19.9M
            return (p - s);
101
800M
        p++;
102
800M
    }
103
169M
    return -1;
104
189M
}
bytesobject.c:stringlib_find_char
Line
Count
Source
51
444k
{
52
444k
    const STRINGLIB_CHAR *p, *e;
53
54
444k
    p = s;
55
444k
    e = s + n;
56
444k
    if (n > MEMCHR_CUT_OFF) {
57
444k
#ifdef STRINGLIB_FAST_MEMCHR
58
444k
        p = STRINGLIB_FAST_MEMCHR(s, ch, n);
59
444k
        if (p != NULL)
60
439k
            return (p - s);
61
4.86k
        return -1;
62
#else
63
        /* use memchr if we can choose a needle without too many likely
64
           false positives */
65
        const STRINGLIB_CHAR *s1, *e1;
66
        unsigned char needle = ch & 0xff;
67
        /* If looking for a multiple of 256, we'd have too
68
           many false positives looking for the '\0' byte in UCS2
69
           and UCS4 representations. */
70
        if (needle != 0) {
71
            do {
72
                const void *candidate = memchr(p, needle,
73
                                               (e - p) * sizeof(STRINGLIB_CHAR));
74
                if (candidate == NULL)
75
                    return -1;
76
                s1 = p;
77
                p = (const STRINGLIB_CHAR *)
78
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
79
                if (*p == ch)
80
                    return (p - s);
81
                /* False positive */
82
                p++;
83
                if (p - s1 > MEMCHR_CUT_OFF)
84
                    continue;
85
                if (e - p <= MEMCHR_CUT_OFF)
86
                    break;
87
                e1 = p + MEMCHR_CUT_OFF;
88
                while (p != e1) {
89
                    if (*p == ch)
90
                        return (p - s);
91
                    p++;
92
                }
93
            }
94
            while (e - p > MEMCHR_CUT_OFF);
95
        }
96
#endif
97
444k
    }
98
84
    while (p < e) {
99
84
        if (*p == ch)
100
14
            return (p - s);
101
70
        p++;
102
70
    }
103
0
    return -1;
104
14
}
unicodeobject.c:ucs1lib_find_char
Line
Count
Source
51
195M
{
52
195M
    const STRINGLIB_CHAR *p, *e;
53
54
195M
    p = s;
55
195M
    e = s + n;
56
195M
    if (n > MEMCHR_CUT_OFF) {
57
18.4M
#ifdef STRINGLIB_FAST_MEMCHR
58
18.4M
        p = STRINGLIB_FAST_MEMCHR(s, ch, n);
59
18.4M
        if (p != NULL)
60
17.4M
            return (p - s);
61
1.05M
        return -1;
62
#else
63
        /* use memchr if we can choose a needle without too many likely
64
           false positives */
65
        const STRINGLIB_CHAR *s1, *e1;
66
        unsigned char needle = ch & 0xff;
67
        /* If looking for a multiple of 256, we'd have too
68
           many false positives looking for the '\0' byte in UCS2
69
           and UCS4 representations. */
70
        if (needle != 0) {
71
            do {
72
                const void *candidate = memchr(p, needle,
73
                                               (e - p) * sizeof(STRINGLIB_CHAR));
74
                if (candidate == NULL)
75
                    return -1;
76
                s1 = p;
77
                p = (const STRINGLIB_CHAR *)
78
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
79
                if (*p == ch)
80
                    return (p - s);
81
                /* False positive */
82
                p++;
83
                if (p - s1 > MEMCHR_CUT_OFF)
84
                    continue;
85
                if (e - p <= MEMCHR_CUT_OFF)
86
                    break;
87
                e1 = p + MEMCHR_CUT_OFF;
88
                while (p != e1) {
89
                    if (*p == ch)
90
                        return (p - s);
91
                    p++;
92
                }
93
            }
94
            while (e - p > MEMCHR_CUT_OFF);
95
        }
96
#endif
97
18.4M
    }
98
894M
    while (p < e) {
99
729M
        if (*p == ch)
100
11.9M
            return (p - s);
101
717M
        p++;
102
717M
    }
103
165M
    return -1;
104
177M
}
unicodeobject.c:ucs2lib_find_char
Line
Count
Source
51
7.67M
{
52
7.67M
    const STRINGLIB_CHAR *p, *e;
53
54
7.67M
    p = s;
55
7.67M
    e = s + n;
56
7.67M
    if (n > MEMCHR_CUT_OFF) {
57
#ifdef STRINGLIB_FAST_MEMCHR
58
        p = STRINGLIB_FAST_MEMCHR(s, ch, n);
59
        if (p != NULL)
60
            return (p - s);
61
        return -1;
62
#else
63
        /* use memchr if we can choose a needle without too many likely
64
           false positives */
65
7.26M
        const STRINGLIB_CHAR *s1, *e1;
66
7.26M
        unsigned char needle = ch & 0xff;
67
        /* If looking for a multiple of 256, we'd have too
68
           many false positives looking for the '\0' byte in UCS2
69
           and UCS4 representations. */
70
7.26M
        if (needle != 0) {
71
7.28M
            do {
72
7.28M
                const void *candidate = memchr(p, needle,
73
7.28M
                                               (e - p) * sizeof(STRINGLIB_CHAR));
74
7.28M
                if (candidate == NULL)
75
61.2k
                    return -1;
76
7.21M
                s1 = p;
77
7.21M
                p = (const STRINGLIB_CHAR *)
78
7.21M
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
79
7.21M
                if (*p == ch)
80
7.18M
                    return (p - s);
81
                /* False positive */
82
29.8k
                p++;
83
29.8k
                if (p - s1 > MEMCHR_CUT_OFF)
84
12.1k
                    continue;
85
17.6k
                if (e - p <= MEMCHR_CUT_OFF)
86
1.88k
                    break;
87
15.8k
                e1 = p + MEMCHR_CUT_OFF;
88
545k
                while (p != e1) {
89
533k
                    if (*p == ch)
90
3.11k
                        return (p - s);
91
530k
                    p++;
92
530k
                }
93
15.8k
            }
94
7.25M
            while (e - p > MEMCHR_CUT_OFF);
95
7.25M
        }
96
7.26M
#endif
97
7.26M
    }
98
5.95M
    while (p < e) {
99
5.73M
        if (*p == ch)
100
205k
            return (p - s);
101
5.53M
        p++;
102
5.53M
    }
103
213k
    return -1;
104
418k
}
unicodeobject.c:ucs4lib_find_char
Line
Count
Source
51
5.03M
{
52
5.03M
    const STRINGLIB_CHAR *p, *e;
53
54
5.03M
    p = s;
55
5.03M
    e = s + n;
56
5.03M
    if (n > MEMCHR_CUT_OFF) {
57
4.97M
#ifdef STRINGLIB_FAST_MEMCHR
58
4.97M
        p = STRINGLIB_FAST_MEMCHR(s, ch, n);
59
4.97M
        if (p != NULL)
60
4.96M
            return (p - s);
61
16.1k
        return -1;
62
#else
63
        /* use memchr if we can choose a needle without too many likely
64
           false positives */
65
        const STRINGLIB_CHAR *s1, *e1;
66
        unsigned char needle = ch & 0xff;
67
        /* If looking for a multiple of 256, we'd have too
68
           many false positives looking for the '\0' byte in UCS2
69
           and UCS4 representations. */
70
        if (needle != 0) {
71
            do {
72
                const void *candidate = memchr(p, needle,
73
                                               (e - p) * sizeof(STRINGLIB_CHAR));
74
                if (candidate == NULL)
75
                    return -1;
76
                s1 = p;
77
                p = (const STRINGLIB_CHAR *)
78
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
79
                if (*p == ch)
80
                    return (p - s);
81
                /* False positive */
82
                p++;
83
                if (p - s1 > MEMCHR_CUT_OFF)
84
                    continue;
85
                if (e - p <= MEMCHR_CUT_OFF)
86
                    break;
87
                e1 = p + MEMCHR_CUT_OFF;
88
                while (p != e1) {
89
                    if (*p == ch)
90
                        return (p - s);
91
                    p++;
92
                }
93
            }
94
            while (e - p > MEMCHR_CUT_OFF);
95
        }
96
#endif
97
4.97M
    }
98
224k
    while (p < e) {
99
195k
        if (*p == ch)
100
26.6k
            return (p - s);
101
169k
        p++;
102
169k
    }
103
28.9k
    return -1;
104
55.5k
}
unicodeobject.c:asciilib_find_char
Line
Count
Source
51
4.76M
{
52
4.76M
    const STRINGLIB_CHAR *p, *e;
53
54
4.76M
    p = s;
55
4.76M
    e = s + n;
56
4.76M
    if (n > MEMCHR_CUT_OFF) {
57
3.94M
#ifdef STRINGLIB_FAST_MEMCHR
58
3.94M
        p = STRINGLIB_FAST_MEMCHR(s, ch, n);
59
3.94M
        if (p != NULL)
60
3.47M
            return (p - s);
61
475k
        return -1;
62
#else
63
        /* use memchr if we can choose a needle without too many likely
64
           false positives */
65
        const STRINGLIB_CHAR *s1, *e1;
66
        unsigned char needle = ch & 0xff;
67
        /* If looking for a multiple of 256, we'd have too
68
           many false positives looking for the '\0' byte in UCS2
69
           and UCS4 representations. */
70
        if (needle != 0) {
71
            do {
72
                const void *candidate = memchr(p, needle,
73
                                               (e - p) * sizeof(STRINGLIB_CHAR));
74
                if (candidate == NULL)
75
                    return -1;
76
                s1 = p;
77
                p = (const STRINGLIB_CHAR *)
78
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
79
                if (*p == ch)
80
                    return (p - s);
81
                /* False positive */
82
                p++;
83
                if (p - s1 > MEMCHR_CUT_OFF)
84
                    continue;
85
                if (e - p <= MEMCHR_CUT_OFF)
86
                    break;
87
                e1 = p + MEMCHR_CUT_OFF;
88
                while (p != e1) {
89
                    if (*p == ch)
90
                        return (p - s);
91
                    p++;
92
                }
93
            }
94
            while (e - p > MEMCHR_CUT_OFF);
95
        }
96
#endif
97
3.94M
    }
98
2.62M
    while (p < e) {
99
2.57M
        if (*p == ch)
100
763k
            return (p - s);
101
1.80M
        p++;
102
1.80M
    }
103
56.4k
    return -1;
104
820k
}
bytes_methods.c:stringlib_find_char
Line
Count
Source
51
16.1M
{
52
16.1M
    const STRINGLIB_CHAR *p, *e;
53
54
16.1M
    p = s;
55
16.1M
    e = s + n;
56
16.1M
    if (n > MEMCHR_CUT_OFF) {
57
5.29M
#ifdef STRINGLIB_FAST_MEMCHR
58
5.29M
        p = STRINGLIB_FAST_MEMCHR(s, ch, n);
59
5.29M
        if (p != NULL)
60
5.15M
            return (p - s);
61
141k
        return -1;
62
#else
63
        /* use memchr if we can choose a needle without too many likely
64
           false positives */
65
        const STRINGLIB_CHAR *s1, *e1;
66
        unsigned char needle = ch & 0xff;
67
        /* If looking for a multiple of 256, we'd have too
68
           many false positives looking for the '\0' byte in UCS2
69
           and UCS4 representations. */
70
        if (needle != 0) {
71
            do {
72
                const void *candidate = memchr(p, needle,
73
                                               (e - p) * sizeof(STRINGLIB_CHAR));
74
                if (candidate == NULL)
75
                    return -1;
76
                s1 = p;
77
                p = (const STRINGLIB_CHAR *)
78
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
79
                if (*p == ch)
80
                    return (p - s);
81
                /* False positive */
82
                p++;
83
                if (p - s1 > MEMCHR_CUT_OFF)
84
                    continue;
85
                if (e - p <= MEMCHR_CUT_OFF)
86
                    break;
87
                e1 = p + MEMCHR_CUT_OFF;
88
                while (p != e1) {
89
                    if (*p == ch)
90
                        return (p - s);
91
                    p++;
92
                }
93
            }
94
            while (e - p > MEMCHR_CUT_OFF);
95
        }
96
#endif
97
5.29M
    }
98
85.9M
    while (p < e) {
99
82.1M
        if (*p == ch)
100
7.02M
            return (p - s);
101
75.1M
        p++;
102
75.1M
    }
103
3.81M
    return -1;
104
10.8M
}
Unexecuted instantiation: bytearrayobject.c:stringlib_find_char
105
106
#undef MEMCHR_CUT_OFF
107
108
#if STRINGLIB_SIZEOF_CHAR == 1
109
135k
#  define MEMRCHR_CUT_OFF 15
110
#else
111
414k
#  define MEMRCHR_CUT_OFF 40
112
#endif
113
114
115
Py_LOCAL_INLINE(Py_ssize_t)
116
STRINGLIB(rfind_char)(const STRINGLIB_CHAR* s, Py_ssize_t n, STRINGLIB_CHAR ch)
117
417k
{
118
417k
    const STRINGLIB_CHAR *p;
119
417k
#ifdef HAVE_MEMRCHR
120
    /* memrchr() is a GNU extension, available since glibc 2.1.91.  it
121
       doesn't seem as optimized as memchr(), but is still quite
122
       faster than our hand-written loop below. There is no wmemrchr
123
       for 4-byte chars. */
124
125
417k
    if (n > MEMRCHR_CUT_OFF) {
126
#if STRINGLIB_SIZEOF_CHAR == 1
127
        p = memrchr(s, ch, n);
128
82.3k
        if (p != NULL)
129
79.2k
            return (p - s);
130
3.08k
        return -1;
131
#else
132
        /* use memrchr if we can choose a needle without too many likely
133
           false positives */
134
        const STRINGLIB_CHAR *s1;
135
        Py_ssize_t n1;
136
        unsigned char needle = ch & 0xff;
137
        /* If looking for a multiple of 256, we'd have too
138
           many false positives looking for the '\0' byte in UCS2
139
           and UCS4 representations. */
140
250k
        if (needle != 0) {
141
284k
            do {
142
284k
                void *candidate = memrchr(s, needle,
143
284k
                                          n * sizeof(STRINGLIB_CHAR));
144
284k
                if (candidate == NULL)
145
1.39k
                    return -1;
146
283k
                n1 = n;
147
283k
                p = (const STRINGLIB_CHAR *)
148
283k
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
149
283k
                n = p - s;
150
283k
                if (*p == ch)
151
246k
                    return n;
152
                /* False positive */
153
36.3k
                if (n1 - n > MEMRCHR_CUT_OFF)
154
5.54k
                    continue;
155
30.8k
                if (n <= MEMRCHR_CUT_OFF)
156
880
                    break;
157
29.9k
                s1 = p - MEMRCHR_CUT_OFF;
158
1.21M
                while (p > s1) {
159
1.18M
                    p--;
160
1.18M
                    if (*p == ch)
161
614
                        return (p - s);
162
1.18M
                }
163
29.3k
                n = p - s;
164
29.3k
            }
165
250k
            while (n > MEMRCHR_CUT_OFF);
166
250k
        }
167
#endif
168
332k
    }
169
86.4k
#endif  /* HAVE_MEMRCHR */
170
86.4k
    p = s + n;
171
509k
    while (p > s) {
172
471k
        p--;
173
471k
        if (*p == ch)
174
48.4k
            return (p - s);
175
471k
    }
176
37.9k
    return -1;
177
86.4k
}
Unexecuted instantiation: bytesobject.c:stringlib_rfind_char
unicodeobject.c:ucs1lib_rfind_char
Line
Count
Source
117
65.8k
{
118
65.8k
    const STRINGLIB_CHAR *p;
119
65.8k
#ifdef HAVE_MEMRCHR
120
    /* memrchr() is a GNU extension, available since glibc 2.1.91.  it
121
       doesn't seem as optimized as memchr(), but is still quite
122
       faster than our hand-written loop below. There is no wmemrchr
123
       for 4-byte chars. */
124
125
65.8k
    if (n > MEMRCHR_CUT_OFF) {
126
61.8k
#if STRINGLIB_SIZEOF_CHAR == 1
127
61.8k
        p = memrchr(s, ch, n);
128
61.8k
        if (p != NULL)
129
60.6k
            return (p - s);
130
1.14k
        return -1;
131
#else
132
        /* use memrchr if we can choose a needle without too many likely
133
           false positives */
134
        const STRINGLIB_CHAR *s1;
135
        Py_ssize_t n1;
136
        unsigned char needle = ch & 0xff;
137
        /* If looking for a multiple of 256, we'd have too
138
           many false positives looking for the '\0' byte in UCS2
139
           and UCS4 representations. */
140
        if (needle != 0) {
141
            do {
142
                void *candidate = memrchr(s, needle,
143
                                          n * sizeof(STRINGLIB_CHAR));
144
                if (candidate == NULL)
145
                    return -1;
146
                n1 = n;
147
                p = (const STRINGLIB_CHAR *)
148
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
149
                n = p - s;
150
                if (*p == ch)
151
                    return n;
152
                /* False positive */
153
                if (n1 - n > MEMRCHR_CUT_OFF)
154
                    continue;
155
                if (n <= MEMRCHR_CUT_OFF)
156
                    break;
157
                s1 = p - MEMRCHR_CUT_OFF;
158
                while (p > s1) {
159
                    p--;
160
                    if (*p == ch)
161
                        return (p - s);
162
                }
163
                n = p - s;
164
            }
165
            while (n > MEMRCHR_CUT_OFF);
166
        }
167
#endif
168
61.8k
    }
169
4.02k
#endif  /* HAVE_MEMRCHR */
170
4.02k
    p = s + n;
171
14.7k
    while (p > s) {
172
13.4k
        p--;
173
13.4k
        if (*p == ch)
174
2.77k
            return (p - s);
175
13.4k
    }
176
1.24k
    return -1;
177
4.02k
}
unicodeobject.c:ucs2lib_rfind_char
Line
Count
Source
117
194k
{
118
194k
    const STRINGLIB_CHAR *p;
119
194k
#ifdef HAVE_MEMRCHR
120
    /* memrchr() is a GNU extension, available since glibc 2.1.91.  it
121
       doesn't seem as optimized as memchr(), but is still quite
122
       faster than our hand-written loop below. There is no wmemrchr
123
       for 4-byte chars. */
124
125
194k
    if (n > MEMRCHR_CUT_OFF) {
126
#if STRINGLIB_SIZEOF_CHAR == 1
127
        p = memrchr(s, ch, n);
128
        if (p != NULL)
129
            return (p - s);
130
        return -1;
131
#else
132
        /* use memrchr if we can choose a needle without too many likely
133
           false positives */
134
183k
        const STRINGLIB_CHAR *s1;
135
183k
        Py_ssize_t n1;
136
183k
        unsigned char needle = ch & 0xff;
137
        /* If looking for a multiple of 256, we'd have too
138
           many false positives looking for the '\0' byte in UCS2
139
           and UCS4 representations. */
140
183k
        if (needle != 0) {
141
185k
            do {
142
185k
                void *candidate = memrchr(s, needle,
143
185k
                                          n * sizeof(STRINGLIB_CHAR));
144
185k
                if (candidate == NULL)
145
783
                    return -1;
146
185k
                n1 = n;
147
185k
                p = (const STRINGLIB_CHAR *)
148
185k
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
149
185k
                n = p - s;
150
185k
                if (*p == ch)
151
181k
                    return n;
152
                /* False positive */
153
3.78k
                if (n1 - n > MEMRCHR_CUT_OFF)
154
1.77k
                    continue;
155
2.01k
                if (n <= MEMRCHR_CUT_OFF)
156
475
                    break;
157
1.53k
                s1 = p - MEMRCHR_CUT_OFF;
158
56.8k
                while (p > s1) {
159
55.5k
                    p--;
160
55.5k
                    if (*p == ch)
161
249
                        return (p - s);
162
55.5k
                }
163
1.28k
                n = p - s;
164
1.28k
            }
165
183k
            while (n > MEMRCHR_CUT_OFF);
166
183k
        }
167
183k
#endif
168
183k
    }
169
12.2k
#endif  /* HAVE_MEMRCHR */
170
12.2k
    p = s + n;
171
61.2k
    while (p > s) {
172
59.1k
        p--;
173
59.1k
        if (*p == ch)
174
10.0k
            return (p - s);
175
59.1k
    }
176
2.16k
    return -1;
177
12.2k
}
unicodeobject.c:ucs4lib_rfind_char
Line
Count
Source
117
88.0k
{
118
88.0k
    const STRINGLIB_CHAR *p;
119
88.0k
#ifdef HAVE_MEMRCHR
120
    /* memrchr() is a GNU extension, available since glibc 2.1.91.  it
121
       doesn't seem as optimized as memchr(), but is still quite
122
       faster than our hand-written loop below. There is no wmemrchr
123
       for 4-byte chars. */
124
125
88.0k
    if (n > MEMRCHR_CUT_OFF) {
126
#if STRINGLIB_SIZEOF_CHAR == 1
127
        p = memrchr(s, ch, n);
128
        if (p != NULL)
129
            return (p - s);
130
        return -1;
131
#else
132
        /* use memrchr if we can choose a needle without too many likely
133
           false positives */
134
67.3k
        const STRINGLIB_CHAR *s1;
135
67.3k
        Py_ssize_t n1;
136
67.3k
        unsigned char needle = ch & 0xff;
137
        /* If looking for a multiple of 256, we'd have too
138
           many false positives looking for the '\0' byte in UCS2
139
           and UCS4 representations. */
140
67.3k
        if (needle != 0) {
141
98.8k
            do {
142
98.8k
                void *candidate = memrchr(s, needle,
143
98.8k
                                          n * sizeof(STRINGLIB_CHAR));
144
98.8k
                if (candidate == NULL)
145
613
                    return -1;
146
98.1k
                n1 = n;
147
98.1k
                p = (const STRINGLIB_CHAR *)
148
98.1k
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
149
98.1k
                n = p - s;
150
98.1k
                if (*p == ch)
151
65.5k
                    return n;
152
                /* False positive */
153
32.6k
                if (n1 - n > MEMRCHR_CUT_OFF)
154
3.76k
                    continue;
155
28.8k
                if (n <= MEMRCHR_CUT_OFF)
156
405
                    break;
157
28.4k
                s1 = p - MEMRCHR_CUT_OFF;
158
1.15M
                while (p > s1) {
159
1.12M
                    p--;
160
1.12M
                    if (*p == ch)
161
365
                        return (p - s);
162
1.12M
                }
163
28.0k
                n = p - s;
164
28.0k
            }
165
67.3k
            while (n > MEMRCHR_CUT_OFF);
166
67.3k
        }
167
67.3k
#endif
168
67.3k
    }
169
21.4k
#endif  /* HAVE_MEMRCHR */
170
21.4k
    p = s + n;
171
161k
    while (p > s) {
172
159k
        p--;
173
159k
        if (*p == ch)
174
19.7k
            return (p - s);
175
159k
    }
176
1.75k
    return -1;
177
21.4k
}
unicodeobject.c:asciilib_rfind_char
Line
Count
Source
117
51.9k
{
118
51.9k
    const STRINGLIB_CHAR *p;
119
51.9k
#ifdef HAVE_MEMRCHR
120
    /* memrchr() is a GNU extension, available since glibc 2.1.91.  it
121
       doesn't seem as optimized as memchr(), but is still quite
122
       faster than our hand-written loop below. There is no wmemrchr
123
       for 4-byte chars. */
124
125
51.9k
    if (n > MEMRCHR_CUT_OFF) {
126
18.2k
#if STRINGLIB_SIZEOF_CHAR == 1
127
18.2k
        p = memrchr(s, ch, n);
128
18.2k
        if (p != NULL)
129
17.9k
            return (p - s);
130
280
        return -1;
131
#else
132
        /* use memrchr if we can choose a needle without too many likely
133
           false positives */
134
        const STRINGLIB_CHAR *s1;
135
        Py_ssize_t n1;
136
        unsigned char needle = ch & 0xff;
137
        /* If looking for a multiple of 256, we'd have too
138
           many false positives looking for the '\0' byte in UCS2
139
           and UCS4 representations. */
140
        if (needle != 0) {
141
            do {
142
                void *candidate = memrchr(s, needle,
143
                                          n * sizeof(STRINGLIB_CHAR));
144
                if (candidate == NULL)
145
                    return -1;
146
                n1 = n;
147
                p = (const STRINGLIB_CHAR *)
148
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
149
                n = p - s;
150
                if (*p == ch)
151
                    return n;
152
                /* False positive */
153
                if (n1 - n > MEMRCHR_CUT_OFF)
154
                    continue;
155
                if (n <= MEMRCHR_CUT_OFF)
156
                    break;
157
                s1 = p - MEMRCHR_CUT_OFF;
158
                while (p > s1) {
159
                    p--;
160
                    if (*p == ch)
161
                        return (p - s);
162
                }
163
                n = p - s;
164
            }
165
            while (n > MEMRCHR_CUT_OFF);
166
        }
167
#endif
168
18.2k
    }
169
33.7k
#endif  /* HAVE_MEMRCHR */
170
33.7k
    p = s + n;
171
192k
    while (p > s) {
172
169k
        p--;
173
169k
        if (*p == ch)
174
11.4k
            return (p - s);
175
169k
    }
176
22.3k
    return -1;
177
33.7k
}
bytes_methods.c:stringlib_rfind_char
Line
Count
Source
117
17.2k
{
118
17.2k
    const STRINGLIB_CHAR *p;
119
17.2k
#ifdef HAVE_MEMRCHR
120
    /* memrchr() is a GNU extension, available since glibc 2.1.91.  it
121
       doesn't seem as optimized as memchr(), but is still quite
122
       faster than our hand-written loop below. There is no wmemrchr
123
       for 4-byte chars. */
124
125
17.2k
    if (n > MEMRCHR_CUT_OFF) {
126
2.31k
#if STRINGLIB_SIZEOF_CHAR == 1
127
2.31k
        p = memrchr(s, ch, n);
128
2.31k
        if (p != NULL)
129
653
            return (p - s);
130
1.66k
        return -1;
131
#else
132
        /* use memrchr if we can choose a needle without too many likely
133
           false positives */
134
        const STRINGLIB_CHAR *s1;
135
        Py_ssize_t n1;
136
        unsigned char needle = ch & 0xff;
137
        /* If looking for a multiple of 256, we'd have too
138
           many false positives looking for the '\0' byte in UCS2
139
           and UCS4 representations. */
140
        if (needle != 0) {
141
            do {
142
                void *candidate = memrchr(s, needle,
143
                                          n * sizeof(STRINGLIB_CHAR));
144
                if (candidate == NULL)
145
                    return -1;
146
                n1 = n;
147
                p = (const STRINGLIB_CHAR *)
148
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
149
                n = p - s;
150
                if (*p == ch)
151
                    return n;
152
                /* False positive */
153
                if (n1 - n > MEMRCHR_CUT_OFF)
154
                    continue;
155
                if (n <= MEMRCHR_CUT_OFF)
156
                    break;
157
                s1 = p - MEMRCHR_CUT_OFF;
158
                while (p > s1) {
159
                    p--;
160
                    if (*p == ch)
161
                        return (p - s);
162
                }
163
                n = p - s;
164
            }
165
            while (n > MEMRCHR_CUT_OFF);
166
        }
167
#endif
168
2.31k
    }
169
14.9k
#endif  /* HAVE_MEMRCHR */
170
14.9k
    p = s + n;
171
80.0k
    while (p > s) {
172
69.5k
        p--;
173
69.5k
        if (*p == ch)
174
4.53k
            return (p - s);
175
69.5k
    }
176
10.4k
    return -1;
177
14.9k
}
Unexecuted instantiation: bytearrayobject.c:stringlib_rfind_char
178
179
#undef MEMRCHR_CUT_OFF
180
181
/* Change to a 1 to see logging comments walk through the algorithm. */
182
#if 0 && STRINGLIB_SIZEOF_CHAR == 1
183
# define LOG(...) printf(__VA_ARGS__)
184
# define LOG_STRING(s, n) printf("\"%.*s\"", (int)(n), s)
185
# define LOG_LINEUP() do {                                         \
186
    LOG("> "); LOG_STRING(haystack, len_haystack); LOG("\n> ");    \
187
    LOG("%*s",(int)(window_last - haystack + 1 - len_needle), ""); \
188
    LOG_STRING(needle, len_needle); LOG("\n");                     \
189
} while(0)
190
#else
191
# define LOG(...)
192
# define LOG_STRING(s, n)
193
# define LOG_LINEUP()
194
#endif
195
196
Py_LOCAL_INLINE(Py_ssize_t)
197
STRINGLIB(_lex_search)(const STRINGLIB_CHAR *needle, Py_ssize_t len_needle,
198
                       Py_ssize_t *return_period, int invert_alphabet)
199
46
{
200
    /* Do a lexicographic search. Essentially this:
201
           >>> max(needle[i:] for i in range(len(needle)+1))
202
       Also find the period of the right half.   */
203
46
    Py_ssize_t max_suffix = 0;
204
46
    Py_ssize_t candidate = 1;
205
46
    Py_ssize_t k = 0;
206
    // The period of the right half.
207
46
    Py_ssize_t period = 1;
208
209
460
    while (candidate + k < len_needle) {
210
        // each loop increases candidate + k + max_suffix
211
414
        STRINGLIB_CHAR a = needle[candidate + k];
212
414
        STRINGLIB_CHAR b = needle[max_suffix + k];
213
        // check if the suffix at candidate is better than max_suffix
214
414
        if (invert_alphabet ? (b < a) : (a < b)) {
215
            // Fell short of max_suffix.
216
            // The next k + 1 characters are non-increasing
217
            // from candidate, so they won't start a maximal suffix.
218
299
            candidate += k + 1;
219
299
            k = 0;
220
            // We've ruled out any period smaller than what's
221
            // been scanned since max_suffix.
222
299
            period = candidate - max_suffix;
223
299
        }
224
115
        else if (a == b) {
225
23
            if (k + 1 != period) {
226
                // Keep scanning the equal strings
227
0
                k++;
228
0
            }
229
23
            else {
230
                // Matched a whole period.
231
                // Start matching the next period.
232
23
                candidate += period;
233
23
                k = 0;
234
23
            }
235
23
        }
236
92
        else {
237
            // Did better than max_suffix, so replace it.
238
92
            max_suffix = candidate;
239
92
            candidate++;
240
92
            k = 0;
241
92
            period = 1;
242
92
        }
243
414
    }
244
46
    *return_period = period;
245
46
    return max_suffix;
246
46
}
Unexecuted instantiation: bytesobject.c:stringlib__lex_search
Unexecuted instantiation: unicodeobject.c:asciilib__lex_search
unicodeobject.c:ucs1lib__lex_search
Line
Count
Source
199
46
{
200
    /* Do a lexicographic search. Essentially this:
201
           >>> max(needle[i:] for i in range(len(needle)+1))
202
       Also find the period of the right half.   */
203
46
    Py_ssize_t max_suffix = 0;
204
46
    Py_ssize_t candidate = 1;
205
46
    Py_ssize_t k = 0;
206
    // The period of the right half.
207
46
    Py_ssize_t period = 1;
208
209
460
    while (candidate + k < len_needle) {
210
        // each loop increases candidate + k + max_suffix
211
414
        STRINGLIB_CHAR a = needle[candidate + k];
212
414
        STRINGLIB_CHAR b = needle[max_suffix + k];
213
        // check if the suffix at candidate is better than max_suffix
214
414
        if (invert_alphabet ? (b < a) : (a < b)) {
215
            // Fell short of max_suffix.
216
            // The next k + 1 characters are non-increasing
217
            // from candidate, so they won't start a maximal suffix.
218
299
            candidate += k + 1;
219
299
            k = 0;
220
            // We've ruled out any period smaller than what's
221
            // been scanned since max_suffix.
222
299
            period = candidate - max_suffix;
223
299
        }
224
115
        else if (a == b) {
225
23
            if (k + 1 != period) {
226
                // Keep scanning the equal strings
227
0
                k++;
228
0
            }
229
23
            else {
230
                // Matched a whole period.
231
                // Start matching the next period.
232
23
                candidate += period;
233
23
                k = 0;
234
23
            }
235
23
        }
236
92
        else {
237
            // Did better than max_suffix, so replace it.
238
92
            max_suffix = candidate;
239
92
            candidate++;
240
92
            k = 0;
241
92
            period = 1;
242
92
        }
243
414
    }
244
46
    *return_period = period;
245
46
    return max_suffix;
246
46
}
Unexecuted instantiation: unicodeobject.c:ucs2lib__lex_search
Unexecuted instantiation: unicodeobject.c:ucs4lib__lex_search
Unexecuted instantiation: bytes_methods.c:stringlib__lex_search
Unexecuted instantiation: bytearrayobject.c:stringlib__lex_search
247
248
Py_LOCAL_INLINE(Py_ssize_t)
249
STRINGLIB(_factorize)(const STRINGLIB_CHAR *needle,
250
                      Py_ssize_t len_needle,
251
                      Py_ssize_t *return_period)
252
23
{
253
    /* Do a "critical factorization", making it so that:
254
       >>> needle = (left := needle[:cut]) + (right := needle[cut:])
255
       where the "local period" of the cut is maximal.
256
257
       The local period of the cut is the minimal length of a string w
258
       such that (left endswith w or w endswith left)
259
       and (right startswith w or w startswith right).
260
261
       The Critical Factorization Theorem says that this maximal local
262
       period is the global period of the string.
263
264
       Crochemore and Perrin (1991) show that this cut can be computed
265
       as the later of two cuts: one that gives a lexicographically
266
       maximal right half, and one that gives the same with the
267
       with respect to a reversed alphabet-ordering.
268
269
       This is what we want to happen:
270
           >>> x = "GCAGAGAG"
271
           >>> cut, period = factorize(x)
272
           >>> x[:cut], (right := x[cut:])
273
           ('GC', 'AGAGAG')
274
           >>> period  # right half period
275
           2
276
           >>> right[period:] == right[:-period]
277
           True
278
279
       This is how the local period lines up in the above example:
280
                GC | AGAGAG
281
           AGAGAGC = AGAGAGC
282
       The length of this minimal repetition is 7, which is indeed the
283
       period of the original string. */
284
285
23
    Py_ssize_t cut1, period1, cut2, period2, cut, period;
286
23
    cut1 = STRINGLIB(_lex_search)(needle, len_needle, &period1, 0);
287
23
    cut2 = STRINGLIB(_lex_search)(needle, len_needle, &period2, 1);
288
289
    // Take the later cut.
290
23
    if (cut1 > cut2) {
291
23
        period = period1;
292
23
        cut = cut1;
293
23
    }
294
0
    else {
295
0
        period = period2;
296
0
        cut = cut2;
297
0
    }
298
299
23
    LOG("split: "); LOG_STRING(needle, cut);
300
23
    LOG(" + "); LOG_STRING(needle + cut, len_needle - cut);
301
23
    LOG("\n");
302
303
23
    *return_period = period;
304
23
    return cut;
305
23
}
Unexecuted instantiation: bytesobject.c:stringlib__factorize
Unexecuted instantiation: unicodeobject.c:asciilib__factorize
unicodeobject.c:ucs1lib__factorize
Line
Count
Source
252
23
{
253
    /* Do a "critical factorization", making it so that:
254
       >>> needle = (left := needle[:cut]) + (right := needle[cut:])
255
       where the "local period" of the cut is maximal.
256
257
       The local period of the cut is the minimal length of a string w
258
       such that (left endswith w or w endswith left)
259
       and (right startswith w or w startswith right).
260
261
       The Critical Factorization Theorem says that this maximal local
262
       period is the global period of the string.
263
264
       Crochemore and Perrin (1991) show that this cut can be computed
265
       as the later of two cuts: one that gives a lexicographically
266
       maximal right half, and one that gives the same with the
267
       with respect to a reversed alphabet-ordering.
268
269
       This is what we want to happen:
270
           >>> x = "GCAGAGAG"
271
           >>> cut, period = factorize(x)
272
           >>> x[:cut], (right := x[cut:])
273
           ('GC', 'AGAGAG')
274
           >>> period  # right half period
275
           2
276
           >>> right[period:] == right[:-period]
277
           True
278
279
       This is how the local period lines up in the above example:
280
                GC | AGAGAG
281
           AGAGAGC = AGAGAGC
282
       The length of this minimal repetition is 7, which is indeed the
283
       period of the original string. */
284
285
23
    Py_ssize_t cut1, period1, cut2, period2, cut, period;
286
23
    cut1 = STRINGLIB(_lex_search)(needle, len_needle, &period1, 0);
287
23
    cut2 = STRINGLIB(_lex_search)(needle, len_needle, &period2, 1);
288
289
    // Take the later cut.
290
23
    if (cut1 > cut2) {
291
23
        period = period1;
292
23
        cut = cut1;
293
23
    }
294
0
    else {
295
0
        period = period2;
296
0
        cut = cut2;
297
0
    }
298
299
23
    LOG("split: "); LOG_STRING(needle, cut);
300
23
    LOG(" + "); LOG_STRING(needle + cut, len_needle - cut);
301
23
    LOG("\n");
302
303
23
    *return_period = period;
304
23
    return cut;
305
23
}
Unexecuted instantiation: unicodeobject.c:ucs2lib__factorize
Unexecuted instantiation: unicodeobject.c:ucs4lib__factorize
Unexecuted instantiation: bytes_methods.c:stringlib__factorize
Unexecuted instantiation: bytearrayobject.c:stringlib__factorize
306
307
308
253
#define SHIFT_TYPE uint8_t
309
#define MAX_SHIFT UINT8_MAX
310
311
82.8k
#define TABLE_SIZE_BITS 6u
312
82.8k
#define TABLE_SIZE (1U << TABLE_SIZE_BITS)
313
81.3k
#define TABLE_MASK (TABLE_SIZE - 1U)
314
315
typedef struct STRINGLIB(_pre) {
316
    const STRINGLIB_CHAR *needle;
317
    Py_ssize_t len_needle;
318
    Py_ssize_t cut;
319
    Py_ssize_t period;
320
    Py_ssize_t gap;
321
    int is_periodic;
322
    SHIFT_TYPE table[TABLE_SIZE];
323
} STRINGLIB(prework);
324
325
326
static void
327
STRINGLIB(_preprocess)(const STRINGLIB_CHAR *needle, Py_ssize_t len_needle,
328
                       STRINGLIB(prework) *p)
329
23
{
330
23
    p->needle = needle;
331
23
    p->len_needle = len_needle;
332
23
    p->cut = STRINGLIB(_factorize)(needle, len_needle, &(p->period));
333
23
    assert(p->period + p->cut <= len_needle);
334
23
    p->is_periodic = (0 == memcmp(needle,
335
23
                                  needle + p->period,
336
23
                                  p->cut * STRINGLIB_SIZEOF_CHAR));
337
23
    if (p->is_periodic) {
338
0
        assert(p->cut <= len_needle/2);
339
0
        assert(p->cut < p->period);
340
0
    }
341
23
    else {
342
        // A lower bound on the period
343
23
        p->period = Py_MAX(p->cut, len_needle - p->cut) + 1;
344
23
    }
345
    // The gap between the last character and the previous
346
    // occurrence of an equivalent character (modulo TABLE_SIZE)
347
23
    p->gap = len_needle;
348
23
    STRINGLIB_CHAR last = needle[len_needle - 1] & TABLE_MASK;
349
161
    for (Py_ssize_t i = len_needle - 2; i >= 0; i--) {
350
161
        STRINGLIB_CHAR x = needle[i] & TABLE_MASK;
351
161
        if (x == last) {
352
23
            p->gap = len_needle - 1 - i;
353
23
            break;
354
23
        }
355
161
    }
356
    // Fill up a compressed Boyer-Moore "Bad Character" table
357
23
    Py_ssize_t not_found_shift = Py_MIN(len_needle, MAX_SHIFT);
358
1.49k
    for (Py_ssize_t i = 0; i < (Py_ssize_t)TABLE_SIZE; i++) {
359
1.47k
        p->table[i] = Py_SAFE_DOWNCAST(not_found_shift,
360
1.47k
                                       Py_ssize_t, SHIFT_TYPE);
361
1.47k
    }
362
253
    for (Py_ssize_t i = len_needle - not_found_shift; i < len_needle; i++) {
363
230
        SHIFT_TYPE shift = Py_SAFE_DOWNCAST(len_needle - 1 - i,
364
230
                                            Py_ssize_t, SHIFT_TYPE);
365
230
        p->table[needle[i] & TABLE_MASK] = shift;
366
230
    }
367
23
}
Unexecuted instantiation: bytesobject.c:stringlib__preprocess
Unexecuted instantiation: unicodeobject.c:asciilib__preprocess
unicodeobject.c:ucs1lib__preprocess
Line
Count
Source
329
23
{
330
23
    p->needle = needle;
331
23
    p->len_needle = len_needle;
332
23
    p->cut = STRINGLIB(_factorize)(needle, len_needle, &(p->period));
333
23
    assert(p->period + p->cut <= len_needle);
334
23
    p->is_periodic = (0 == memcmp(needle,
335
23
                                  needle + p->period,
336
23
                                  p->cut * STRINGLIB_SIZEOF_CHAR));
337
23
    if (p->is_periodic) {
338
0
        assert(p->cut <= len_needle/2);
339
0
        assert(p->cut < p->period);
340
0
    }
341
23
    else {
342
        // A lower bound on the period
343
23
        p->period = Py_MAX(p->cut, len_needle - p->cut) + 1;
344
23
    }
345
    // The gap between the last character and the previous
346
    // occurrence of an equivalent character (modulo TABLE_SIZE)
347
23
    p->gap = len_needle;
348
23
    STRINGLIB_CHAR last = needle[len_needle - 1] & TABLE_MASK;
349
161
    for (Py_ssize_t i = len_needle - 2; i >= 0; i--) {
350
161
        STRINGLIB_CHAR x = needle[i] & TABLE_MASK;
351
161
        if (x == last) {
352
23
            p->gap = len_needle - 1 - i;
353
23
            break;
354
23
        }
355
161
    }
356
    // Fill up a compressed Boyer-Moore "Bad Character" table
357
23
    Py_ssize_t not_found_shift = Py_MIN(len_needle, MAX_SHIFT);
358
1.49k
    for (Py_ssize_t i = 0; i < (Py_ssize_t)TABLE_SIZE; i++) {
359
1.47k
        p->table[i] = Py_SAFE_DOWNCAST(not_found_shift,
360
1.47k
                                       Py_ssize_t, SHIFT_TYPE);
361
1.47k
    }
362
253
    for (Py_ssize_t i = len_needle - not_found_shift; i < len_needle; i++) {
363
230
        SHIFT_TYPE shift = Py_SAFE_DOWNCAST(len_needle - 1 - i,
364
230
                                            Py_ssize_t, SHIFT_TYPE);
365
230
        p->table[needle[i] & TABLE_MASK] = shift;
366
230
    }
367
23
}
Unexecuted instantiation: unicodeobject.c:ucs2lib__preprocess
Unexecuted instantiation: unicodeobject.c:ucs4lib__preprocess
Unexecuted instantiation: bytes_methods.c:stringlib__preprocess
Unexecuted instantiation: bytearrayobject.c:stringlib__preprocess
368
369
static Py_ssize_t
370
STRINGLIB(_two_way)(const STRINGLIB_CHAR *haystack, Py_ssize_t len_haystack,
371
                    STRINGLIB(prework) *p)
372
23
{
373
    // Crochemore and Perrin's (1991) Two-Way algorithm.
374
    // See http://www-igm.univ-mlv.fr/~lecroq/string/node26.html#SECTION00260
375
23
    const Py_ssize_t len_needle = p->len_needle;
376
23
    const Py_ssize_t cut = p->cut;
377
23
    Py_ssize_t period = p->period;
378
23
    const STRINGLIB_CHAR *const needle = p->needle;
379
23
    const STRINGLIB_CHAR *window_last = haystack + len_needle - 1;
380
23
    const STRINGLIB_CHAR *const haystack_end = haystack + len_haystack;
381
23
    SHIFT_TYPE *table = p->table;
382
23
    const STRINGLIB_CHAR *window;
383
23
    LOG("===== Two-way: \"%s\" in \"%s\". =====\n", needle, haystack);
384
385
23
    Py_ssize_t gap = p->gap;
386
23
    Py_ssize_t gap_jump_end = Py_MIN(len_needle, cut + gap);
387
23
    if (p->is_periodic) {
388
0
        LOG("Needle is periodic.\n");
389
0
        Py_ssize_t memory = 0;
390
0
      periodicwindowloop:
391
0
        while (window_last < haystack_end) {
392
0
            assert(memory == 0);
393
0
            for (;;) {
394
0
                LOG_LINEUP();
395
0
                Py_ssize_t shift = table[(*window_last) & TABLE_MASK];
396
0
                window_last += shift;
397
0
                if (shift == 0) {
398
0
                    break;
399
0
                }
400
0
                if (window_last >= haystack_end) {
401
0
                    return -1;
402
0
                }
403
0
                LOG("Horspool skip\n");
404
0
            }
405
0
          no_shift:
406
0
            window = window_last - len_needle + 1;
407
0
            assert((window[len_needle - 1] & TABLE_MASK) ==
408
0
                   (needle[len_needle - 1] & TABLE_MASK));
409
0
            Py_ssize_t i = Py_MAX(cut, memory);
410
0
            for (; i < len_needle; i++) {
411
0
                if (needle[i] != window[i]) {
412
0
                    if (i < gap_jump_end) {
413
0
                        LOG("Early right half mismatch: jump by gap.\n");
414
0
                        assert(gap >= i - cut + 1);
415
0
                        window_last += gap;
416
0
                    }
417
0
                    else {
418
0
                        LOG("Late right half mismatch: jump by n (>gap)\n");
419
0
                        assert(i - cut + 1 > gap);
420
0
                        window_last += i - cut + 1;
421
0
                    }
422
0
                    memory = 0;
423
0
                    goto periodicwindowloop;
424
0
                }
425
0
            }
426
0
            for (i = memory; i < cut; i++) {
427
0
                if (needle[i] != window[i]) {
428
0
                    LOG("Left half does not match.\n");
429
0
                    window_last += period;
430
0
                    memory = len_needle - period;
431
0
                    if (window_last >= haystack_end) {
432
0
                        return -1;
433
0
                    }
434
0
                    Py_ssize_t shift = table[(*window_last) & TABLE_MASK];
435
0
                    if (shift) {
436
                        // A mismatch has been identified to the right
437
                        // of where i will next start, so we can jump
438
                        // at least as far as if the mismatch occurred
439
                        // on the first comparison.
440
0
                        Py_ssize_t mem_jump = Py_MAX(cut, memory) - cut + 1;
441
0
                        LOG("Skip with Memory.\n");
442
0
                        memory = 0;
443
0
                        window_last += Py_MAX(shift, mem_jump);
444
0
                        goto periodicwindowloop;
445
0
                    }
446
0
                    goto no_shift;
447
0
                }
448
0
            }
449
0
            LOG("Found a match!\n");
450
0
            return window - haystack;
451
0
        }
452
0
    }
453
23
    else {
454
23
        period = Py_MAX(gap, period);
455
23
        LOG("Needle is not periodic.\n");
456
14.1k
      windowloop:
457
14.1k
        while (window_last < haystack_end) {
458
80.9k
            for (;;) {
459
80.9k
                LOG_LINEUP();
460
80.9k
                Py_ssize_t shift = table[(*window_last) & TABLE_MASK];
461
80.9k
                window_last += shift;
462
80.9k
                if (shift == 0) {
463
14.1k
                    break;
464
14.1k
                }
465
66.8k
                if (window_last >= haystack_end) {
466
18
                    return -1;
467
18
                }
468
66.7k
                LOG("Horspool skip\n");
469
66.7k
            }
470
14.1k
            window = window_last - len_needle + 1;
471
14.1k
            assert((window[len_needle - 1] & TABLE_MASK) ==
472
14.1k
                   (needle[len_needle - 1] & TABLE_MASK));
473
14.1k
            Py_ssize_t i = cut;
474
14.4k
            for (; i < len_needle; i++) {
475
14.2k
                if (needle[i] != window[i]) {
476
13.9k
                    if (i < gap_jump_end) {
477
13.9k
                        LOG("Early right half mismatch: jump by gap.\n");
478
13.9k
                        assert(gap >= i - cut + 1);
479
13.9k
                        window_last += gap;
480
13.9k
                    }
481
0
                    else {
482
0
                        LOG("Late right half mismatch: jump by n (>gap)\n");
483
0
                        assert(i - cut + 1 > gap);
484
0
                        window_last += i - cut + 1;
485
0
                    }
486
13.9k
                    goto windowloop;
487
13.9k
                }
488
14.2k
            }
489
230
            for (Py_ssize_t i = 0; i < cut; i++) {
490
228
                if (needle[i] != window[i]) {
491
132
                    LOG("Left half does not match.\n");
492
132
                    window_last += period;
493
132
                    goto windowloop;
494
132
                }
495
228
            }
496
2
            LOG("Found a match!\n");
497
2
            return window - haystack;
498
134
        }
499
14.1k
    }
500
3
    LOG("Not found. Returning -1.\n");
501
3
    return -1;
502
23
}
Unexecuted instantiation: bytesobject.c:stringlib__two_way
Unexecuted instantiation: unicodeobject.c:asciilib__two_way
unicodeobject.c:ucs1lib__two_way
Line
Count
Source
372
23
{
373
    // Crochemore and Perrin's (1991) Two-Way algorithm.
374
    // See http://www-igm.univ-mlv.fr/~lecroq/string/node26.html#SECTION00260
375
23
    const Py_ssize_t len_needle = p->len_needle;
376
23
    const Py_ssize_t cut = p->cut;
377
23
    Py_ssize_t period = p->period;
378
23
    const STRINGLIB_CHAR *const needle = p->needle;
379
23
    const STRINGLIB_CHAR *window_last = haystack + len_needle - 1;
380
23
    const STRINGLIB_CHAR *const haystack_end = haystack + len_haystack;
381
23
    SHIFT_TYPE *table = p->table;
382
23
    const STRINGLIB_CHAR *window;
383
23
    LOG("===== Two-way: \"%s\" in \"%s\". =====\n", needle, haystack);
384
385
23
    Py_ssize_t gap = p->gap;
386
23
    Py_ssize_t gap_jump_end = Py_MIN(len_needle, cut + gap);
387
23
    if (p->is_periodic) {
388
0
        LOG("Needle is periodic.\n");
389
0
        Py_ssize_t memory = 0;
390
0
      periodicwindowloop:
391
0
        while (window_last < haystack_end) {
392
0
            assert(memory == 0);
393
0
            for (;;) {
394
0
                LOG_LINEUP();
395
0
                Py_ssize_t shift = table[(*window_last) & TABLE_MASK];
396
0
                window_last += shift;
397
0
                if (shift == 0) {
398
0
                    break;
399
0
                }
400
0
                if (window_last >= haystack_end) {
401
0
                    return -1;
402
0
                }
403
0
                LOG("Horspool skip\n");
404
0
            }
405
0
          no_shift:
406
0
            window = window_last - len_needle + 1;
407
0
            assert((window[len_needle - 1] & TABLE_MASK) ==
408
0
                   (needle[len_needle - 1] & TABLE_MASK));
409
0
            Py_ssize_t i = Py_MAX(cut, memory);
410
0
            for (; i < len_needle; i++) {
411
0
                if (needle[i] != window[i]) {
412
0
                    if (i < gap_jump_end) {
413
0
                        LOG("Early right half mismatch: jump by gap.\n");
414
0
                        assert(gap >= i - cut + 1);
415
0
                        window_last += gap;
416
0
                    }
417
0
                    else {
418
0
                        LOG("Late right half mismatch: jump by n (>gap)\n");
419
0
                        assert(i - cut + 1 > gap);
420
0
                        window_last += i - cut + 1;
421
0
                    }
422
0
                    memory = 0;
423
0
                    goto periodicwindowloop;
424
0
                }
425
0
            }
426
0
            for (i = memory; i < cut; i++) {
427
0
                if (needle[i] != window[i]) {
428
0
                    LOG("Left half does not match.\n");
429
0
                    window_last += period;
430
0
                    memory = len_needle - period;
431
0
                    if (window_last >= haystack_end) {
432
0
                        return -1;
433
0
                    }
434
0
                    Py_ssize_t shift = table[(*window_last) & TABLE_MASK];
435
0
                    if (shift) {
436
                        // A mismatch has been identified to the right
437
                        // of where i will next start, so we can jump
438
                        // at least as far as if the mismatch occurred
439
                        // on the first comparison.
440
0
                        Py_ssize_t mem_jump = Py_MAX(cut, memory) - cut + 1;
441
0
                        LOG("Skip with Memory.\n");
442
0
                        memory = 0;
443
0
                        window_last += Py_MAX(shift, mem_jump);
444
0
                        goto periodicwindowloop;
445
0
                    }
446
0
                    goto no_shift;
447
0
                }
448
0
            }
449
0
            LOG("Found a match!\n");
450
0
            return window - haystack;
451
0
        }
452
0
    }
453
23
    else {
454
23
        period = Py_MAX(gap, period);
455
23
        LOG("Needle is not periodic.\n");
456
14.1k
      windowloop:
457
14.1k
        while (window_last < haystack_end) {
458
80.9k
            for (;;) {
459
80.9k
                LOG_LINEUP();
460
80.9k
                Py_ssize_t shift = table[(*window_last) & TABLE_MASK];
461
80.9k
                window_last += shift;
462
80.9k
                if (shift == 0) {
463
14.1k
                    break;
464
14.1k
                }
465
66.8k
                if (window_last >= haystack_end) {
466
18
                    return -1;
467
18
                }
468
66.7k
                LOG("Horspool skip\n");
469
66.7k
            }
470
14.1k
            window = window_last - len_needle + 1;
471
14.1k
            assert((window[len_needle - 1] & TABLE_MASK) ==
472
14.1k
                   (needle[len_needle - 1] & TABLE_MASK));
473
14.1k
            Py_ssize_t i = cut;
474
14.4k
            for (; i < len_needle; i++) {
475
14.2k
                if (needle[i] != window[i]) {
476
13.9k
                    if (i < gap_jump_end) {
477
13.9k
                        LOG("Early right half mismatch: jump by gap.\n");
478
13.9k
                        assert(gap >= i - cut + 1);
479
13.9k
                        window_last += gap;
480
13.9k
                    }
481
0
                    else {
482
0
                        LOG("Late right half mismatch: jump by n (>gap)\n");
483
0
                        assert(i - cut + 1 > gap);
484
0
                        window_last += i - cut + 1;
485
0
                    }
486
13.9k
                    goto windowloop;
487
13.9k
                }
488
14.2k
            }
489
230
            for (Py_ssize_t i = 0; i < cut; i++) {
490
228
                if (needle[i] != window[i]) {
491
132
                    LOG("Left half does not match.\n");
492
132
                    window_last += period;
493
132
                    goto windowloop;
494
132
                }
495
228
            }
496
2
            LOG("Found a match!\n");
497
2
            return window - haystack;
498
134
        }
499
14.1k
    }
500
3
    LOG("Not found. Returning -1.\n");
501
3
    return -1;
502
23
}
Unexecuted instantiation: unicodeobject.c:ucs2lib__two_way
Unexecuted instantiation: unicodeobject.c:ucs4lib__two_way
Unexecuted instantiation: bytes_methods.c:stringlib__two_way
Unexecuted instantiation: bytearrayobject.c:stringlib__two_way
503
504
505
static Py_ssize_t
506
STRINGLIB(_two_way_find)(const STRINGLIB_CHAR *haystack,
507
                         Py_ssize_t len_haystack,
508
                         const STRINGLIB_CHAR *needle,
509
                         Py_ssize_t len_needle)
510
23
{
511
23
    LOG("###### Finding \"%s\" in \"%s\".\n", needle, haystack);
512
23
    STRINGLIB(prework) p;
513
23
    STRINGLIB(_preprocess)(needle, len_needle, &p);
514
23
    return STRINGLIB(_two_way)(haystack, len_haystack, &p);
515
23
}
Unexecuted instantiation: bytesobject.c:stringlib__two_way_find
Unexecuted instantiation: unicodeobject.c:asciilib__two_way_find
unicodeobject.c:ucs1lib__two_way_find
Line
Count
Source
510
23
{
511
23
    LOG("###### Finding \"%s\" in \"%s\".\n", needle, haystack);
512
23
    STRINGLIB(prework) p;
513
23
    STRINGLIB(_preprocess)(needle, len_needle, &p);
514
23
    return STRINGLIB(_two_way)(haystack, len_haystack, &p);
515
23
}
Unexecuted instantiation: unicodeobject.c:ucs2lib__two_way_find
Unexecuted instantiation: unicodeobject.c:ucs4lib__two_way_find
Unexecuted instantiation: bytes_methods.c:stringlib__two_way_find
Unexecuted instantiation: bytearrayobject.c:stringlib__two_way_find
516
517
518
static Py_ssize_t
519
STRINGLIB(_two_way_count)(const STRINGLIB_CHAR *haystack,
520
                          Py_ssize_t len_haystack,
521
                          const STRINGLIB_CHAR *needle,
522
                          Py_ssize_t len_needle,
523
                          Py_ssize_t maxcount)
524
0
{
525
0
    LOG("###### Counting \"%s\" in \"%s\".\n", needle, haystack);
526
0
    STRINGLIB(prework) p;
527
0
    STRINGLIB(_preprocess)(needle, len_needle, &p);
528
0
    Py_ssize_t index = 0, count = 0;
529
0
    while (1) {
530
0
        Py_ssize_t result;
531
0
        result = STRINGLIB(_two_way)(haystack + index,
532
0
                                     len_haystack - index, &p);
533
0
        if (result == -1) {
534
0
            return count;
535
0
        }
536
0
        count++;
537
0
        if (count == maxcount) {
538
0
            return maxcount;
539
0
        }
540
0
        index += result + len_needle;
541
0
    }
542
0
    return count;
543
0
}
Unexecuted instantiation: bytesobject.c:stringlib__two_way_count
Unexecuted instantiation: unicodeobject.c:asciilib__two_way_count
Unexecuted instantiation: unicodeobject.c:ucs1lib__two_way_count
Unexecuted instantiation: unicodeobject.c:ucs2lib__two_way_count
Unexecuted instantiation: unicodeobject.c:ucs4lib__two_way_count
Unexecuted instantiation: bytes_methods.c:stringlib__two_way_count
Unexecuted instantiation: bytearrayobject.c:stringlib__two_way_count
544
545
#undef SHIFT_TYPE
546
#undef NOT_FOUND
547
#undef SHIFT_OVERFLOW
548
#undef TABLE_SIZE_BITS
549
#undef TABLE_SIZE
550
#undef TABLE_MASK
551
552
#undef LOG
553
#undef LOG_STRING
554
#undef LOG_LINEUP
555
556
static inline Py_ssize_t
557
STRINGLIB(default_find)(const STRINGLIB_CHAR* s, Py_ssize_t n,
558
                        const STRINGLIB_CHAR* p, Py_ssize_t m,
559
                        Py_ssize_t maxcount, int mode)
560
31.5M
{
561
31.5M
    const Py_ssize_t w = n - m;
562
31.5M
    Py_ssize_t mlast = m - 1, count = 0;
563
31.5M
    Py_ssize_t gap = mlast;
564
31.5M
    const STRINGLIB_CHAR last = p[mlast];
565
31.5M
    const STRINGLIB_CHAR *const ss = &s[mlast];
566
567
31.5M
    unsigned long mask = 0;
568
166M
    for (Py_ssize_t i = 0; i < mlast; i++) {
569
134M
        STRINGLIB_BLOOM_ADD(mask, p[i]);
570
134M
        if (p[i] == last) {
571
1.04M
            gap = mlast - i - 1;
572
1.04M
        }
573
134M
    }
574
31.5M
    STRINGLIB_BLOOM_ADD(mask, last);
575
576
810M
    for (Py_ssize_t i = 0; i <= w; i++) {
577
790M
        if (ss[i] == last) {
578
            /* candidate match */
579
42.8M
            Py_ssize_t j;
580
65.2M
            for (j = 0; j < mlast; j++) {
581
43.8M
                if (s[i+j] != p[j]) {
582
21.4M
                    break;
583
21.4M
                }
584
43.8M
            }
585
42.8M
            if (j == mlast) {
586
                /* got a match! */
587
21.4M
                if (mode != FAST_COUNT) {
588
11.1M
                    return i;
589
11.1M
                }
590
10.3M
                count++;
591
10.3M
                if (count == maxcount) {
592
0
                    return maxcount;
593
0
                }
594
10.3M
                i = i + mlast;
595
10.3M
                continue;
596
10.3M
            }
597
            /* miss: check if next character is part of pattern */
598
21.4M
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
599
4.62M
                i = i + m;
600
4.62M
            }
601
16.8M
            else {
602
16.8M
                i = i + gap;
603
16.8M
            }
604
21.4M
        }
605
747M
        else {
606
            /* skip: check if next character is part of pattern */
607
747M
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
608
644M
                i = i + m;
609
644M
            }
610
747M
        }
611
790M
    }
612
20.4M
    return mode == FAST_COUNT ? count : -1;
613
31.5M
}
Unexecuted instantiation: bytesobject.c:stringlib_default_find
unicodeobject.c:asciilib_default_find
Line
Count
Source
560
2.06M
{
561
2.06M
    const Py_ssize_t w = n - m;
562
2.06M
    Py_ssize_t mlast = m - 1, count = 0;
563
2.06M
    Py_ssize_t gap = mlast;
564
2.06M
    const STRINGLIB_CHAR last = p[mlast];
565
2.06M
    const STRINGLIB_CHAR *const ss = &s[mlast];
566
567
2.06M
    unsigned long mask = 0;
568
4.36M
    for (Py_ssize_t i = 0; i < mlast; i++) {
569
2.30M
        STRINGLIB_BLOOM_ADD(mask, p[i]);
570
2.30M
        if (p[i] == last) {
571
20.2k
            gap = mlast - i - 1;
572
20.2k
        }
573
2.30M
    }
574
2.06M
    STRINGLIB_BLOOM_ADD(mask, last);
575
576
39.6M
    for (Py_ssize_t i = 0; i <= w; i++) {
577
39.5M
        if (ss[i] == last) {
578
            /* candidate match */
579
4.19M
            Py_ssize_t j;
580
6.41M
            for (j = 0; j < mlast; j++) {
581
4.38M
                if (s[i+j] != p[j]) {
582
2.16M
                    break;
583
2.16M
                }
584
4.38M
            }
585
4.19M
            if (j == mlast) {
586
                /* got a match! */
587
2.02M
                if (mode != FAST_COUNT) {
588
2.02M
                    return i;
589
2.02M
                }
590
0
                count++;
591
0
                if (count == maxcount) {
592
0
                    return maxcount;
593
0
                }
594
0
                i = i + mlast;
595
0
                continue;
596
0
            }
597
            /* miss: check if next character is part of pattern */
598
2.16M
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
599
78.3k
                i = i + m;
600
78.3k
            }
601
2.08M
            else {
602
2.08M
                i = i + gap;
603
2.08M
            }
604
2.16M
        }
605
35.3M
        else {
606
            /* skip: check if next character is part of pattern */
607
35.3M
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
608
31.2M
                i = i + m;
609
31.2M
            }
610
35.3M
        }
611
39.5M
    }
612
41.2k
    return mode == FAST_COUNT ? count : -1;
613
2.06M
}
unicodeobject.c:ucs1lib_default_find
Line
Count
Source
560
22.0M
{
561
22.0M
    const Py_ssize_t w = n - m;
562
22.0M
    Py_ssize_t mlast = m - 1, count = 0;
563
22.0M
    Py_ssize_t gap = mlast;
564
22.0M
    const STRINGLIB_CHAR last = p[mlast];
565
22.0M
    const STRINGLIB_CHAR *const ss = &s[mlast];
566
567
22.0M
    unsigned long mask = 0;
568
146M
    for (Py_ssize_t i = 0; i < mlast; i++) {
569
124M
        STRINGLIB_BLOOM_ADD(mask, p[i]);
570
124M
        if (p[i] == last) {
571
966k
            gap = mlast - i - 1;
572
966k
        }
573
124M
    }
574
22.0M
    STRINGLIB_BLOOM_ADD(mask, last);
575
576
356M
    for (Py_ssize_t i = 0; i <= w; i++) {
577
335M
        if (ss[i] == last) {
578
            /* candidate match */
579
15.3M
            Py_ssize_t j;
580
20.7M
            for (j = 0; j < mlast; j++) {
581
15.7M
                if (s[i+j] != p[j]) {
582
10.3M
                    break;
583
10.3M
                }
584
15.7M
            }
585
15.3M
            if (j == mlast) {
586
                /* got a match! */
587
5.01M
                if (mode != FAST_COUNT) {
588
1.72M
                    return i;
589
1.72M
                }
590
3.28M
                count++;
591
3.28M
                if (count == maxcount) {
592
0
                    return maxcount;
593
0
                }
594
3.28M
                i = i + mlast;
595
3.28M
                continue;
596
3.28M
            }
597
            /* miss: check if next character is part of pattern */
598
10.3M
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
599
1.94M
                i = i + m;
600
1.94M
            }
601
8.35M
            else {
602
8.35M
                i = i + gap;
603
8.35M
            }
604
10.3M
        }
605
320M
        else {
606
            /* skip: check if next character is part of pattern */
607
320M
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
608
235M
                i = i + m;
609
235M
            }
610
320M
        }
611
335M
    }
612
20.3M
    return mode == FAST_COUNT ? count : -1;
613
22.0M
}
unicodeobject.c:ucs2lib_default_find
Line
Count
Source
560
3.33M
{
561
3.33M
    const Py_ssize_t w = n - m;
562
3.33M
    Py_ssize_t mlast = m - 1, count = 0;
563
3.33M
    Py_ssize_t gap = mlast;
564
3.33M
    const STRINGLIB_CHAR last = p[mlast];
565
3.33M
    const STRINGLIB_CHAR *const ss = &s[mlast];
566
567
3.33M
    unsigned long mask = 0;
568
6.87M
    for (Py_ssize_t i = 0; i < mlast; i++) {
569
3.54M
        STRINGLIB_BLOOM_ADD(mask, p[i]);
570
3.54M
        if (p[i] == last) {
571
38.9k
            gap = mlast - i - 1;
572
38.9k
        }
573
3.54M
    }
574
3.33M
    STRINGLIB_BLOOM_ADD(mask, last);
575
576
244M
    for (Py_ssize_t i = 0; i <= w; i++) {
577
244M
        if (ss[i] == last) {
578
            /* candidate match */
579
10.3M
            Py_ssize_t j;
580
16.7M
            for (j = 0; j < mlast; j++) {
581
10.5M
                if (s[i+j] != p[j]) {
582
4.13M
                    break;
583
4.13M
                }
584
10.5M
            }
585
10.3M
            if (j == mlast) {
586
                /* got a match! */
587
6.21M
                if (mode != FAST_COUNT) {
588
3.23M
                    return i;
589
3.23M
                }
590
2.97M
                count++;
591
2.97M
                if (count == maxcount) {
592
0
                    return maxcount;
593
0
                }
594
2.97M
                i = i + mlast;
595
2.97M
                continue;
596
2.97M
            }
597
            /* miss: check if next character is part of pattern */
598
4.13M
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
599
1.00M
                i = i + m;
600
1.00M
            }
601
3.12M
            else {
602
3.12M
                i = i + gap;
603
3.12M
            }
604
4.13M
        }
605
234M
        else {
606
            /* skip: check if next character is part of pattern */
607
234M
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
608
228M
                i = i + m;
609
228M
            }
610
234M
        }
611
244M
    }
612
91.5k
    return mode == FAST_COUNT ? count : -1;
613
3.33M
}
unicodeobject.c:ucs4lib_default_find
Line
Count
Source
560
4.16M
{
561
4.16M
    const Py_ssize_t w = n - m;
562
4.16M
    Py_ssize_t mlast = m - 1, count = 0;
563
4.16M
    Py_ssize_t gap = mlast;
564
4.16M
    const STRINGLIB_CHAR last = p[mlast];
565
4.16M
    const STRINGLIB_CHAR *const ss = &s[mlast];
566
567
4.16M
    unsigned long mask = 0;
568
8.40M
    for (Py_ssize_t i = 0; i < mlast; i++) {
569
4.24M
        STRINGLIB_BLOOM_ADD(mask, p[i]);
570
4.24M
        if (p[i] == last) {
571
20.5k
            gap = mlast - i - 1;
572
20.5k
        }
573
4.24M
    }
574
4.16M
    STRINGLIB_BLOOM_ADD(mask, last);
575
576
168M
    for (Py_ssize_t i = 0; i <= w; i++) {
577
168M
        if (ss[i] == last) {
578
            /* candidate match */
579
13.0M
            Py_ssize_t j;
580
21.2M
            for (j = 0; j < mlast; j++) {
581
13.0M
                if (s[i+j] != p[j]) {
582
4.83M
                    break;
583
4.83M
                }
584
13.0M
            }
585
13.0M
            if (j == mlast) {
586
                /* got a match! */
587
8.17M
                if (mode != FAST_COUNT) {
588
4.13M
                    return i;
589
4.13M
                }
590
4.04M
                count++;
591
4.04M
                if (count == maxcount) {
592
0
                    return maxcount;
593
0
                }
594
4.04M
                i = i + mlast;
595
4.04M
                continue;
596
4.04M
            }
597
            /* miss: check if next character is part of pattern */
598
4.83M
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
599
1.59M
                i = i + m;
600
1.59M
            }
601
3.24M
            else {
602
3.24M
                i = i + gap;
603
3.24M
            }
604
4.83M
        }
605
155M
        else {
606
            /* skip: check if next character is part of pattern */
607
155M
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
608
150M
                i = i + m;
609
150M
            }
610
155M
        }
611
168M
    }
612
28.7k
    return mode == FAST_COUNT ? count : -1;
613
4.16M
}
bytes_methods.c:stringlib_default_find
Line
Count
Source
560
2.57k
{
561
2.57k
    const Py_ssize_t w = n - m;
562
2.57k
    Py_ssize_t mlast = m - 1, count = 0;
563
2.57k
    Py_ssize_t gap = mlast;
564
2.57k
    const STRINGLIB_CHAR last = p[mlast];
565
2.57k
    const STRINGLIB_CHAR *const ss = &s[mlast];
566
567
2.57k
    unsigned long mask = 0;
568
10.3k
    for (Py_ssize_t i = 0; i < mlast; i++) {
569
7.73k
        STRINGLIB_BLOOM_ADD(mask, p[i]);
570
7.73k
        if (p[i] == last) {
571
2.57k
            gap = mlast - i - 1;
572
2.57k
        }
573
7.73k
    }
574
2.57k
    STRINGLIB_BLOOM_ADD(mask, last);
575
576
1.19M
    for (Py_ssize_t i = 0; i <= w; i++) {
577
1.19M
        if (ss[i] == last) {
578
            /* candidate match */
579
7.75k
            Py_ssize_t j;
580
14.7k
            for (j = 0; j < mlast; j++) {
581
12.5k
                if (s[i+j] != p[j]) {
582
5.46k
                    break;
583
5.46k
                }
584
12.5k
            }
585
7.75k
            if (j == mlast) {
586
                /* got a match! */
587
2.28k
                if (mode != FAST_COUNT) {
588
2.28k
                    return i;
589
2.28k
                }
590
0
                count++;
591
0
                if (count == maxcount) {
592
0
                    return maxcount;
593
0
                }
594
0
                i = i + mlast;
595
0
                continue;
596
0
            }
597
            /* miss: check if next character is part of pattern */
598
5.46k
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
599
912
                i = i + m;
600
912
            }
601
4.55k
            else {
602
4.55k
                i = i + gap;
603
4.55k
            }
604
5.46k
        }
605
1.19M
        else {
606
            /* skip: check if next character is part of pattern */
607
1.19M
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
608
106k
                i = i + m;
609
106k
            }
610
1.19M
        }
611
1.19M
    }
612
290
    return mode == FAST_COUNT ? count : -1;
613
2.57k
}
Unexecuted instantiation: bytearrayobject.c:stringlib_default_find
614
615
616
static Py_ssize_t
617
STRINGLIB(adaptive_find)(const STRINGLIB_CHAR* s, Py_ssize_t n,
618
                         const STRINGLIB_CHAR* p, Py_ssize_t m,
619
                         Py_ssize_t maxcount, int mode)
620
0
{
621
0
    const Py_ssize_t w = n - m;
622
0
    Py_ssize_t mlast = m - 1, count = 0;
623
0
    Py_ssize_t gap = mlast;
624
0
    Py_ssize_t hits = 0, res;
625
0
    const STRINGLIB_CHAR last = p[mlast];
626
0
    const STRINGLIB_CHAR *const ss = &s[mlast];
627
628
0
    unsigned long mask = 0;
629
0
    for (Py_ssize_t i = 0; i < mlast; i++) {
630
0
        STRINGLIB_BLOOM_ADD(mask, p[i]);
631
0
        if (p[i] == last) {
632
0
            gap = mlast - i - 1;
633
0
        }
634
0
    }
635
0
    STRINGLIB_BLOOM_ADD(mask, last);
636
637
0
    for (Py_ssize_t i = 0; i <= w; i++) {
638
0
        if (ss[i] == last) {
639
            /* candidate match */
640
0
            Py_ssize_t j;
641
0
            for (j = 0; j < mlast; j++) {
642
0
                if (s[i+j] != p[j]) {
643
0
                    break;
644
0
                }
645
0
            }
646
0
            if (j == mlast) {
647
                /* got a match! */
648
0
                if (mode != FAST_COUNT) {
649
0
                    return i;
650
0
                }
651
0
                count++;
652
0
                if (count == maxcount) {
653
0
                    return maxcount;
654
0
                }
655
0
                i = i + mlast;
656
0
                continue;
657
0
            }
658
0
            hits += j + 1;
659
0
            if (hits > m / 4 && w - i > 2000) {
660
0
                if (mode == FAST_SEARCH) {
661
0
                    res = STRINGLIB(_two_way_find)(s + i, n - i, p, m);
662
0
                    return res == -1 ? -1 : res + i;
663
0
                }
664
0
                else {
665
0
                    res = STRINGLIB(_two_way_count)(s + i, n - i, p, m,
666
0
                                                    maxcount - count);
667
0
                    return res + count;
668
0
                }
669
0
            }
670
            /* miss: check if next character is part of pattern */
671
0
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
672
0
                i = i + m;
673
0
            }
674
0
            else {
675
0
                i = i + gap;
676
0
            }
677
0
        }
678
0
        else {
679
            /* skip: check if next character is part of pattern */
680
0
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
681
0
                i = i + m;
682
0
            }
683
0
        }
684
0
    }
685
0
    return mode == FAST_COUNT ? count : -1;
686
0
}
Unexecuted instantiation: bytesobject.c:stringlib_adaptive_find
Unexecuted instantiation: unicodeobject.c:asciilib_adaptive_find
Unexecuted instantiation: unicodeobject.c:ucs1lib_adaptive_find
Unexecuted instantiation: unicodeobject.c:ucs2lib_adaptive_find
Unexecuted instantiation: unicodeobject.c:ucs4lib_adaptive_find
Unexecuted instantiation: bytes_methods.c:stringlib_adaptive_find
Unexecuted instantiation: bytearrayobject.c:stringlib_adaptive_find
687
688
689
static Py_ssize_t
690
STRINGLIB(default_rfind)(const STRINGLIB_CHAR* s, Py_ssize_t n,
691
                         const STRINGLIB_CHAR* p, Py_ssize_t m,
692
                         Py_ssize_t maxcount, int mode)
693
6.78k
{
694
    /* create compressed boyer-moore delta 1 table */
695
6.78k
    unsigned long mask = 0;
696
6.78k
    Py_ssize_t i, j, mlast = m - 1, skip = m - 1, w = n - m;
697
698
    /* process pattern[0] outside the loop */
699
6.78k
    STRINGLIB_BLOOM_ADD(mask, p[0]);
700
    /* process pattern[:0:-1] */
701
27.1k
    for (i = mlast; i > 0; i--) {
702
20.3k
        STRINGLIB_BLOOM_ADD(mask, p[i]);
703
20.3k
        if (p[i] == p[0]) {
704
0
            skip = i - 1;
705
0
        }
706
20.3k
    }
707
708
1.48M
    for (i = w; i >= 0; i--) {
709
1.48M
        if (s[i] == p[0]) {
710
            /* candidate match */
711
82.2k
            for (j = mlast; j > 0; j--) {
712
75.5k
                if (s[i+j] != p[j]) {
713
54.9k
                    break;
714
54.9k
                }
715
75.5k
            }
716
61.5k
            if (j == 0) {
717
                /* got a match! */
718
6.65k
                return i;
719
6.65k
            }
720
            /* miss: check if previous character is part of pattern */
721
54.9k
            if (i > 0 && !STRINGLIB_BLOOM(mask, s[i-1])) {
722
53.0k
                i = i - m;
723
53.0k
            }
724
1.82k
            else {
725
1.82k
                i = i - skip;
726
1.82k
            }
727
54.9k
        }
728
1.42M
        else {
729
            /* skip: check if previous character is part of pattern */
730
1.42M
            if (i > 0 && !STRINGLIB_BLOOM(mask, s[i-1])) {
731
1.30M
                i = i - m;
732
1.30M
            }
733
1.42M
        }
734
1.48M
    }
735
125
    return -1;
736
6.78k
}
Unexecuted instantiation: bytesobject.c:stringlib_default_rfind
Unexecuted instantiation: unicodeobject.c:asciilib_default_rfind
Unexecuted instantiation: unicodeobject.c:ucs1lib_default_rfind
Unexecuted instantiation: unicodeobject.c:ucs2lib_default_rfind
Unexecuted instantiation: unicodeobject.c:ucs4lib_default_rfind
bytes_methods.c:stringlib_default_rfind
Line
Count
Source
693
6.78k
{
694
    /* create compressed boyer-moore delta 1 table */
695
6.78k
    unsigned long mask = 0;
696
6.78k
    Py_ssize_t i, j, mlast = m - 1, skip = m - 1, w = n - m;
697
698
    /* process pattern[0] outside the loop */
699
6.78k
    STRINGLIB_BLOOM_ADD(mask, p[0]);
700
    /* process pattern[:0:-1] */
701
27.1k
    for (i = mlast; i > 0; i--) {
702
20.3k
        STRINGLIB_BLOOM_ADD(mask, p[i]);
703
20.3k
        if (p[i] == p[0]) {
704
0
            skip = i - 1;
705
0
        }
706
20.3k
    }
707
708
1.48M
    for (i = w; i >= 0; i--) {
709
1.48M
        if (s[i] == p[0]) {
710
            /* candidate match */
711
82.2k
            for (j = mlast; j > 0; j--) {
712
75.5k
                if (s[i+j] != p[j]) {
713
54.9k
                    break;
714
54.9k
                }
715
75.5k
            }
716
61.5k
            if (j == 0) {
717
                /* got a match! */
718
6.65k
                return i;
719
6.65k
            }
720
            /* miss: check if previous character is part of pattern */
721
54.9k
            if (i > 0 && !STRINGLIB_BLOOM(mask, s[i-1])) {
722
53.0k
                i = i - m;
723
53.0k
            }
724
1.82k
            else {
725
1.82k
                i = i - skip;
726
1.82k
            }
727
54.9k
        }
728
1.42M
        else {
729
            /* skip: check if previous character is part of pattern */
730
1.42M
            if (i > 0 && !STRINGLIB_BLOOM(mask, s[i-1])) {
731
1.30M
                i = i - m;
732
1.30M
            }
733
1.42M
        }
734
1.48M
    }
735
125
    return -1;
736
6.78k
}
Unexecuted instantiation: bytearrayobject.c:stringlib_default_rfind
737
738
739
static inline Py_ssize_t
740
STRINGLIB(count_char)(const STRINGLIB_CHAR *s, Py_ssize_t n,
741
                      const STRINGLIB_CHAR p0, Py_ssize_t maxcount)
742
0
{
743
0
    Py_ssize_t i, count = 0;
744
0
    for (i = 0; i < n; i++) {
745
0
        if (s[i] == p0) {
746
0
            count++;
747
0
            if (count == maxcount) {
748
0
                return maxcount;
749
0
            }
750
0
        }
751
0
    }
752
0
    return count;
753
0
}
Unexecuted instantiation: bytesobject.c:stringlib_count_char
Unexecuted instantiation: unicodeobject.c:asciilib_count_char
Unexecuted instantiation: unicodeobject.c:ucs1lib_count_char
Unexecuted instantiation: unicodeobject.c:ucs2lib_count_char
Unexecuted instantiation: unicodeobject.c:ucs4lib_count_char
Unexecuted instantiation: bytes_methods.c:stringlib_count_char
Unexecuted instantiation: bytearrayobject.c:stringlib_count_char
754
755
756
static inline Py_ssize_t
757
STRINGLIB(count_char_no_maxcount)(const STRINGLIB_CHAR *s, Py_ssize_t n,
758
                                  const STRINGLIB_CHAR p0)
759
/* A specialized function of count_char that does not cut off at a maximum.
760
   As a result, the compiler is able to vectorize the loop. */
761
31.3M
{
762
31.3M
    Py_ssize_t count = 0;
763
4.31G
    for (Py_ssize_t i = 0; i < n; i++) {
764
4.28G
        if (s[i] == p0) {
765
683M
            count++;
766
683M
        }
767
4.28G
    }
768
31.3M
    return count;
769
31.3M
}
Unexecuted instantiation: bytesobject.c:stringlib_count_char_no_maxcount
Unexecuted instantiation: unicodeobject.c:asciilib_count_char_no_maxcount
unicodeobject.c:ucs1lib_count_char_no_maxcount
Line
Count
Source
761
20.4M
{
762
20.4M
    Py_ssize_t count = 0;
763
588M
    for (Py_ssize_t i = 0; i < n; i++) {
764
567M
        if (s[i] == p0) {
765
34.3M
            count++;
766
34.3M
        }
767
567M
    }
768
20.4M
    return count;
769
20.4M
}
unicodeobject.c:ucs2lib_count_char_no_maxcount
Line
Count
Source
761
4.08M
{
762
4.08M
    Py_ssize_t count = 0;
763
562M
    for (Py_ssize_t i = 0; i < n; i++) {
764
558M
        if (s[i] == p0) {
765
17.8M
            count++;
766
17.8M
        }
767
558M
    }
768
4.08M
    return count;
769
4.08M
}
unicodeobject.c:ucs4lib_count_char_no_maxcount
Line
Count
Source
761
1.36M
{
762
1.36M
    Py_ssize_t count = 0;
763
376M
    for (Py_ssize_t i = 0; i < n; i++) {
764
375M
        if (s[i] == p0) {
765
12.4M
            count++;
766
12.4M
        }
767
375M
    }
768
1.36M
    return count;
769
1.36M
}
bytes_methods.c:stringlib_count_char_no_maxcount
Line
Count
Source
761
5.42M
{
762
5.42M
    Py_ssize_t count = 0;
763
2.78G
    for (Py_ssize_t i = 0; i < n; i++) {
764
2.77G
        if (s[i] == p0) {
765
618M
            count++;
766
618M
        }
767
2.77G
    }
768
5.42M
    return count;
769
5.42M
}
Unexecuted instantiation: bytearrayobject.c:stringlib_count_char_no_maxcount
770
771
772
Py_LOCAL_INLINE(Py_ssize_t)
773
FASTSEARCH(const STRINGLIB_CHAR* s, Py_ssize_t n,
774
           const STRINGLIB_CHAR* p, Py_ssize_t m,
775
           Py_ssize_t maxcount, int mode)
776
82.9M
{
777
82.9M
    if (n < m || (mode == FAST_COUNT && maxcount == 0)) {
778
561
        return -1;
779
561
    }
780
781
    /* look for special cases */
782
82.9M
    if (m <= 1) {
783
51.3M
        if (m <= 0) {
784
0
            return -1;
785
0
        }
786
        /* use special case for 1-character strings */
787
51.3M
        if (mode == FAST_SEARCH)
788
20.0M
            return STRINGLIB(find_char)(s, n, p[0]);
789
31.3M
        else if (mode == FAST_RSEARCH)
790
51.9k
            return STRINGLIB(rfind_char)(s, n, p[0]);
791
31.3M
        else {
792
31.3M
            if (maxcount == PY_SSIZE_T_MAX) {
793
31.3M
                return STRINGLIB(count_char_no_maxcount)(s, n, p[0]);
794
31.3M
            }
795
0
            return STRINGLIB(count_char)(s, n, p[0], maxcount);
796
31.3M
        }
797
51.3M
    }
798
799
31.6M
    if (mode != FAST_RSEARCH) {
800
31.5M
        if (n < 2500 || (m < 100 && n < 30000) || m < 6) {
801
31.5M
            return STRINGLIB(default_find)(s, n, p, m, maxcount, mode);
802
31.5M
        }
803
23
        else if ((m >> 2) * 3 < (n >> 2)) {
804
            /* 33% threshold, but don't overflow. */
805
            /* For larger problems where the needle isn't a huge
806
               percentage of the size of the haystack, the relatively
807
               expensive O(m) startup cost of the two-way algorithm
808
               will surely pay off. */
809
23
            if (mode == FAST_SEARCH) {
810
23
                return STRINGLIB(_two_way_find)(s, n, p, m);
811
23
            }
812
0
            else {
813
0
                return STRINGLIB(_two_way_count)(s, n, p, m, maxcount);
814
0
            }
815
23
        }
816
0
        else {
817
            /* To ensure that we have good worst-case behavior,
818
               here's an adaptive version of the algorithm, where if
819
               we match O(m) characters without any matches of the
820
               entire needle, then we predict that the startup cost of
821
               the two-way algorithm will probably be worth it. */
822
0
            return STRINGLIB(adaptive_find)(s, n, p, m, maxcount, mode);
823
0
        }
824
31.5M
    }
825
6.78k
    else {
826
        /* FAST_RSEARCH */
827
6.78k
        return STRINGLIB(default_rfind)(s, n, p, m, maxcount, mode);
828
6.78k
    }
829
31.6M
}
bytesobject.c:fastsearch
Line
Count
Source
776
444k
{
777
444k
    if (n < m || (mode == FAST_COUNT && maxcount == 0)) {
778
0
        return -1;
779
0
    }
780
781
    /* look for special cases */
782
444k
    if (m <= 1) {
783
444k
        if (m <= 0) {
784
0
            return -1;
785
0
        }
786
        /* use special case for 1-character strings */
787
444k
        if (mode == FAST_SEARCH)
788
444k
            return STRINGLIB(find_char)(s, n, p[0]);
789
0
        else if (mode == FAST_RSEARCH)
790
0
            return STRINGLIB(rfind_char)(s, n, p[0]);
791
0
        else {
792
0
            if (maxcount == PY_SSIZE_T_MAX) {
793
0
                return STRINGLIB(count_char_no_maxcount)(s, n, p[0]);
794
0
            }
795
0
            return STRINGLIB(count_char)(s, n, p[0], maxcount);
796
0
        }
797
444k
    }
798
799
0
    if (mode != FAST_RSEARCH) {
800
0
        if (n < 2500 || (m < 100 && n < 30000) || m < 6) {
801
0
            return STRINGLIB(default_find)(s, n, p, m, maxcount, mode);
802
0
        }
803
0
        else if ((m >> 2) * 3 < (n >> 2)) {
804
            /* 33% threshold, but don't overflow. */
805
            /* For larger problems where the needle isn't a huge
806
               percentage of the size of the haystack, the relatively
807
               expensive O(m) startup cost of the two-way algorithm
808
               will surely pay off. */
809
0
            if (mode == FAST_SEARCH) {
810
0
                return STRINGLIB(_two_way_find)(s, n, p, m);
811
0
            }
812
0
            else {
813
0
                return STRINGLIB(_two_way_count)(s, n, p, m, maxcount);
814
0
            }
815
0
        }
816
0
        else {
817
            /* To ensure that we have good worst-case behavior,
818
               here's an adaptive version of the algorithm, where if
819
               we match O(m) characters without any matches of the
820
               entire needle, then we predict that the startup cost of
821
               the two-way algorithm will probably be worth it. */
822
0
            return STRINGLIB(adaptive_find)(s, n, p, m, maxcount, mode);
823
0
        }
824
0
    }
825
0
    else {
826
        /* FAST_RSEARCH */
827
0
        return STRINGLIB(default_rfind)(s, n, p, m, maxcount, mode);
828
0
    }
829
0
}
unicodeobject.c:asciilib_fastsearch
Line
Count
Source
776
6.88M
{
777
6.88M
    if (n < m || (mode == FAST_COUNT && maxcount == 0)) {
778
0
        return -1;
779
0
    }
780
781
    /* look for special cases */
782
6.88M
    if (m <= 1) {
783
4.82M
        if (m <= 0) {
784
0
            return -1;
785
0
        }
786
        /* use special case for 1-character strings */
787
4.82M
        if (mode == FAST_SEARCH)
788
4.76M
            return STRINGLIB(find_char)(s, n, p[0]);
789
51.9k
        else if (mode == FAST_RSEARCH)
790
51.9k
            return STRINGLIB(rfind_char)(s, n, p[0]);
791
0
        else {
792
0
            if (maxcount == PY_SSIZE_T_MAX) {
793
0
                return STRINGLIB(count_char_no_maxcount)(s, n, p[0]);
794
0
            }
795
0
            return STRINGLIB(count_char)(s, n, p[0], maxcount);
796
0
        }
797
4.82M
    }
798
799
2.06M
    if (mode != FAST_RSEARCH) {
800
2.06M
        if (n < 2500 || (m < 100 && n < 30000) || m < 6) {
801
2.06M
            return STRINGLIB(default_find)(s, n, p, m, maxcount, mode);
802
2.06M
        }
803
0
        else if ((m >> 2) * 3 < (n >> 2)) {
804
            /* 33% threshold, but don't overflow. */
805
            /* For larger problems where the needle isn't a huge
806
               percentage of the size of the haystack, the relatively
807
               expensive O(m) startup cost of the two-way algorithm
808
               will surely pay off. */
809
0
            if (mode == FAST_SEARCH) {
810
0
                return STRINGLIB(_two_way_find)(s, n, p, m);
811
0
            }
812
0
            else {
813
0
                return STRINGLIB(_two_way_count)(s, n, p, m, maxcount);
814
0
            }
815
0
        }
816
0
        else {
817
            /* To ensure that we have good worst-case behavior,
818
               here's an adaptive version of the algorithm, where if
819
               we match O(m) characters without any matches of the
820
               entire needle, then we predict that the startup cost of
821
               the two-way algorithm will probably be worth it. */
822
0
            return STRINGLIB(adaptive_find)(s, n, p, m, maxcount, mode);
823
0
        }
824
2.06M
    }
825
0
    else {
826
        /* FAST_RSEARCH */
827
0
        return STRINGLIB(default_rfind)(s, n, p, m, maxcount, mode);
828
0
    }
829
2.06M
}
unicodeobject.c:ucs1lib_fastsearch
Line
Count
Source
776
50.2M
{
777
50.2M
    if (n < m || (mode == FAST_COUNT && maxcount == 0)) {
778
0
        return -1;
779
0
    }
780
781
    /* look for special cases */
782
50.2M
    if (m <= 1) {
783
28.2M
        if (m <= 0) {
784
0
            return -1;
785
0
        }
786
        /* use special case for 1-character strings */
787
28.2M
        if (mode == FAST_SEARCH)
788
7.78M
            return STRINGLIB(find_char)(s, n, p[0]);
789
20.4M
        else if (mode == FAST_RSEARCH)
790
0
            return STRINGLIB(rfind_char)(s, n, p[0]);
791
20.4M
        else {
792
20.4M
            if (maxcount == PY_SSIZE_T_MAX) {
793
20.4M
                return STRINGLIB(count_char_no_maxcount)(s, n, p[0]);
794
20.4M
            }
795
0
            return STRINGLIB(count_char)(s, n, p[0], maxcount);
796
20.4M
        }
797
28.2M
    }
798
799
22.0M
    if (mode != FAST_RSEARCH) {
800
22.0M
        if (n < 2500 || (m < 100 && n < 30000) || m < 6) {
801
22.0M
            return STRINGLIB(default_find)(s, n, p, m, maxcount, mode);
802
22.0M
        }
803
23
        else if ((m >> 2) * 3 < (n >> 2)) {
804
            /* 33% threshold, but don't overflow. */
805
            /* For larger problems where the needle isn't a huge
806
               percentage of the size of the haystack, the relatively
807
               expensive O(m) startup cost of the two-way algorithm
808
               will surely pay off. */
809
23
            if (mode == FAST_SEARCH) {
810
23
                return STRINGLIB(_two_way_find)(s, n, p, m);
811
23
            }
812
0
            else {
813
0
                return STRINGLIB(_two_way_count)(s, n, p, m, maxcount);
814
0
            }
815
23
        }
816
0
        else {
817
            /* To ensure that we have good worst-case behavior,
818
               here's an adaptive version of the algorithm, where if
819
               we match O(m) characters without any matches of the
820
               entire needle, then we predict that the startup cost of
821
               the two-way algorithm will probably be worth it. */
822
0
            return STRINGLIB(adaptive_find)(s, n, p, m, maxcount, mode);
823
0
        }
824
22.0M
    }
825
0
    else {
826
        /* FAST_RSEARCH */
827
0
        return STRINGLIB(default_rfind)(s, n, p, m, maxcount, mode);
828
0
    }
829
22.0M
}
unicodeobject.c:ucs2lib_fastsearch
Line
Count
Source
776
10.9M
{
777
10.9M
    if (n < m || (mode == FAST_COUNT && maxcount == 0)) {
778
553
        return -1;
779
553
    }
780
781
    /* look for special cases */
782
10.9M
    if (m <= 1) {
783
7.58M
        if (m <= 0) {
784
0
            return -1;
785
0
        }
786
        /* use special case for 1-character strings */
787
7.58M
        if (mode == FAST_SEARCH)
788
3.50M
            return STRINGLIB(find_char)(s, n, p[0]);
789
4.08M
        else if (mode == FAST_RSEARCH)
790
0
            return STRINGLIB(rfind_char)(s, n, p[0]);
791
4.08M
        else {
792
4.08M
            if (maxcount == PY_SSIZE_T_MAX) {
793
4.08M
                return STRINGLIB(count_char_no_maxcount)(s, n, p[0]);
794
4.08M
            }
795
0
            return STRINGLIB(count_char)(s, n, p[0], maxcount);
796
4.08M
        }
797
7.58M
    }
798
799
3.33M
    if (mode != FAST_RSEARCH) {
800
3.33M
        if (n < 2500 || (m < 100 && n < 30000) || m < 6) {
801
3.33M
            return STRINGLIB(default_find)(s, n, p, m, maxcount, mode);
802
3.33M
        }
803
0
        else if ((m >> 2) * 3 < (n >> 2)) {
804
            /* 33% threshold, but don't overflow. */
805
            /* For larger problems where the needle isn't a huge
806
               percentage of the size of the haystack, the relatively
807
               expensive O(m) startup cost of the two-way algorithm
808
               will surely pay off. */
809
0
            if (mode == FAST_SEARCH) {
810
0
                return STRINGLIB(_two_way_find)(s, n, p, m);
811
0
            }
812
0
            else {
813
0
                return STRINGLIB(_two_way_count)(s, n, p, m, maxcount);
814
0
            }
815
0
        }
816
0
        else {
817
            /* To ensure that we have good worst-case behavior,
818
               here's an adaptive version of the algorithm, where if
819
               we match O(m) characters without any matches of the
820
               entire needle, then we predict that the startup cost of
821
               the two-way algorithm will probably be worth it. */
822
0
            return STRINGLIB(adaptive_find)(s, n, p, m, maxcount, mode);
823
0
        }
824
3.33M
    }
825
0
    else {
826
        /* FAST_RSEARCH */
827
0
        return STRINGLIB(default_rfind)(s, n, p, m, maxcount, mode);
828
0
    }
829
3.33M
}
unicodeobject.c:ucs4lib_fastsearch
Line
Count
Source
776
9.06M
{
777
9.06M
    if (n < m || (mode == FAST_COUNT && maxcount == 0)) {
778
0
        return -1;
779
0
    }
780
781
    /* look for special cases */
782
9.06M
    if (m <= 1) {
783
4.90M
        if (m <= 0) {
784
0
            return -1;
785
0
        }
786
        /* use special case for 1-character strings */
787
4.90M
        if (mode == FAST_SEARCH)
788
3.53M
            return STRINGLIB(find_char)(s, n, p[0]);
789
1.36M
        else if (mode == FAST_RSEARCH)
790
0
            return STRINGLIB(rfind_char)(s, n, p[0]);
791
1.36M
        else {
792
1.36M
            if (maxcount == PY_SSIZE_T_MAX) {
793
1.36M
                return STRINGLIB(count_char_no_maxcount)(s, n, p[0]);
794
1.36M
            }
795
0
            return STRINGLIB(count_char)(s, n, p[0], maxcount);
796
1.36M
        }
797
4.90M
    }
798
799
4.16M
    if (mode != FAST_RSEARCH) {
800
4.16M
        if (n < 2500 || (m < 100 && n < 30000) || m < 6) {
801
4.16M
            return STRINGLIB(default_find)(s, n, p, m, maxcount, mode);
802
4.16M
        }
803
0
        else if ((m >> 2) * 3 < (n >> 2)) {
804
            /* 33% threshold, but don't overflow. */
805
            /* For larger problems where the needle isn't a huge
806
               percentage of the size of the haystack, the relatively
807
               expensive O(m) startup cost of the two-way algorithm
808
               will surely pay off. */
809
0
            if (mode == FAST_SEARCH) {
810
0
                return STRINGLIB(_two_way_find)(s, n, p, m);
811
0
            }
812
0
            else {
813
0
                return STRINGLIB(_two_way_count)(s, n, p, m, maxcount);
814
0
            }
815
0
        }
816
0
        else {
817
            /* To ensure that we have good worst-case behavior,
818
               here's an adaptive version of the algorithm, where if
819
               we match O(m) characters without any matches of the
820
               entire needle, then we predict that the startup cost of
821
               the two-way algorithm will probably be worth it. */
822
0
            return STRINGLIB(adaptive_find)(s, n, p, m, maxcount, mode);
823
0
        }
824
4.16M
    }
825
0
    else {
826
        /* FAST_RSEARCH */
827
0
        return STRINGLIB(default_rfind)(s, n, p, m, maxcount, mode);
828
0
    }
829
4.16M
}
bytes_methods.c:fastsearch
Line
Count
Source
776
5.43M
{
777
5.43M
    if (n < m || (mode == FAST_COUNT && maxcount == 0)) {
778
8
        return -1;
779
8
    }
780
781
    /* look for special cases */
782
5.43M
    if (m <= 1) {
783
5.42M
        if (m <= 0) {
784
0
            return -1;
785
0
        }
786
        /* use special case for 1-character strings */
787
5.42M
        if (mode == FAST_SEARCH)
788
0
            return STRINGLIB(find_char)(s, n, p[0]);
789
5.42M
        else if (mode == FAST_RSEARCH)
790
0
            return STRINGLIB(rfind_char)(s, n, p[0]);
791
5.42M
        else {
792
5.42M
            if (maxcount == PY_SSIZE_T_MAX) {
793
5.42M
                return STRINGLIB(count_char_no_maxcount)(s, n, p[0]);
794
5.42M
            }
795
0
            return STRINGLIB(count_char)(s, n, p[0], maxcount);
796
5.42M
        }
797
5.42M
    }
798
799
9.35k
    if (mode != FAST_RSEARCH) {
800
2.57k
        if (n < 2500 || (m < 100 && n < 30000) || m < 6) {
801
2.57k
            return STRINGLIB(default_find)(s, n, p, m, maxcount, mode);
802
2.57k
        }
803
0
        else if ((m >> 2) * 3 < (n >> 2)) {
804
            /* 33% threshold, but don't overflow. */
805
            /* For larger problems where the needle isn't a huge
806
               percentage of the size of the haystack, the relatively
807
               expensive O(m) startup cost of the two-way algorithm
808
               will surely pay off. */
809
0
            if (mode == FAST_SEARCH) {
810
0
                return STRINGLIB(_two_way_find)(s, n, p, m);
811
0
            }
812
0
            else {
813
0
                return STRINGLIB(_two_way_count)(s, n, p, m, maxcount);
814
0
            }
815
0
        }
816
0
        else {
817
            /* To ensure that we have good worst-case behavior,
818
               here's an adaptive version of the algorithm, where if
819
               we match O(m) characters without any matches of the
820
               entire needle, then we predict that the startup cost of
821
               the two-way algorithm will probably be worth it. */
822
0
            return STRINGLIB(adaptive_find)(s, n, p, m, maxcount, mode);
823
0
        }
824
2.57k
    }
825
6.78k
    else {
826
        /* FAST_RSEARCH */
827
6.78k
        return STRINGLIB(default_rfind)(s, n, p, m, maxcount, mode);
828
6.78k
    }
829
9.35k
}
Unexecuted instantiation: bytearrayobject.c:fastsearch
830