Coverage Report

Created: 2025-07-04 06:49

/src/cpython/Objects/stringlib/fastsearch.h
Line
Count
Source (jump to first uncovered line)
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
580M
#define FAST_COUNT 0
25
390M
#define FAST_SEARCH 1
26
84.7M
#define FAST_RSEARCH 2
27
28
#if LONG_BIT >= 128
29
#define STRINGLIB_BLOOM_WIDTH 128
30
#elif LONG_BIT >= 64
31
5.84G
#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
32.5M
    ((mask |= (1UL << ((ch) & (STRINGLIB_BLOOM_WIDTH -1)))))
40
#define STRINGLIB_BLOOM(mask, ch)     \
41
5.80G
    ((mask &  (1UL << ((ch) & (STRINGLIB_BLOOM_WIDTH -1)))))
42
43
#ifdef STRINGLIB_FAST_MEMCHR
44
206M
#  define MEMCHR_CUT_OFF 15
45
#else
46
57.6M
#  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
263M
{
52
263M
    const STRINGLIB_CHAR *p, *e;
53
54
263M
    p = s;
55
263M
    e = s + n;
56
263M
    if (n > MEMCHR_CUT_OFF) {
57
#ifdef STRINGLIB_FAST_MEMCHR
58
110M
        p = STRINGLIB_FAST_MEMCHR(s, ch, n);
59
110M
        if (p != NULL)
60
109M
            return (p - s);
61
1.50M
        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
49.8M
        if (needle != 0) {
71
49.4M
            do {
72
49.4M
                void *candidate = memchr(p, needle,
73
49.4M
                                         (e - p) * sizeof(STRINGLIB_CHAR));
74
49.4M
                if (candidate == NULL)
75
506k
                    return -1;
76
48.9M
                s1 = p;
77
48.9M
                p = (const STRINGLIB_CHAR *)
78
48.9M
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
79
48.9M
                if (*p == ch)
80
48.9M
                    return (p - s);
81
                /* False positive */
82
59.0k
                p++;
83
59.0k
                if (p - s1 > MEMCHR_CUT_OFF)
84
28.2k
                    continue;
85
30.7k
                if (e - p <= MEMCHR_CUT_OFF)
86
3.31k
                    break;
87
27.4k
                e1 = p + MEMCHR_CUT_OFF;
88
917k
                while (p != e1) {
89
897k
                    if (*p == ch)
90
7.40k
                        return (p - s);
91
890k
                    p++;
92
890k
                }
93
27.4k
            }
94
49.4M
            while (e - p > MEMCHR_CUT_OFF);
95
49.4M
        }
96
#endif
97
160M
    }
98
399M
    while (p < e) {
99
310M
        if (*p == ch)
100
15.1M
            return (p - s);
101
295M
        p++;
102
295M
    }
103
88.4M
    return -1;
104
103M
}
Unexecuted instantiation: bytesobject.c:stringlib_find_char
unicodeobject.c:ucs1lib_find_char
Line
Count
Source
51
107M
{
52
107M
    const STRINGLIB_CHAR *p, *e;
53
54
107M
    p = s;
55
107M
    e = s + n;
56
107M
    if (n > MEMCHR_CUT_OFF) {
57
17.9M
#ifdef STRINGLIB_FAST_MEMCHR
58
17.9M
        p = STRINGLIB_FAST_MEMCHR(s, ch, n);
59
17.9M
        if (p != NULL)
60
17.1M
            return (p - s);
61
792k
        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
                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
17.9M
    }
98
253M
    while (p < e) {
99
167M
        if (*p == ch)
100
3.95M
            return (p - s);
101
163M
        p++;
102
163M
    }
103
85.8M
    return -1;
104
89.8M
}
unicodeobject.c:ucs2lib_find_char
Line
Count
Source
51
57.4M
{
52
57.4M
    const STRINGLIB_CHAR *p, *e;
53
54
57.4M
    p = s;
55
57.4M
    e = s + n;
56
57.4M
    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
49.8M
        const STRINGLIB_CHAR *s1, *e1;
66
49.8M
        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
49.8M
        if (needle != 0) {
71
49.4M
            do {
72
49.4M
                void *candidate = memchr(p, needle,
73
49.4M
                                         (e - p) * sizeof(STRINGLIB_CHAR));
74
49.4M
                if (candidate == NULL)
75
506k
                    return -1;
76
48.9M
                s1 = p;
77
48.9M
                p = (const STRINGLIB_CHAR *)
78
48.9M
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
79
48.9M
                if (*p == ch)
80
48.9M
                    return (p - s);
81
                /* False positive */
82
59.0k
                p++;
83
59.0k
                if (p - s1 > MEMCHR_CUT_OFF)
84
28.2k
                    continue;
85
30.7k
                if (e - p <= MEMCHR_CUT_OFF)
86
3.31k
                    break;
87
27.4k
                e1 = p + MEMCHR_CUT_OFF;
88
917k
                while (p != e1) {
89
897k
                    if (*p == ch)
90
7.40k
                        return (p - s);
91
890k
                    p++;
92
890k
                }
93
27.4k
            }
94
49.4M
            while (e - p > MEMCHR_CUT_OFF);
95
49.4M
        }
96
49.8M
#endif
97
49.8M
    }
98
128M
    while (p < e) {
99
126M
        if (*p == ch)
100
5.59M
            return (p - s);
101
120M
        p++;
102
120M
    }
103
2.47M
    return -1;
104
8.06M
}
unicodeobject.c:ucs4lib_find_char
Line
Count
Source
51
80.6M
{
52
80.6M
    const STRINGLIB_CHAR *p, *e;
53
54
80.6M
    p = s;
55
80.6M
    e = s + n;
56
80.6M
    if (n > MEMCHR_CUT_OFF) {
57
80.5M
#ifdef STRINGLIB_FAST_MEMCHR
58
80.5M
        p = STRINGLIB_FAST_MEMCHR(s, ch, n);
59
80.5M
        if (p != NULL)
60
80.5M
            return (p - s);
61
31.3k
        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
                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
80.5M
    }
98
262k
    while (p < e) {
99
229k
        if (*p == ch)
100
26.7k
            return (p - s);
101
202k
        p++;
102
202k
    }
103
33.1k
    return -1;
104
59.8k
}
unicodeobject.c:asciilib_find_char
Line
Count
Source
51
17.8M
{
52
17.8M
    const STRINGLIB_CHAR *p, *e;
53
54
17.8M
    p = s;
55
17.8M
    e = s + n;
56
17.8M
    if (n > MEMCHR_CUT_OFF) {
57
12.2M
#ifdef STRINGLIB_FAST_MEMCHR
58
12.2M
        p = STRINGLIB_FAST_MEMCHR(s, ch, n);
59
12.2M
        if (p != NULL)
60
11.5M
            return (p - s);
61
682k
        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
                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
12.2M
    }
98
16.4M
    while (p < e) {
99
16.4M
        if (*p == ch)
100
5.55M
            return (p - s);
101
10.8M
        p++;
102
10.8M
    }
103
48.3k
    return -1;
104
5.60M
}
bytes_methods.c:stringlib_find_char
Line
Count
Source
51
1.59k
{
52
1.59k
    const STRINGLIB_CHAR *p, *e;
53
54
1.59k
    p = s;
55
1.59k
    e = s + n;
56
1.59k
    if (n > MEMCHR_CUT_OFF) {
57
1.59k
#ifdef STRINGLIB_FAST_MEMCHR
58
1.59k
        p = STRINGLIB_FAST_MEMCHR(s, ch, n);
59
1.59k
        if (p != NULL)
60
1.35k
            return (p - s);
61
242
        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
                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
1.59k
    }
98
0
    while (p < e) {
99
0
        if (*p == ch)
100
0
            return (p - s);
101
0
        p++;
102
0
    }
103
0
    return -1;
104
0
}
Unexecuted instantiation: bytearrayobject.c:stringlib_find_char
105
106
#undef MEMCHR_CUT_OFF
107
108
#if STRINGLIB_SIZEOF_CHAR == 1
109
37.4k
#  define MEMRCHR_CUT_OFF 15
110
#else
111
309k
#  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
308k
{
118
308k
    const STRINGLIB_CHAR *p;
119
308k
#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
308k
    if (n > MEMRCHR_CUT_OFF) {
126
#if STRINGLIB_SIZEOF_CHAR == 1
127
        p = memrchr(s, ch, n);
128
10.6k
        if (p != NULL)
129
7.27k
            return (p - s);
130
3.36k
        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
127k
        if (needle != 0) {
141
137k
            do {
142
137k
                void *candidate = memrchr(s, needle,
143
137k
                                          n * sizeof(STRINGLIB_CHAR));
144
137k
                if (candidate == NULL)
145
635
                    return -1;
146
136k
                n1 = n;
147
136k
                p = (const STRINGLIB_CHAR *)
148
136k
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
149
136k
                n = p - s;
150
136k
                if (*p == ch)
151
123k
                    return n;
152
                /* False positive */
153
13.3k
                if (n1 - n > MEMRCHR_CUT_OFF)
154
6.05k
                    continue;
155
7.26k
                if (n <= MEMRCHR_CUT_OFF)
156
847
                    break;
157
6.42k
                s1 = p - MEMRCHR_CUT_OFF;
158
243k
                while (p > s1) {
159
237k
                    p--;
160
237k
                    if (*p == ch)
161
905
                        return (p - s);
162
237k
                }
163
5.51k
                n = p - s;
164
5.51k
            }
165
127k
            while (n > MEMRCHR_CUT_OFF);
166
127k
        }
167
#endif
168
138k
    }
169
173k
#endif  /* HAVE_MEMRCHR */
170
173k
    p = s + n;
171
650k
    while (p > s) {
172
634k
        p--;
173
634k
        if (*p == ch)
174
156k
            return (p - s);
175
634k
    }
176
16.3k
    return -1;
177
173k
}
Unexecuted instantiation: bytesobject.c:stringlib_rfind_char
unicodeobject.c:ucs1lib_rfind_char
Line
Count
Source
117
9.14k
{
118
9.14k
    const STRINGLIB_CHAR *p;
119
9.14k
#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
9.14k
    if (n > MEMRCHR_CUT_OFF) {
126
3.91k
#if STRINGLIB_SIZEOF_CHAR == 1
127
3.91k
        p = memrchr(s, ch, n);
128
3.91k
        if (p != NULL)
129
2.97k
            return (p - s);
130
944
        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
3.91k
    }
169
5.22k
#endif  /* HAVE_MEMRCHR */
170
5.22k
    p = s + n;
171
14.9k
    while (p > s) {
172
14.2k
        p--;
173
14.2k
        if (*p == ch)
174
4.49k
            return (p - s);
175
14.2k
    }
176
733
    return -1;
177
5.22k
}
unicodeobject.c:ucs2lib_rfind_char
Line
Count
Source
117
28.7k
{
118
28.7k
    const STRINGLIB_CHAR *p;
119
28.7k
#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
28.7k
    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
12.3k
        const STRINGLIB_CHAR *s1;
135
12.3k
        Py_ssize_t n1;
136
12.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
12.3k
        if (needle != 0) {
141
14.2k
            do {
142
14.2k
                void *candidate = memrchr(s, needle,
143
14.2k
                                          n * sizeof(STRINGLIB_CHAR));
144
14.2k
                if (candidate == NULL)
145
372
                    return -1;
146
13.9k
                n1 = n;
147
13.9k
                p = (const STRINGLIB_CHAR *)
148
13.9k
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
149
13.9k
                n = p - s;
150
13.9k
                if (*p == ch)
151
10.4k
                    return n;
152
                /* False positive */
153
3.50k
                if (n1 - n > MEMRCHR_CUT_OFF)
154
1.29k
                    continue;
155
2.20k
                if (n <= MEMRCHR_CUT_OFF)
156
399
                    break;
157
1.80k
                s1 = p - MEMRCHR_CUT_OFF;
158
67.9k
                while (p > s1) {
159
66.3k
                    p--;
160
66.3k
                    if (*p == ch)
161
223
                        return (p - s);
162
66.3k
                }
163
1.58k
                n = p - s;
164
1.58k
            }
165
12.3k
            while (n > MEMRCHR_CUT_OFF);
166
12.3k
        }
167
12.3k
#endif
168
12.3k
    }
169
17.7k
#endif  /* HAVE_MEMRCHR */
170
17.7k
    p = s + n;
171
99.9k
    while (p > s) {
172
98.1k
        p--;
173
98.1k
        if (*p == ch)
174
15.9k
            return (p - s);
175
98.1k
    }
176
1.78k
    return -1;
177
17.7k
}
unicodeobject.c:ucs4lib_rfind_char
Line
Count
Source
117
242k
{
118
242k
    const STRINGLIB_CHAR *p;
119
242k
#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
242k
    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
115k
        const STRINGLIB_CHAR *s1;
135
115k
        Py_ssize_t n1;
136
115k
        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
115k
        if (needle != 0) {
141
123k
            do {
142
123k
                void *candidate = memrchr(s, needle,
143
123k
                                          n * sizeof(STRINGLIB_CHAR));
144
123k
                if (candidate == NULL)
145
263
                    return -1;
146
122k
                n1 = n;
147
122k
                p = (const STRINGLIB_CHAR *)
148
122k
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
149
122k
                n = p - s;
150
122k
                if (*p == ch)
151
113k
                    return n;
152
                /* False positive */
153
9.82k
                if (n1 - n > MEMRCHR_CUT_OFF)
154
4.76k
                    continue;
155
5.06k
                if (n <= MEMRCHR_CUT_OFF)
156
448
                    break;
157
4.61k
                s1 = p - MEMRCHR_CUT_OFF;
158
175k
                while (p > s1) {
159
171k
                    p--;
160
171k
                    if (*p == ch)
161
682
                        return (p - s);
162
171k
                }
163
3.93k
                n = p - s;
164
3.93k
            }
165
115k
            while (n > MEMRCHR_CUT_OFF);
166
115k
        }
167
115k
#endif
168
115k
    }
169
128k
#endif  /* HAVE_MEMRCHR */
170
128k
    p = s + n;
171
416k
    while (p > s) {
172
414k
        p--;
173
414k
        if (*p == ch)
174
127k
            return (p - s);
175
414k
    }
176
1.38k
    return -1;
177
128k
}
unicodeobject.c:asciilib_rfind_char
Line
Count
Source
117
9.92k
{
118
9.92k
    const STRINGLIB_CHAR *p;
119
9.92k
#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
9.92k
    if (n > MEMRCHR_CUT_OFF) {
126
3.51k
#if STRINGLIB_SIZEOF_CHAR == 1
127
3.51k
        p = memrchr(s, ch, n);
128
3.51k
        if (p != NULL)
129
3.43k
            return (p - s);
130
80
        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
3.51k
    }
169
6.40k
#endif  /* HAVE_MEMRCHR */
170
6.40k
    p = s + n;
171
35.5k
    while (p > s) {
172
33.5k
        p--;
173
33.5k
        if (*p == ch)
174
4.40k
            return (p - s);
175
33.5k
    }
176
2.00k
    return -1;
177
6.40k
}
bytes_methods.c:stringlib_rfind_char
Line
Count
Source
117
18.3k
{
118
18.3k
    const STRINGLIB_CHAR *p;
119
18.3k
#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
18.3k
    if (n > MEMRCHR_CUT_OFF) {
126
3.20k
#if STRINGLIB_SIZEOF_CHAR == 1
127
3.20k
        p = memrchr(s, ch, n);
128
3.20k
        if (p != NULL)
129
864
            return (p - s);
130
2.33k
        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
3.20k
    }
169
15.1k
#endif  /* HAVE_MEMRCHR */
170
15.1k
    p = s + n;
171
84.3k
    while (p > s) {
172
73.9k
        p--;
173
73.9k
        if (*p == ch)
174
4.74k
            return (p - s);
175
73.9k
    }
176
10.3k
    return -1;
177
15.1k
}
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
38
{
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
38
    Py_ssize_t max_suffix = 0;
204
38
    Py_ssize_t candidate = 1;
205
38
    Py_ssize_t k = 0;
206
    // The period of the right half.
207
38
    Py_ssize_t period = 1;
208
209
380
    while (candidate + k < len_needle) {
210
        // each loop increases candidate + k + max_suffix
211
342
        STRINGLIB_CHAR a = needle[candidate + k];
212
342
        STRINGLIB_CHAR b = needle[max_suffix + k];
213
        // check if the suffix at candidate is better than max_suffix
214
342
        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
247
            candidate += k + 1;
219
247
            k = 0;
220
            // We've ruled out any period smaller than what's
221
            // been scanned since max_suffix.
222
247
            period = candidate - max_suffix;
223
247
        }
224
95
        else if (a == b) {
225
19
            if (k + 1 != period) {
226
                // Keep scanning the equal strings
227
0
                k++;
228
0
            }
229
19
            else {
230
                // Matched a whole period.
231
                // Start matching the next period.
232
19
                candidate += period;
233
19
                k = 0;
234
19
            }
235
19
        }
236
76
        else {
237
            // Did better than max_suffix, so replace it.
238
76
            max_suffix = candidate;
239
76
            candidate++;
240
76
            k = 0;
241
76
            period = 1;
242
76
        }
243
342
    }
244
38
    *return_period = period;
245
38
    return max_suffix;
246
38
}
Unexecuted instantiation: bytesobject.c:stringlib__lex_search
Unexecuted instantiation: unicodeobject.c:asciilib__lex_search
unicodeobject.c:ucs1lib__lex_search
Line
Count
Source
199
38
{
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
38
    Py_ssize_t max_suffix = 0;
204
38
    Py_ssize_t candidate = 1;
205
38
    Py_ssize_t k = 0;
206
    // The period of the right half.
207
38
    Py_ssize_t period = 1;
208
209
380
    while (candidate + k < len_needle) {
210
        // each loop increases candidate + k + max_suffix
211
342
        STRINGLIB_CHAR a = needle[candidate + k];
212
342
        STRINGLIB_CHAR b = needle[max_suffix + k];
213
        // check if the suffix at candidate is better than max_suffix
214
342
        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
247
            candidate += k + 1;
219
247
            k = 0;
220
            // We've ruled out any period smaller than what's
221
            // been scanned since max_suffix.
222
247
            period = candidate - max_suffix;
223
247
        }
224
95
        else if (a == b) {
225
19
            if (k + 1 != period) {
226
                // Keep scanning the equal strings
227
0
                k++;
228
0
            }
229
19
            else {
230
                // Matched a whole period.
231
                // Start matching the next period.
232
19
                candidate += period;
233
19
                k = 0;
234
19
            }
235
19
        }
236
76
        else {
237
            // Did better than max_suffix, so replace it.
238
76
            max_suffix = candidate;
239
76
            candidate++;
240
76
            k = 0;
241
76
            period = 1;
242
76
        }
243
342
    }
244
38
    *return_period = period;
245
38
    return max_suffix;
246
38
}
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
19
{
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
19
    Py_ssize_t cut1, period1, cut2, period2, cut, period;
286
19
    cut1 = STRINGLIB(_lex_search)(needle, len_needle, &period1, 0);
287
19
    cut2 = STRINGLIB(_lex_search)(needle, len_needle, &period2, 1);
288
289
    // Take the later cut.
290
19
    if (cut1 > cut2) {
291
19
        period = period1;
292
19
        cut = cut1;
293
19
    }
294
0
    else {
295
0
        period = period2;
296
0
        cut = cut2;
297
0
    }
298
299
19
    LOG("split: "); LOG_STRING(needle, cut);
300
19
    LOG(" + "); LOG_STRING(needle + cut, len_needle - cut);
301
19
    LOG("\n");
302
303
19
    *return_period = period;
304
19
    return cut;
305
19
}
Unexecuted instantiation: bytesobject.c:stringlib__factorize
Unexecuted instantiation: unicodeobject.c:asciilib__factorize
unicodeobject.c:ucs1lib__factorize
Line
Count
Source
252
19
{
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
19
    Py_ssize_t cut1, period1, cut2, period2, cut, period;
286
19
    cut1 = STRINGLIB(_lex_search)(needle, len_needle, &period1, 0);
287
19
    cut2 = STRINGLIB(_lex_search)(needle, len_needle, &period2, 1);
288
289
    // Take the later cut.
290
19
    if (cut1 > cut2) {
291
19
        period = period1;
292
19
        cut = cut1;
293
19
    }
294
0
    else {
295
0
        period = period2;
296
0
        cut = cut2;
297
0
    }
298
299
19
    LOG("split: "); LOG_STRING(needle, cut);
300
19
    LOG(" + "); LOG_STRING(needle + cut, len_needle - cut);
301
19
    LOG("\n");
302
303
19
    *return_period = period;
304
19
    return cut;
305
19
}
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
209
#define SHIFT_TYPE uint8_t
309
#define MAX_SHIFT UINT8_MAX
310
311
68.4k
#define TABLE_SIZE_BITS 6u
312
68.4k
#define TABLE_SIZE (1U << TABLE_SIZE_BITS)
313
67.1k
#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
19
{
330
19
    p->needle = needle;
331
19
    p->len_needle = len_needle;
332
19
    p->cut = STRINGLIB(_factorize)(needle, len_needle, &(p->period));
333
19
    assert(p->period + p->cut <= len_needle);
334
19
    p->is_periodic = (0 == memcmp(needle,
335
19
                                  needle + p->period,
336
19
                                  p->cut * STRINGLIB_SIZEOF_CHAR));
337
19
    if (p->is_periodic) {
338
0
        assert(p->cut <= len_needle/2);
339
0
        assert(p->cut < p->period);
340
0
    }
341
19
    else {
342
        // A lower bound on the period
343
19
        p->period = Py_MAX(p->cut, len_needle - p->cut) + 1;
344
19
    }
345
    // The gap between the last character and the previous
346
    // occurrence of an equivalent character (modulo TABLE_SIZE)
347
19
    p->gap = len_needle;
348
19
    STRINGLIB_CHAR last = needle[len_needle - 1] & TABLE_MASK;
349
133
    for (Py_ssize_t i = len_needle - 2; i >= 0; i--) {
350
133
        STRINGLIB_CHAR x = needle[i] & TABLE_MASK;
351
133
        if (x == last) {
352
19
            p->gap = len_needle - 1 - i;
353
19
            break;
354
19
        }
355
133
    }
356
    // Fill up a compressed Boyer-Moore "Bad Character" table
357
19
    Py_ssize_t not_found_shift = Py_MIN(len_needle, MAX_SHIFT);
358
1.23k
    for (Py_ssize_t i = 0; i < (Py_ssize_t)TABLE_SIZE; i++) {
359
1.21k
        p->table[i] = Py_SAFE_DOWNCAST(not_found_shift,
360
1.21k
                                       Py_ssize_t, SHIFT_TYPE);
361
1.21k
    }
362
209
    for (Py_ssize_t i = len_needle - not_found_shift; i < len_needle; i++) {
363
190
        SHIFT_TYPE shift = Py_SAFE_DOWNCAST(len_needle - 1 - i,
364
190
                                            Py_ssize_t, SHIFT_TYPE);
365
190
        p->table[needle[i] & TABLE_MASK] = shift;
366
190
    }
367
19
}
Unexecuted instantiation: bytesobject.c:stringlib__preprocess
Unexecuted instantiation: unicodeobject.c:asciilib__preprocess
unicodeobject.c:ucs1lib__preprocess
Line
Count
Source
329
19
{
330
19
    p->needle = needle;
331
19
    p->len_needle = len_needle;
332
19
    p->cut = STRINGLIB(_factorize)(needle, len_needle, &(p->period));
333
19
    assert(p->period + p->cut <= len_needle);
334
19
    p->is_periodic = (0 == memcmp(needle,
335
19
                                  needle + p->period,
336
19
                                  p->cut * STRINGLIB_SIZEOF_CHAR));
337
19
    if (p->is_periodic) {
338
0
        assert(p->cut <= len_needle/2);
339
0
        assert(p->cut < p->period);
340
0
    }
341
19
    else {
342
        // A lower bound on the period
343
19
        p->period = Py_MAX(p->cut, len_needle - p->cut) + 1;
344
19
    }
345
    // The gap between the last character and the previous
346
    // occurrence of an equivalent character (modulo TABLE_SIZE)
347
19
    p->gap = len_needle;
348
19
    STRINGLIB_CHAR last = needle[len_needle - 1] & TABLE_MASK;
349
133
    for (Py_ssize_t i = len_needle - 2; i >= 0; i--) {
350
133
        STRINGLIB_CHAR x = needle[i] & TABLE_MASK;
351
133
        if (x == last) {
352
19
            p->gap = len_needle - 1 - i;
353
19
            break;
354
19
        }
355
133
    }
356
    // Fill up a compressed Boyer-Moore "Bad Character" table
357
19
    Py_ssize_t not_found_shift = Py_MIN(len_needle, MAX_SHIFT);
358
1.23k
    for (Py_ssize_t i = 0; i < (Py_ssize_t)TABLE_SIZE; i++) {
359
1.21k
        p->table[i] = Py_SAFE_DOWNCAST(not_found_shift,
360
1.21k
                                       Py_ssize_t, SHIFT_TYPE);
361
1.21k
    }
362
209
    for (Py_ssize_t i = len_needle - not_found_shift; i < len_needle; i++) {
363
190
        SHIFT_TYPE shift = Py_SAFE_DOWNCAST(len_needle - 1 - i,
364
190
                                            Py_ssize_t, SHIFT_TYPE);
365
190
        p->table[needle[i] & TABLE_MASK] = shift;
366
190
    }
367
19
}
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
19
{
373
    // Crochemore and Perrin's (1991) Two-Way algorithm.
374
    // See http://www-igm.univ-mlv.fr/~lecroq/string/node26.html#SECTION00260
375
19
    const Py_ssize_t len_needle = p->len_needle;
376
19
    const Py_ssize_t cut = p->cut;
377
19
    Py_ssize_t period = p->period;
378
19
    const STRINGLIB_CHAR *const needle = p->needle;
379
19
    const STRINGLIB_CHAR *window_last = haystack + len_needle - 1;
380
19
    const STRINGLIB_CHAR *const haystack_end = haystack + len_haystack;
381
19
    SHIFT_TYPE *table = p->table;
382
19
    const STRINGLIB_CHAR *window;
383
19
    LOG("===== Two-way: \"%s\" in \"%s\". =====\n", needle, haystack);
384
385
19
    Py_ssize_t gap = p->gap;
386
19
    Py_ssize_t gap_jump_end = Py_MIN(len_needle, cut + gap);
387
19
    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
19
    else {
454
19
        period = Py_MAX(gap, period);
455
19
        LOG("Needle is not periodic.\n");
456
12.2k
      windowloop:
457
12.2k
        while (window_last < haystack_end) {
458
66.8k
            for (;;) {
459
66.8k
                LOG_LINEUP();
460
66.8k
                Py_ssize_t shift = table[(*window_last) & TABLE_MASK];
461
66.8k
                window_last += shift;
462
66.8k
                if (shift == 0) {
463
12.1k
                    break;
464
12.1k
                }
465
54.6k
                if (window_last >= haystack_end) {
466
16
                    return -1;
467
16
                }
468
54.6k
                LOG("Horspool skip\n");
469
54.6k
            }
470
12.1k
            window = window_last - len_needle + 1;
471
12.1k
            assert((window[len_needle - 1] & TABLE_MASK) ==
472
12.1k
                   (needle[len_needle - 1] & TABLE_MASK));
473
12.1k
            Py_ssize_t i = cut;
474
12.3k
            for (; i < len_needle; i++) {
475
12.2k
                if (needle[i] != window[i]) {
476
12.1k
                    if (i < gap_jump_end) {
477
12.1k
                        LOG("Early right half mismatch: jump by gap.\n");
478
12.1k
                        assert(gap >= i - cut + 1);
479
12.1k
                        window_last += gap;
480
12.1k
                    }
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
12.1k
                    goto windowloop;
487
12.1k
                }
488
12.2k
            }
489
98
            for (Py_ssize_t i = 0; i < cut; i++) {
490
96
                if (needle[i] != window[i]) {
491
80
                    LOG("Left half does not match.\n");
492
80
                    window_last += period;
493
80
                    goto windowloop;
494
80
                }
495
96
            }
496
2
            LOG("Found a match!\n");
497
2
            return window - haystack;
498
82
        }
499
12.2k
    }
500
1
    LOG("Not found. Returning -1.\n");
501
1
    return -1;
502
19
}
Unexecuted instantiation: bytesobject.c:stringlib__two_way
Unexecuted instantiation: unicodeobject.c:asciilib__two_way
unicodeobject.c:ucs1lib__two_way
Line
Count
Source
372
19
{
373
    // Crochemore and Perrin's (1991) Two-Way algorithm.
374
    // See http://www-igm.univ-mlv.fr/~lecroq/string/node26.html#SECTION00260
375
19
    const Py_ssize_t len_needle = p->len_needle;
376
19
    const Py_ssize_t cut = p->cut;
377
19
    Py_ssize_t period = p->period;
378
19
    const STRINGLIB_CHAR *const needle = p->needle;
379
19
    const STRINGLIB_CHAR *window_last = haystack + len_needle - 1;
380
19
    const STRINGLIB_CHAR *const haystack_end = haystack + len_haystack;
381
19
    SHIFT_TYPE *table = p->table;
382
19
    const STRINGLIB_CHAR *window;
383
19
    LOG("===== Two-way: \"%s\" in \"%s\". =====\n", needle, haystack);
384
385
19
    Py_ssize_t gap = p->gap;
386
19
    Py_ssize_t gap_jump_end = Py_MIN(len_needle, cut + gap);
387
19
    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
19
    else {
454
19
        period = Py_MAX(gap, period);
455
19
        LOG("Needle is not periodic.\n");
456
12.2k
      windowloop:
457
12.2k
        while (window_last < haystack_end) {
458
66.8k
            for (;;) {
459
66.8k
                LOG_LINEUP();
460
66.8k
                Py_ssize_t shift = table[(*window_last) & TABLE_MASK];
461
66.8k
                window_last += shift;
462
66.8k
                if (shift == 0) {
463
12.1k
                    break;
464
12.1k
                }
465
54.6k
                if (window_last >= haystack_end) {
466
16
                    return -1;
467
16
                }
468
54.6k
                LOG("Horspool skip\n");
469
54.6k
            }
470
12.1k
            window = window_last - len_needle + 1;
471
12.1k
            assert((window[len_needle - 1] & TABLE_MASK) ==
472
12.1k
                   (needle[len_needle - 1] & TABLE_MASK));
473
12.1k
            Py_ssize_t i = cut;
474
12.3k
            for (; i < len_needle; i++) {
475
12.2k
                if (needle[i] != window[i]) {
476
12.1k
                    if (i < gap_jump_end) {
477
12.1k
                        LOG("Early right half mismatch: jump by gap.\n");
478
12.1k
                        assert(gap >= i - cut + 1);
479
12.1k
                        window_last += gap;
480
12.1k
                    }
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
12.1k
                    goto windowloop;
487
12.1k
                }
488
12.2k
            }
489
98
            for (Py_ssize_t i = 0; i < cut; i++) {
490
96
                if (needle[i] != window[i]) {
491
80
                    LOG("Left half does not match.\n");
492
80
                    window_last += period;
493
80
                    goto windowloop;
494
80
                }
495
96
            }
496
2
            LOG("Found a match!\n");
497
2
            return window - haystack;
498
82
        }
499
12.2k
    }
500
1
    LOG("Not found. Returning -1.\n");
501
1
    return -1;
502
19
}
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
19
{
511
19
    LOG("###### Finding \"%s\" in \"%s\".\n", needle, haystack);
512
19
    STRINGLIB(prework) p;
513
19
    STRINGLIB(_preprocess)(needle, len_needle, &p);
514
19
    return STRINGLIB(_two_way)(haystack, len_haystack, &p);
515
19
}
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
19
{
511
19
    LOG("###### Finding \"%s\" in \"%s\".\n", needle, haystack);
512
19
    STRINGLIB(prework) p;
513
19
    STRINGLIB(_preprocess)(needle, len_needle, &p);
514
19
    return STRINGLIB(_two_way)(haystack, len_haystack, &p);
515
19
}
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
16.2M
{
561
16.2M
    const Py_ssize_t w = n - m;
562
16.2M
    Py_ssize_t mlast = m - 1, count = 0;
563
16.2M
    Py_ssize_t gap = mlast;
564
16.2M
    const STRINGLIB_CHAR last = p[mlast];
565
16.2M
    const STRINGLIB_CHAR *const ss = &s[mlast];
566
567
16.2M
    unsigned long mask = 0;
568
32.5M
    for (Py_ssize_t i = 0; i < mlast; i++) {
569
16.3M
        STRINGLIB_BLOOM_ADD(mask, p[i]);
570
16.3M
        if (p[i] == last) {
571
338k
            gap = mlast - i - 1;
572
338k
        }
573
16.3M
    }
574
16.2M
    STRINGLIB_BLOOM_ADD(mask, last);
575
576
5.83G
    for (Py_ssize_t i = 0; i <= w; i++) {
577
5.83G
        if (ss[i] == last) {
578
            /* candidate match */
579
67.1M
            Py_ssize_t j;
580
90.9M
            for (j = 0; j < mlast; j++) {
581
67.1M
                if (s[i+j] != p[j]) {
582
43.4M
                    break;
583
43.4M
                }
584
67.1M
            }
585
67.1M
            if (j == mlast) {
586
                /* got a match! */
587
23.7M
                if (mode != FAST_COUNT) {
588
11.9M
                    return i;
589
11.9M
                }
590
11.8M
                count++;
591
11.8M
                if (count == maxcount) {
592
0
                    return maxcount;
593
0
                }
594
11.8M
                i = i + mlast;
595
11.8M
                continue;
596
11.8M
            }
597
            /* miss: check if next character is part of pattern */
598
43.4M
            if (!STRINGLIB_BLOOM(mask, ss[i+1])) {
599
8.21M
                i = i + m;
600
8.21M
            }
601
35.1M
            else {
602
35.1M
                i = i + gap;
603
35.1M
            }
604
43.4M
        }
605
5.76G
        else {
606
            /* skip: check if next character is part of pattern */
607
5.76G
            if (!STRINGLIB_BLOOM(mask, ss[i+1])) {
608
5.70G
                i = i + m;
609
5.70G
            }
610
5.76G
        }
611
5.83G
    }
612
4.33M
    return mode == FAST_COUNT ? count : -1;
613
16.2M
}
Unexecuted instantiation: bytesobject.c:stringlib_default_find
unicodeobject.c:asciilib_default_find
Line
Count
Source
560
1.92M
{
561
1.92M
    const Py_ssize_t w = n - m;
562
1.92M
    Py_ssize_t mlast = m - 1, count = 0;
563
1.92M
    Py_ssize_t gap = mlast;
564
1.92M
    const STRINGLIB_CHAR last = p[mlast];
565
1.92M
    const STRINGLIB_CHAR *const ss = &s[mlast];
566
567
1.92M
    unsigned long mask = 0;
568
3.85M
    for (Py_ssize_t i = 0; i < mlast; i++) {
569
1.92M
        STRINGLIB_BLOOM_ADD(mask, p[i]);
570
1.92M
        if (p[i] == last) {
571
3.87k
            gap = mlast - i - 1;
572
3.87k
        }
573
1.92M
    }
574
1.92M
    STRINGLIB_BLOOM_ADD(mask, last);
575
576
172M
    for (Py_ssize_t i = 0; i <= w; i++) {
577
172M
        if (ss[i] == last) {
578
            /* candidate match */
579
3.81M
            Py_ssize_t j;
580
5.71M
            for (j = 0; j < mlast; j++) {
581
3.81M
                if (s[i+j] != p[j]) {
582
1.91M
                    break;
583
1.91M
                }
584
3.81M
            }
585
3.81M
            if (j == mlast) {
586
                /* got a match! */
587
1.90M
                if (mode != FAST_COUNT) {
588
1.90M
                    return i;
589
1.90M
                }
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
1.91M
            if (!STRINGLIB_BLOOM(mask, ss[i+1])) {
599
23.0k
                i = i + m;
600
23.0k
            }
601
1.89M
            else {
602
1.89M
                i = i + gap;
603
1.89M
            }
604
1.91M
        }
605
168M
        else {
606
            /* skip: check if next character is part of pattern */
607
168M
            if (!STRINGLIB_BLOOM(mask, ss[i+1])) {
608
162M
                i = i + m;
609
162M
            }
610
168M
        }
611
172M
    }
612
26.4k
    return mode == FAST_COUNT ? count : -1;
613
1.92M
}
unicodeobject.c:ucs1lib_default_find
Line
Count
Source
560
5.57M
{
561
5.57M
    const Py_ssize_t w = n - m;
562
5.57M
    Py_ssize_t mlast = m - 1, count = 0;
563
5.57M
    Py_ssize_t gap = mlast;
564
5.57M
    const STRINGLIB_CHAR last = p[mlast];
565
5.57M
    const STRINGLIB_CHAR *const ss = &s[mlast];
566
567
5.57M
    unsigned long mask = 0;
568
11.2M
    for (Py_ssize_t i = 0; i < mlast; i++) {
569
5.62M
        STRINGLIB_BLOOM_ADD(mask, p[i]);
570
5.62M
        if (p[i] == last) {
571
274k
            gap = mlast - i - 1;
572
274k
        }
573
5.62M
    }
574
5.57M
    STRINGLIB_BLOOM_ADD(mask, last);
575
576
2.94G
    for (Py_ssize_t i = 0; i <= w; i++) {
577
2.93G
        if (ss[i] == last) {
578
            /* candidate match */
579
21.4M
            Py_ssize_t j;
580
26.2M
            for (j = 0; j < mlast; j++) {
581
21.4M
                if (s[i+j] != p[j]) {
582
16.7M
                    break;
583
16.7M
                }
584
21.4M
            }
585
21.4M
            if (j == mlast) {
586
                /* got a match! */
587
4.76M
                if (mode != FAST_COUNT) {
588
1.41M
                    return i;
589
1.41M
                }
590
3.34M
                count++;
591
3.34M
                if (count == maxcount) {
592
0
                    return maxcount;
593
0
                }
594
3.34M
                i = i + mlast;
595
3.34M
                continue;
596
3.34M
            }
597
            /* miss: check if next character is part of pattern */
598
16.7M
            if (!STRINGLIB_BLOOM(mask, ss[i+1])) {
599
3.89M
                i = i + m;
600
3.89M
            }
601
12.8M
            else {
602
12.8M
                i = i + gap;
603
12.8M
            }
604
16.7M
        }
605
2.91G
        else {
606
            /* skip: check if next character is part of pattern */
607
2.91G
            if (!STRINGLIB_BLOOM(mask, ss[i+1])) {
608
2.90G
                i = i + m;
609
2.90G
            }
610
2.91G
        }
611
2.93G
    }
612
4.15M
    return mode == FAST_COUNT ? count : -1;
613
5.57M
}
unicodeobject.c:ucs2lib_default_find
Line
Count
Source
560
4.01M
{
561
4.01M
    const Py_ssize_t w = n - m;
562
4.01M
    Py_ssize_t mlast = m - 1, count = 0;
563
4.01M
    Py_ssize_t gap = mlast;
564
4.01M
    const STRINGLIB_CHAR last = p[mlast];
565
4.01M
    const STRINGLIB_CHAR *const ss = &s[mlast];
566
567
4.01M
    unsigned long mask = 0;
568
8.03M
    for (Py_ssize_t i = 0; i < mlast; i++) {
569
4.02M
        STRINGLIB_BLOOM_ADD(mask, p[i]);
570
4.02M
        if (p[i] == last) {
571
36.5k
            gap = mlast - i - 1;
572
36.5k
        }
573
4.02M
    }
574
4.01M
    STRINGLIB_BLOOM_ADD(mask, last);
575
576
1.30G
    for (Py_ssize_t i = 0; i <= w; i++) {
577
1.30G
        if (ss[i] == last) {
578
            /* candidate match */
579
14.2M
            Py_ssize_t j;
580
21.9M
            for (j = 0; j < mlast; j++) {
581
14.2M
                if (s[i+j] != p[j]) {
582
6.48M
                    break;
583
6.48M
                }
584
14.2M
            }
585
14.2M
            if (j == mlast) {
586
                /* got a match! */
587
7.74M
                if (mode != FAST_COUNT) {
588
3.90M
                    return i;
589
3.90M
                }
590
3.84M
                count++;
591
3.84M
                if (count == maxcount) {
592
0
                    return maxcount;
593
0
                }
594
3.84M
                i = i + mlast;
595
3.84M
                continue;
596
3.84M
            }
597
            /* miss: check if next character is part of pattern */
598
6.48M
            if (!STRINGLIB_BLOOM(mask, ss[i+1])) {
599
1.59M
                i = i + m;
600
1.59M
            }
601
4.88M
            else {
602
4.88M
                i = i + gap;
603
4.88M
            }
604
6.48M
        }
605
1.29G
        else {
606
            /* skip: check if next character is part of pattern */
607
1.29G
            if (!STRINGLIB_BLOOM(mask, ss[i+1])) {
608
1.28G
                i = i + m;
609
1.28G
            }
610
1.29G
        }
611
1.30G
    }
612
108k
    return mode == FAST_COUNT ? count : -1;
613
4.01M
}
unicodeobject.c:ucs4lib_default_find
Line
Count
Source
560
4.72M
{
561
4.72M
    const Py_ssize_t w = n - m;
562
4.72M
    Py_ssize_t mlast = m - 1, count = 0;
563
4.72M
    Py_ssize_t gap = mlast;
564
4.72M
    const STRINGLIB_CHAR last = p[mlast];
565
4.72M
    const STRINGLIB_CHAR *const ss = &s[mlast];
566
567
4.72M
    unsigned long mask = 0;
568
9.44M
    for (Py_ssize_t i = 0; i < mlast; i++) {
569
4.72M
        STRINGLIB_BLOOM_ADD(mask, p[i]);
570
4.72M
        if (p[i] == last) {
571
20.9k
            gap = mlast - i - 1;
572
20.9k
        }
573
4.72M
    }
574
4.72M
    STRINGLIB_BLOOM_ADD(mask, last);
575
576
1.41G
    for (Py_ssize_t i = 0; i <= w; i++) {
577
1.41G
        if (ss[i] == last) {
578
            /* candidate match */
579
27.6M
            Py_ssize_t j;
580
36.9M
            for (j = 0; j < mlast; j++) {
581
27.6M
                if (s[i+j] != p[j]) {
582
18.2M
                    break;
583
18.2M
                }
584
27.6M
            }
585
27.6M
            if (j == mlast) {
586
                /* got a match! */
587
9.34M
                if (mode != FAST_COUNT) {
588
4.68M
                    return i;
589
4.68M
                }
590
4.66M
                count++;
591
4.66M
                if (count == maxcount) {
592
0
                    return maxcount;
593
0
                }
594
4.66M
                i = i + mlast;
595
4.66M
                continue;
596
4.66M
            }
597
            /* miss: check if next character is part of pattern */
598
18.2M
            if (!STRINGLIB_BLOOM(mask, ss[i+1])) {
599
2.70M
                i = i + m;
600
2.70M
            }
601
15.5M
            else {
602
15.5M
                i = i + gap;
603
15.5M
            }
604
18.2M
        }
605
1.38G
        else {
606
            /* skip: check if next character is part of pattern */
607
1.38G
            if (!STRINGLIB_BLOOM(mask, ss[i+1])) {
608
1.36G
                i = i + m;
609
1.36G
            }
610
1.38G
        }
611
1.41G
    }
612
41.9k
    return mode == FAST_COUNT ? count : -1;
613
4.72M
}
bytes_methods.c:stringlib_default_find
Line
Count
Source
560
2.93k
{
561
2.93k
    const Py_ssize_t w = n - m;
562
2.93k
    Py_ssize_t mlast = m - 1, count = 0;
563
2.93k
    Py_ssize_t gap = mlast;
564
2.93k
    const STRINGLIB_CHAR last = p[mlast];
565
2.93k
    const STRINGLIB_CHAR *const ss = &s[mlast];
566
567
2.93k
    unsigned long mask = 0;
568
11.7k
    for (Py_ssize_t i = 0; i < mlast; i++) {
569
8.79k
        STRINGLIB_BLOOM_ADD(mask, p[i]);
570
8.79k
        if (p[i] == last) {
571
2.93k
            gap = mlast - i - 1;
572
2.93k
        }
573
8.79k
    }
574
2.93k
    STRINGLIB_BLOOM_ADD(mask, last);
575
576
1.39M
    for (Py_ssize_t i = 0; i <= w; i++) {
577
1.39M
        if (ss[i] == last) {
578
            /* candidate match */
579
8.63k
            Py_ssize_t j;
580
17.0k
            for (j = 0; j < mlast; j++) {
581
14.3k
                if (s[i+j] != p[j]) {
582
5.91k
                    break;
583
5.91k
                }
584
14.3k
            }
585
8.63k
            if (j == mlast) {
586
                /* got a match! */
587
2.71k
                if (mode != FAST_COUNT) {
588
2.71k
                    return i;
589
2.71k
                }
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.91k
            if (!STRINGLIB_BLOOM(mask, ss[i+1])) {
599
638
                i = i + m;
600
638
            }
601
5.27k
            else {
602
5.27k
                i = i + gap;
603
5.27k
            }
604
5.91k
        }
605
1.38M
        else {
606
            /* skip: check if next character is part of pattern */
607
1.38M
            if (!STRINGLIB_BLOOM(mask, ss[i+1])) {
608
120k
                i = i + m;
609
120k
            }
610
1.38M
        }
611
1.39M
    }
612
219
    return mode == FAST_COUNT ? count : -1;
613
2.93k
}
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 (!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 (!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
4
{
694
    /* create compressed boyer-moore delta 1 table */
695
4
    unsigned long mask = 0;
696
4
    Py_ssize_t i, j, mlast = m - 1, skip = m - 1, w = n - m;
697
698
    /* process pattern[0] outside the loop */
699
4
    STRINGLIB_BLOOM_ADD(mask, p[0]);
700
    /* process pattern[:0:-1] */
701
16
    for (i = mlast; i > 0; i--) {
702
12
        STRINGLIB_BLOOM_ADD(mask, p[i]);
703
12
        if (p[i] == p[0]) {
704
0
            skip = i - 1;
705
0
        }
706
12
    }
707
708
356
    for (i = w; i >= 0; i--) {
709
352
        if (s[i] == p[0]) {
710
            /* candidate match */
711
8
            for (j = mlast; j > 0; j--) {
712
8
                if (s[i+j] != p[j]) {
713
8
                    break;
714
8
                }
715
8
            }
716
8
            if (j == 0) {
717
                /* got a match! */
718
0
                return i;
719
0
            }
720
            /* miss: check if previous character is part of pattern */
721
8
            if (i > 0 && !STRINGLIB_BLOOM(mask, s[i-1])) {
722
8
                i = i - m;
723
8
            }
724
0
            else {
725
0
                i = i - skip;
726
0
            }
727
8
        }
728
344
        else {
729
            /* skip: check if previous character is part of pattern */
730
344
            if (i > 0 && !STRINGLIB_BLOOM(mask, s[i-1])) {
731
336
                i = i - m;
732
336
            }
733
344
        }
734
352
    }
735
4
    return -1;
736
4
}
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
4
{
694
    /* create compressed boyer-moore delta 1 table */
695
4
    unsigned long mask = 0;
696
4
    Py_ssize_t i, j, mlast = m - 1, skip = m - 1, w = n - m;
697
698
    /* process pattern[0] outside the loop */
699
4
    STRINGLIB_BLOOM_ADD(mask, p[0]);
700
    /* process pattern[:0:-1] */
701
16
    for (i = mlast; i > 0; i--) {
702
12
        STRINGLIB_BLOOM_ADD(mask, p[i]);
703
12
        if (p[i] == p[0]) {
704
0
            skip = i - 1;
705
0
        }
706
12
    }
707
708
356
    for (i = w; i >= 0; i--) {
709
352
        if (s[i] == p[0]) {
710
            /* candidate match */
711
8
            for (j = mlast; j > 0; j--) {
712
8
                if (s[i+j] != p[j]) {
713
8
                    break;
714
8
                }
715
8
            }
716
8
            if (j == 0) {
717
                /* got a match! */
718
0
                return i;
719
0
            }
720
            /* miss: check if previous character is part of pattern */
721
8
            if (i > 0 && !STRINGLIB_BLOOM(mask, s[i-1])) {
722
8
                i = i - m;
723
8
            }
724
0
            else {
725
0
                i = i - skip;
726
0
            }
727
8
        }
728
344
        else {
729
            /* skip: check if previous character is part of pattern */
730
344
            if (i > 0 && !STRINGLIB_BLOOM(mask, s[i-1])) {
731
336
                i = i - m;
732
336
            }
733
344
        }
734
352
    }
735
4
    return -1;
736
4
}
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
68.5M
{
762
68.5M
    Py_ssize_t count = 0;
763
13.7G
    for (Py_ssize_t i = 0; i < n; i++) {
764
13.6G
        if (s[i] == p0) {
765
259M
            count++;
766
259M
        }
767
13.6G
    }
768
68.5M
    return count;
769
68.5M
}
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
59.6M
{
762
59.6M
    Py_ssize_t count = 0;
763
8.95G
    for (Py_ssize_t i = 0; i < n; i++) {
764
8.89G
        if (s[i] == p0) {
765
60.8M
            count++;
766
60.8M
        }
767
8.89G
    }
768
59.6M
    return count;
769
59.6M
}
unicodeobject.c:ucs2lib_count_char_no_maxcount
Line
Count
Source
761
7.79M
{
762
7.79M
    Py_ssize_t count = 0;
763
2.56G
    for (Py_ssize_t i = 0; i < n; i++) {
764
2.55G
        if (s[i] == p0) {
765
64.4M
            count++;
766
64.4M
        }
767
2.55G
    }
768
7.79M
    return count;
769
7.79M
}
unicodeobject.c:ucs4lib_count_char_no_maxcount
Line
Count
Source
761
1.06M
{
762
1.06M
    Py_ssize_t count = 0;
763
2.24G
    for (Py_ssize_t i = 0; i < n; i++) {
764
2.24G
        if (s[i] == p0) {
765
133M
            count++;
766
133M
        }
767
2.24G
    }
768
1.06M
    return count;
769
1.06M
}
Unexecuted instantiation: bytes_methods.c:stringlib_count_char_no_maxcount
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
239M
{
777
239M
    if (n < m || (mode == FAST_COUNT && maxcount == 0)) {
778
11.1k
        return -1;
779
11.1k
    }
780
781
    /* look for special cases */
782
239M
    if (m <= 1) {
783
223M
        if (m <= 0) {
784
0
            return -1;
785
0
        }
786
        /* use special case for 1-character strings */
787
223M
        if (mode == FAST_SEARCH)
788
155M
            return STRINGLIB(find_char)(s, n, p[0]);
789
68.5M
        else if (mode == FAST_RSEARCH)
790
9.92k
            return STRINGLIB(rfind_char)(s, n, p[0]);
791
68.5M
        else {
792
68.5M
            if (maxcount == PY_SSIZE_T_MAX) {
793
68.5M
                return STRINGLIB(count_char_no_maxcount)(s, n, p[0]);
794
68.5M
            }
795
0
            return STRINGLIB(count_char)(s, n, p[0], maxcount);
796
68.5M
        }
797
223M
    }
798
799
16.2M
    if (mode != FAST_RSEARCH) {
800
16.2M
        if (n < 2500 || (m < 100 && n < 30000) || m < 6) {
801
16.2M
            return STRINGLIB(default_find)(s, n, p, m, maxcount, mode);
802
16.2M
        }
803
19
        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
19
            if (mode == FAST_SEARCH) {
810
19
                return STRINGLIB(_two_way_find)(s, n, p, m);
811
19
            }
812
0
            else {
813
0
                return STRINGLIB(_two_way_count)(s, n, p, m, maxcount);
814
0
            }
815
19
        }
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
16.2M
    }
825
4
    else {
826
        /* FAST_RSEARCH */
827
4
        return STRINGLIB(default_rfind)(s, n, p, m, maxcount, mode);
828
4
    }
829
16.2M
}
Unexecuted instantiation: bytesobject.c:fastsearch
unicodeobject.c:asciilib_fastsearch
Line
Count
Source
776
19.7M
{
777
19.7M
    if (n < m || (mode == FAST_COUNT && maxcount == 0)) {
778
0
        return -1;
779
0
    }
780
781
    /* look for special cases */
782
19.7M
    if (m <= 1) {
783
17.8M
        if (m <= 0) {
784
0
            return -1;
785
0
        }
786
        /* use special case for 1-character strings */
787
17.8M
        if (mode == FAST_SEARCH)
788
17.8M
            return STRINGLIB(find_char)(s, n, p[0]);
789
9.92k
        else if (mode == FAST_RSEARCH)
790
9.92k
            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
17.8M
    }
798
799
1.92M
    if (mode != FAST_RSEARCH) {
800
1.92M
        if (n < 2500 || (m < 100 && n < 30000) || m < 6) {
801
1.92M
            return STRINGLIB(default_find)(s, n, p, m, maxcount, mode);
802
1.92M
        }
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
1.92M
    }
825
0
    else {
826
        /* FAST_RSEARCH */
827
0
        return STRINGLIB(default_rfind)(s, n, p, m, maxcount, mode);
828
0
    }
829
1.92M
}
unicodeobject.c:ucs1lib_fastsearch
Line
Count
Source
776
71.8M
{
777
71.8M
    if (n < m || (mode == FAST_COUNT && maxcount == 0)) {
778
0
        return -1;
779
0
    }
780
781
    /* look for special cases */
782
71.8M
    if (m <= 1) {
783
66.3M
        if (m <= 0) {
784
0
            return -1;
785
0
        }
786
        /* use special case for 1-character strings */
787
66.3M
        if (mode == FAST_SEARCH)
788
6.66M
            return STRINGLIB(find_char)(s, n, p[0]);
789
59.6M
        else if (mode == FAST_RSEARCH)
790
0
            return STRINGLIB(rfind_char)(s, n, p[0]);
791
59.6M
        else {
792
59.6M
            if (maxcount == PY_SSIZE_T_MAX) {
793
59.6M
                return STRINGLIB(count_char_no_maxcount)(s, n, p[0]);
794
59.6M
            }
795
0
            return STRINGLIB(count_char)(s, n, p[0], maxcount);
796
59.6M
        }
797
66.3M
    }
798
799
5.57M
    if (mode != FAST_RSEARCH) {
800
5.57M
        if (n < 2500 || (m < 100 && n < 30000) || m < 6) {
801
5.57M
            return STRINGLIB(default_find)(s, n, p, m, maxcount, mode);
802
5.57M
        }
803
19
        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
19
            if (mode == FAST_SEARCH) {
810
19
                return STRINGLIB(_two_way_find)(s, n, p, m);
811
19
            }
812
0
            else {
813
0
                return STRINGLIB(_two_way_count)(s, n, p, m, maxcount);
814
0
            }
815
19
        }
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
5.57M
    }
825
0
    else {
826
        /* FAST_RSEARCH */
827
0
        return STRINGLIB(default_rfind)(s, n, p, m, maxcount, mode);
828
0
    }
829
5.57M
}
unicodeobject.c:ucs2lib_fastsearch
Line
Count
Source
776
62.5M
{
777
62.5M
    if (n < m || (mode == FAST_COUNT && maxcount == 0)) {
778
11.1k
        return -1;
779
11.1k
    }
780
781
    /* look for special cases */
782
62.5M
    if (m <= 1) {
783
58.5M
        if (m <= 0) {
784
0
            return -1;
785
0
        }
786
        /* use special case for 1-character strings */
787
58.5M
        if (mode == FAST_SEARCH)
788
50.7M
            return STRINGLIB(find_char)(s, n, p[0]);
789
7.79M
        else if (mode == FAST_RSEARCH)
790
0
            return STRINGLIB(rfind_char)(s, n, p[0]);
791
7.79M
        else {
792
7.79M
            if (maxcount == PY_SSIZE_T_MAX) {
793
7.79M
                return STRINGLIB(count_char_no_maxcount)(s, n, p[0]);
794
7.79M
            }
795
0
            return STRINGLIB(count_char)(s, n, p[0], maxcount);
796
7.79M
        }
797
58.5M
    }
798
799
4.01M
    if (mode != FAST_RSEARCH) {
800
4.01M
        if (n < 2500 || (m < 100 && n < 30000) || m < 6) {
801
4.01M
            return STRINGLIB(default_find)(s, n, p, m, maxcount, mode);
802
4.01M
        }
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.01M
    }
825
0
    else {
826
        /* FAST_RSEARCH */
827
0
        return STRINGLIB(default_rfind)(s, n, p, m, maxcount, mode);
828
0
    }
829
4.01M
}
unicodeobject.c:ucs4lib_fastsearch
Line
Count
Source
776
85.6M
{
777
85.6M
    if (n < m || (mode == FAST_COUNT && maxcount == 0)) {
778
0
        return -1;
779
0
    }
780
781
    /* look for special cases */
782
85.6M
    if (m <= 1) {
783
80.8M
        if (m <= 0) {
784
0
            return -1;
785
0
        }
786
        /* use special case for 1-character strings */
787
80.8M
        if (mode == FAST_SEARCH)
788
79.8M
            return STRINGLIB(find_char)(s, n, p[0]);
789
1.06M
        else if (mode == FAST_RSEARCH)
790
0
            return STRINGLIB(rfind_char)(s, n, p[0]);
791
1.06M
        else {
792
1.06M
            if (maxcount == PY_SSIZE_T_MAX) {
793
1.06M
                return STRINGLIB(count_char_no_maxcount)(s, n, p[0]);
794
1.06M
            }
795
0
            return STRINGLIB(count_char)(s, n, p[0], maxcount);
796
1.06M
        }
797
80.8M
    }
798
799
4.72M
    if (mode != FAST_RSEARCH) {
800
4.72M
        if (n < 2500 || (m < 100 && n < 30000) || m < 6) {
801
4.72M
            return STRINGLIB(default_find)(s, n, p, m, maxcount, mode);
802
4.72M
        }
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.72M
    }
825
0
    else {
826
        /* FAST_RSEARCH */
827
0
        return STRINGLIB(default_rfind)(s, n, p, m, maxcount, mode);
828
0
    }
829
4.72M
}
bytes_methods.c:fastsearch
Line
Count
Source
776
2.94k
{
777
2.94k
    if (n < m || (mode == FAST_COUNT && maxcount == 0)) {
778
9
        return -1;
779
9
    }
780
781
    /* look for special cases */
782
2.93k
    if (m <= 1) {
783
0
        if (m <= 0) {
784
0
            return -1;
785
0
        }
786
        /* use special case for 1-character strings */
787
0
        if (mode == FAST_SEARCH)
788
0
            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
0
    }
798
799
2.93k
    if (mode != FAST_RSEARCH) {
800
2.93k
        if (n < 2500 || (m < 100 && n < 30000) || m < 6) {
801
2.93k
            return STRINGLIB(default_find)(s, n, p, m, maxcount, mode);
802
2.93k
        }
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.93k
    }
825
4
    else {
826
        /* FAST_RSEARCH */
827
4
        return STRINGLIB(default_rfind)(s, n, p, m, maxcount, mode);
828
4
    }
829
2.93k
}
Unexecuted instantiation: bytearrayobject.c:fastsearch
830