Coverage Report

Created: 2025-07-18 06:09

/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
546M
#define FAST_COUNT 0
25
369M
#define FAST_SEARCH 1
26
77.8M
#define FAST_RSEARCH 2
27
28
#if LONG_BIT >= 128
29
#define STRINGLIB_BLOOM_WIDTH 128
30
#elif LONG_BIT >= 64
31
5.44G
#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.2M
    ((mask |= (1UL << ((ch) & (STRINGLIB_BLOOM_WIDTH -1)))))
40
#define STRINGLIB_BLOOM(mask, ch)     \
41
5.41G
    ((mask &  (1UL << ((ch) & (STRINGLIB_BLOOM_WIDTH -1)))))
42
43
#ifdef STRINGLIB_FAST_MEMCHR
44
200M
#  define MEMCHR_CUT_OFF 15
45
#else
46
60.3M
#  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
261M
{
52
261M
    const STRINGLIB_CHAR *p, *e;
53
54
261M
    p = s;
55
261M
    e = s + n;
56
261M
    if (n > MEMCHR_CUT_OFF) {
57
#ifdef STRINGLIB_FAST_MEMCHR
58
102M
        p = STRINGLIB_FAST_MEMCHR(s, ch, n);
59
102M
        if (p != NULL)
60
100M
            return (p - s);
61
1.62M
        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
53.3M
        if (needle != 0) {
71
53.1M
            do {
72
53.1M
                void *candidate = memchr(p, needle,
73
53.1M
                                         (e - p) * sizeof(STRINGLIB_CHAR));
74
53.1M
                if (candidate == NULL)
75
354k
                    return -1;
76
52.8M
                s1 = p;
77
52.8M
                p = (const STRINGLIB_CHAR *)
78
52.8M
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
79
52.8M
                if (*p == ch)
80
52.7M
                    return (p - s);
81
                /* False positive */
82
54.4k
                p++;
83
54.4k
                if (p - s1 > MEMCHR_CUT_OFF)
84
24.1k
                    continue;
85
30.2k
                if (e - p <= MEMCHR_CUT_OFF)
86
3.26k
                    break;
87
27.0k
                e1 = p + MEMCHR_CUT_OFF;
88
920k
                while (p != e1) {
89
900k
                    if (*p == ch)
90
6.55k
                        return (p - s);
91
893k
                    p++;
92
893k
                }
93
27.0k
            }
94
53.1M
            while (e - p > MEMCHR_CUT_OFF);
95
53.1M
        }
96
#endif
97
155M
    }
98
398M
    while (p < e) {
99
306M
        if (*p == ch)
100
13.8M
            return (p - s);
101
293M
        p++;
102
293M
    }
103
91.7M
    return -1;
104
105M
}
Unexecuted instantiation: bytesobject.c:stringlib_find_char
unicodeobject.c:ucs1lib_find_char
Line
Count
Source
51
112M
{
52
112M
    const STRINGLIB_CHAR *p, *e;
53
54
112M
    p = s;
55
112M
    e = s + n;
56
112M
    if (n > MEMCHR_CUT_OFF) {
57
18.7M
#ifdef STRINGLIB_FAST_MEMCHR
58
18.7M
        p = STRINGLIB_FAST_MEMCHR(s, ch, n);
59
18.7M
        if (p != NULL)
60
17.9M
            return (p - s);
61
834k
        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
18.7M
    }
98
264M
    while (p < e) {
99
174M
        if (*p == ch)
100
4.09M
            return (p - s);
101
170M
        p++;
102
170M
    }
103
89.5M
    return -1;
104
93.6M
}
unicodeobject.c:ucs2lib_find_char
Line
Count
Source
51
60.2M
{
52
60.2M
    const STRINGLIB_CHAR *p, *e;
53
54
60.2M
    p = s;
55
60.2M
    e = s + n;
56
60.2M
    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
53.3M
        const STRINGLIB_CHAR *s1, *e1;
66
53.3M
        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
53.3M
        if (needle != 0) {
71
53.1M
            do {
72
53.1M
                void *candidate = memchr(p, needle,
73
53.1M
                                         (e - p) * sizeof(STRINGLIB_CHAR));
74
53.1M
                if (candidate == NULL)
75
354k
                    return -1;
76
52.8M
                s1 = p;
77
52.8M
                p = (const STRINGLIB_CHAR *)
78
52.8M
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
79
52.8M
                if (*p == ch)
80
52.7M
                    return (p - s);
81
                /* False positive */
82
54.4k
                p++;
83
54.4k
                if (p - s1 > MEMCHR_CUT_OFF)
84
24.1k
                    continue;
85
30.2k
                if (e - p <= MEMCHR_CUT_OFF)
86
3.26k
                    break;
87
27.0k
                e1 = p + MEMCHR_CUT_OFF;
88
920k
                while (p != e1) {
89
900k
                    if (*p == ch)
90
6.55k
                        return (p - s);
91
893k
                    p++;
92
893k
                }
93
27.0k
            }
94
53.1M
            while (e - p > MEMCHR_CUT_OFF);
95
53.1M
        }
96
53.3M
#endif
97
53.3M
    }
98
119M
    while (p < e) {
99
117M
        if (*p == ch)
100
4.93M
            return (p - s);
101
112M
        p++;
102
112M
    }
103
2.15M
    return -1;
104
7.08M
}
unicodeobject.c:ucs4lib_find_char
Line
Count
Source
51
71.5M
{
52
71.5M
    const STRINGLIB_CHAR *p, *e;
53
54
71.5M
    p = s;
55
71.5M
    e = s + n;
56
71.5M
    if (n > MEMCHR_CUT_OFF) {
57
71.4M
#ifdef STRINGLIB_FAST_MEMCHR
58
71.4M
        p = STRINGLIB_FAST_MEMCHR(s, ch, n);
59
71.4M
        if (p != NULL)
60
71.4M
            return (p - s);
61
31.4k
        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
71.4M
    }
98
275k
    while (p < e) {
99
243k
        if (*p == ch)
100
27.5k
            return (p - s);
101
216k
        p++;
102
216k
    }
103
31.8k
    return -1;
104
59.3k
}
unicodeobject.c:asciilib_find_char
Line
Count
Source
51
16.9M
{
52
16.9M
    const STRINGLIB_CHAR *p, *e;
53
54
16.9M
    p = s;
55
16.9M
    e = s + n;
56
16.9M
    if (n > MEMCHR_CUT_OFF) {
57
12.1M
#ifdef STRINGLIB_FAST_MEMCHR
58
12.1M
        p = STRINGLIB_FAST_MEMCHR(s, ch, n);
59
12.1M
        if (p != NULL)
60
11.3M
            return (p - s);
61
756k
        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.1M
    }
98
14.3M
    while (p < e) {
99
14.3M
        if (*p == ch)
100
4.75M
            return (p - s);
101
9.54M
        p++;
102
9.54M
    }
103
56.0k
    return -1;
104
4.81M
}
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
36.3k
#  define MEMRCHR_CUT_OFF 15
110
#else
111
231k
#  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
234k
{
118
234k
    const STRINGLIB_CHAR *p;
119
234k
#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
234k
    if (n > MEMRCHR_CUT_OFF) {
126
#if STRINGLIB_SIZEOF_CHAR == 1
127
        p = memrchr(s, ch, n);
128
10.8k
        if (p != NULL)
129
7.01k
            return (p - s);
130
3.80k
        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
94.1k
        if (needle != 0) {
141
101k
            do {
142
101k
                void *candidate = memrchr(s, needle,
143
101k
                                          n * sizeof(STRINGLIB_CHAR));
144
101k
                if (candidate == NULL)
145
677
                    return -1;
146
100k
                n1 = n;
147
100k
                p = (const STRINGLIB_CHAR *)
148
100k
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
149
100k
                n = p - s;
150
100k
                if (*p == ch)
151
89.0k
                    return n;
152
                /* False positive */
153
11.4k
                if (n1 - n > MEMRCHR_CUT_OFF)
154
4.49k
                    continue;
155
6.93k
                if (n <= MEMRCHR_CUT_OFF)
156
1.03k
                    break;
157
5.90k
                s1 = p - MEMRCHR_CUT_OFF;
158
204k
                while (p > s1) {
159
199k
                    p--;
160
199k
                    if (*p == ch)
161
1.54k
                        return (p - s);
162
199k
                }
163
4.35k
                n = p - s;
164
4.35k
            }
165
94.1k
            while (n > MEMRCHR_CUT_OFF);
166
94.1k
        }
167
#endif
168
104k
    }
169
132k
#endif  /* HAVE_MEMRCHR */
170
132k
    p = s + n;
171
669k
    while (p > s) {
172
652k
        p--;
173
652k
        if (*p == ch)
174
115k
            return (p - s);
175
652k
    }
176
16.4k
    return -1;
177
132k
}
Unexecuted instantiation: bytesobject.c:stringlib_rfind_char
unicodeobject.c:ucs1lib_rfind_char
Line
Count
Source
117
7.35k
{
118
7.35k
    const STRINGLIB_CHAR *p;
119
7.35k
#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
7.35k
    if (n > MEMRCHR_CUT_OFF) {
126
3.81k
#if STRINGLIB_SIZEOF_CHAR == 1
127
3.81k
        p = memrchr(s, ch, n);
128
3.81k
        if (p != NULL)
129
2.86k
            return (p - s);
130
946
        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.81k
    }
169
3.54k
#endif  /* HAVE_MEMRCHR */
170
3.54k
    p = s + n;
171
11.4k
    while (p > s) {
172
10.6k
        p--;
173
10.6k
        if (*p == ch)
174
2.78k
            return (p - s);
175
10.6k
    }
176
759
    return -1;
177
3.54k
}
unicodeobject.c:ucs2lib_rfind_char
Line
Count
Source
117
44.2k
{
118
44.2k
    const STRINGLIB_CHAR *p;
119
44.2k
#ifdef HAVE_MEMRCHR
120
    /* memrchr() is a GNU extension, available since glibc 2.1.91.  it
121
       doesn't seem as optimized as memchr(), but is still quite
122
       faster than our hand-written loop below. There is no wmemrchr
123
       for 4-byte chars. */
124
125
44.2k
    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
17.3k
        const STRINGLIB_CHAR *s1;
135
17.3k
        Py_ssize_t n1;
136
17.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
17.3k
        if (needle != 0) {
141
19.0k
            do {
142
19.0k
                void *candidate = memrchr(s, needle,
143
19.0k
                                          n * sizeof(STRINGLIB_CHAR));
144
19.0k
                if (candidate == NULL)
145
387
                    return -1;
146
18.6k
                n1 = n;
147
18.6k
                p = (const STRINGLIB_CHAR *)
148
18.6k
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
149
18.6k
                n = p - s;
150
18.6k
                if (*p == ch)
151
15.3k
                    return n;
152
                /* False positive */
153
3.32k
                if (n1 - n > MEMRCHR_CUT_OFF)
154
1.21k
                    continue;
155
2.10k
                if (n <= MEMRCHR_CUT_OFF)
156
486
                    break;
157
1.62k
                s1 = p - MEMRCHR_CUT_OFF;
158
60.0k
                while (p > s1) {
159
58.6k
                    p--;
160
58.6k
                    if (*p == ch)
161
230
                        return (p - s);
162
58.6k
                }
163
1.39k
                n = p - s;
164
1.39k
            }
165
17.3k
            while (n > MEMRCHR_CUT_OFF);
166
17.3k
        }
167
17.3k
#endif
168
17.3k
    }
169
28.3k
#endif  /* HAVE_MEMRCHR */
170
28.3k
    p = s + n;
171
158k
    while (p > s) {
172
157k
        p--;
173
157k
        if (*p == ch)
174
26.5k
            return (p - s);
175
157k
    }
176
1.72k
    return -1;
177
28.3k
}
unicodeobject.c:ucs4lib_rfind_char
Line
Count
Source
117
153k
{
118
153k
    const STRINGLIB_CHAR *p;
119
153k
#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
153k
    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
76.7k
        const STRINGLIB_CHAR *s1;
135
76.7k
        Py_ssize_t n1;
136
76.7k
        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
76.7k
        if (needle != 0) {
141
82.0k
            do {
142
82.0k
                void *candidate = memrchr(s, needle,
143
82.0k
                                          n * sizeof(STRINGLIB_CHAR));
144
82.0k
                if (candidate == NULL)
145
290
                    return -1;
146
81.7k
                n1 = n;
147
81.7k
                p = (const STRINGLIB_CHAR *)
148
81.7k
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
149
81.7k
                n = p - s;
150
81.7k
                if (*p == ch)
151
73.6k
                    return n;
152
                /* False positive */
153
8.10k
                if (n1 - n > MEMRCHR_CUT_OFF)
154
3.28k
                    continue;
155
4.82k
                if (n <= MEMRCHR_CUT_OFF)
156
546
                    break;
157
4.27k
                s1 = p - MEMRCHR_CUT_OFF;
158
144k
                while (p > s1) {
159
141k
                    p--;
160
141k
                    if (*p == ch)
161
1.31k
                        return (p - s);
162
141k
                }
163
2.96k
                n = p - s;
164
2.96k
            }
165
76.7k
            while (n > MEMRCHR_CUT_OFF);
166
76.7k
        }
167
76.7k
#endif
168
76.7k
    }
169
78.4k
#endif  /* HAVE_MEMRCHR */
170
78.4k
    p = s + n;
171
378k
    while (p > s) {
172
376k
        p--;
173
376k
        if (*p == ch)
174
77.1k
            return (p - s);
175
376k
    }
176
1.33k
    return -1;
177
78.4k
}
unicodeobject.c:asciilib_rfind_char
Line
Count
Source
117
9.64k
{
118
9.64k
    const STRINGLIB_CHAR *p;
119
9.64k
#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.64k
    if (n > MEMRCHR_CUT_OFF) {
126
3.36k
#if STRINGLIB_SIZEOF_CHAR == 1
127
3.36k
        p = memrchr(s, ch, n);
128
3.36k
        if (p != NULL)
129
3.28k
            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.36k
    }
169
6.28k
#endif  /* HAVE_MEMRCHR */
170
6.28k
    p = s + n;
171
35.0k
    while (p > s) {
172
33.0k
        p--;
173
33.0k
        if (*p == ch)
174
4.28k
            return (p - s);
175
33.0k
    }
176
2.00k
    return -1;
177
6.28k
}
bytes_methods.c:stringlib_rfind_char
Line
Count
Source
117
19.3k
{
118
19.3k
    const STRINGLIB_CHAR *p;
119
19.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
19.3k
    if (n > MEMRCHR_CUT_OFF) {
126
3.63k
#if STRINGLIB_SIZEOF_CHAR == 1
127
3.63k
        p = memrchr(s, ch, n);
128
3.63k
        if (p != NULL)
129
863
            return (p - s);
130
2.77k
        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.63k
    }
169
15.7k
#endif  /* HAVE_MEMRCHR */
170
15.7k
    p = s + n;
171
85.4k
    while (p > s) {
172
74.8k
        p--;
173
74.8k
        if (*p == ch)
174
5.13k
            return (p - s);
175
74.8k
    }
176
10.5k
    return -1;
177
15.7k
}
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
40
{
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
40
    Py_ssize_t max_suffix = 0;
204
40
    Py_ssize_t candidate = 1;
205
40
    Py_ssize_t k = 0;
206
    // The period of the right half.
207
40
    Py_ssize_t period = 1;
208
209
400
    while (candidate + k < len_needle) {
210
        // each loop increases candidate + k + max_suffix
211
360
        STRINGLIB_CHAR a = needle[candidate + k];
212
360
        STRINGLIB_CHAR b = needle[max_suffix + k];
213
        // check if the suffix at candidate is better than max_suffix
214
360
        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
260
            candidate += k + 1;
219
260
            k = 0;
220
            // We've ruled out any period smaller than what's
221
            // been scanned since max_suffix.
222
260
            period = candidate - max_suffix;
223
260
        }
224
100
        else if (a == b) {
225
20
            if (k + 1 != period) {
226
                // Keep scanning the equal strings
227
0
                k++;
228
0
            }
229
20
            else {
230
                // Matched a whole period.
231
                // Start matching the next period.
232
20
                candidate += period;
233
20
                k = 0;
234
20
            }
235
20
        }
236
80
        else {
237
            // Did better than max_suffix, so replace it.
238
80
            max_suffix = candidate;
239
80
            candidate++;
240
80
            k = 0;
241
80
            period = 1;
242
80
        }
243
360
    }
244
40
    *return_period = period;
245
40
    return max_suffix;
246
40
}
Unexecuted instantiation: bytesobject.c:stringlib__lex_search
Unexecuted instantiation: unicodeobject.c:asciilib__lex_search
unicodeobject.c:ucs1lib__lex_search
Line
Count
Source
199
40
{
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
40
    Py_ssize_t max_suffix = 0;
204
40
    Py_ssize_t candidate = 1;
205
40
    Py_ssize_t k = 0;
206
    // The period of the right half.
207
40
    Py_ssize_t period = 1;
208
209
400
    while (candidate + k < len_needle) {
210
        // each loop increases candidate + k + max_suffix
211
360
        STRINGLIB_CHAR a = needle[candidate + k];
212
360
        STRINGLIB_CHAR b = needle[max_suffix + k];
213
        // check if the suffix at candidate is better than max_suffix
214
360
        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
260
            candidate += k + 1;
219
260
            k = 0;
220
            // We've ruled out any period smaller than what's
221
            // been scanned since max_suffix.
222
260
            period = candidate - max_suffix;
223
260
        }
224
100
        else if (a == b) {
225
20
            if (k + 1 != period) {
226
                // Keep scanning the equal strings
227
0
                k++;
228
0
            }
229
20
            else {
230
                // Matched a whole period.
231
                // Start matching the next period.
232
20
                candidate += period;
233
20
                k = 0;
234
20
            }
235
20
        }
236
80
        else {
237
            // Did better than max_suffix, so replace it.
238
80
            max_suffix = candidate;
239
80
            candidate++;
240
80
            k = 0;
241
80
            period = 1;
242
80
        }
243
360
    }
244
40
    *return_period = period;
245
40
    return max_suffix;
246
40
}
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
20
{
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
20
    Py_ssize_t cut1, period1, cut2, period2, cut, period;
286
20
    cut1 = STRINGLIB(_lex_search)(needle, len_needle, &period1, 0);
287
20
    cut2 = STRINGLIB(_lex_search)(needle, len_needle, &period2, 1);
288
289
    // Take the later cut.
290
20
    if (cut1 > cut2) {
291
20
        period = period1;
292
20
        cut = cut1;
293
20
    }
294
0
    else {
295
0
        period = period2;
296
0
        cut = cut2;
297
0
    }
298
299
20
    LOG("split: "); LOG_STRING(needle, cut);
300
20
    LOG(" + "); LOG_STRING(needle + cut, len_needle - cut);
301
20
    LOG("\n");
302
303
20
    *return_period = period;
304
20
    return cut;
305
20
}
Unexecuted instantiation: bytesobject.c:stringlib__factorize
Unexecuted instantiation: unicodeobject.c:asciilib__factorize
unicodeobject.c:ucs1lib__factorize
Line
Count
Source
252
20
{
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
20
    Py_ssize_t cut1, period1, cut2, period2, cut, period;
286
20
    cut1 = STRINGLIB(_lex_search)(needle, len_needle, &period1, 0);
287
20
    cut2 = STRINGLIB(_lex_search)(needle, len_needle, &period2, 1);
288
289
    // Take the later cut.
290
20
    if (cut1 > cut2) {
291
20
        period = period1;
292
20
        cut = cut1;
293
20
    }
294
0
    else {
295
0
        period = period2;
296
0
        cut = cut2;
297
0
    }
298
299
20
    LOG("split: "); LOG_STRING(needle, cut);
300
20
    LOG(" + "); LOG_STRING(needle + cut, len_needle - cut);
301
20
    LOG("\n");
302
303
20
    *return_period = period;
304
20
    return cut;
305
20
}
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
220
#define SHIFT_TYPE uint8_t
309
#define MAX_SHIFT UINT8_MAX
310
311
69.2k
#define TABLE_SIZE_BITS 6u
312
69.2k
#define TABLE_SIZE (1U << TABLE_SIZE_BITS)
313
67.9k
#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
20
{
330
20
    p->needle = needle;
331
20
    p->len_needle = len_needle;
332
20
    p->cut = STRINGLIB(_factorize)(needle, len_needle, &(p->period));
333
20
    assert(p->period + p->cut <= len_needle);
334
20
    p->is_periodic = (0 == memcmp(needle,
335
20
                                  needle + p->period,
336
20
                                  p->cut * STRINGLIB_SIZEOF_CHAR));
337
20
    if (p->is_periodic) {
338
0
        assert(p->cut <= len_needle/2);
339
0
        assert(p->cut < p->period);
340
0
    }
341
20
    else {
342
        // A lower bound on the period
343
20
        p->period = Py_MAX(p->cut, len_needle - p->cut) + 1;
344
20
    }
345
    // The gap between the last character and the previous
346
    // occurrence of an equivalent character (modulo TABLE_SIZE)
347
20
    p->gap = len_needle;
348
20
    STRINGLIB_CHAR last = needle[len_needle - 1] & TABLE_MASK;
349
140
    for (Py_ssize_t i = len_needle - 2; i >= 0; i--) {
350
140
        STRINGLIB_CHAR x = needle[i] & TABLE_MASK;
351
140
        if (x == last) {
352
20
            p->gap = len_needle - 1 - i;
353
20
            break;
354
20
        }
355
140
    }
356
    // Fill up a compressed Boyer-Moore "Bad Character" table
357
20
    Py_ssize_t not_found_shift = Py_MIN(len_needle, MAX_SHIFT);
358
1.30k
    for (Py_ssize_t i = 0; i < (Py_ssize_t)TABLE_SIZE; i++) {
359
1.28k
        p->table[i] = Py_SAFE_DOWNCAST(not_found_shift,
360
1.28k
                                       Py_ssize_t, SHIFT_TYPE);
361
1.28k
    }
362
220
    for (Py_ssize_t i = len_needle - not_found_shift; i < len_needle; i++) {
363
200
        SHIFT_TYPE shift = Py_SAFE_DOWNCAST(len_needle - 1 - i,
364
200
                                            Py_ssize_t, SHIFT_TYPE);
365
200
        p->table[needle[i] & TABLE_MASK] = shift;
366
200
    }
367
20
}
Unexecuted instantiation: bytesobject.c:stringlib__preprocess
Unexecuted instantiation: unicodeobject.c:asciilib__preprocess
unicodeobject.c:ucs1lib__preprocess
Line
Count
Source
329
20
{
330
20
    p->needle = needle;
331
20
    p->len_needle = len_needle;
332
20
    p->cut = STRINGLIB(_factorize)(needle, len_needle, &(p->period));
333
20
    assert(p->period + p->cut <= len_needle);
334
20
    p->is_periodic = (0 == memcmp(needle,
335
20
                                  needle + p->period,
336
20
                                  p->cut * STRINGLIB_SIZEOF_CHAR));
337
20
    if (p->is_periodic) {
338
0
        assert(p->cut <= len_needle/2);
339
0
        assert(p->cut < p->period);
340
0
    }
341
20
    else {
342
        // A lower bound on the period
343
20
        p->period = Py_MAX(p->cut, len_needle - p->cut) + 1;
344
20
    }
345
    // The gap between the last character and the previous
346
    // occurrence of an equivalent character (modulo TABLE_SIZE)
347
20
    p->gap = len_needle;
348
20
    STRINGLIB_CHAR last = needle[len_needle - 1] & TABLE_MASK;
349
140
    for (Py_ssize_t i = len_needle - 2; i >= 0; i--) {
350
140
        STRINGLIB_CHAR x = needle[i] & TABLE_MASK;
351
140
        if (x == last) {
352
20
            p->gap = len_needle - 1 - i;
353
20
            break;
354
20
        }
355
140
    }
356
    // Fill up a compressed Boyer-Moore "Bad Character" table
357
20
    Py_ssize_t not_found_shift = Py_MIN(len_needle, MAX_SHIFT);
358
1.30k
    for (Py_ssize_t i = 0; i < (Py_ssize_t)TABLE_SIZE; i++) {
359
1.28k
        p->table[i] = Py_SAFE_DOWNCAST(not_found_shift,
360
1.28k
                                       Py_ssize_t, SHIFT_TYPE);
361
1.28k
    }
362
220
    for (Py_ssize_t i = len_needle - not_found_shift; i < len_needle; i++) {
363
200
        SHIFT_TYPE shift = Py_SAFE_DOWNCAST(len_needle - 1 - i,
364
200
                                            Py_ssize_t, SHIFT_TYPE);
365
200
        p->table[needle[i] & TABLE_MASK] = shift;
366
200
    }
367
20
}
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
20
{
373
    // Crochemore and Perrin's (1991) Two-Way algorithm.
374
    // See http://www-igm.univ-mlv.fr/~lecroq/string/node26.html#SECTION00260
375
20
    const Py_ssize_t len_needle = p->len_needle;
376
20
    const Py_ssize_t cut = p->cut;
377
20
    Py_ssize_t period = p->period;
378
20
    const STRINGLIB_CHAR *const needle = p->needle;
379
20
    const STRINGLIB_CHAR *window_last = haystack + len_needle - 1;
380
20
    const STRINGLIB_CHAR *const haystack_end = haystack + len_haystack;
381
20
    SHIFT_TYPE *table = p->table;
382
20
    const STRINGLIB_CHAR *window;
383
20
    LOG("===== Two-way: \"%s\" in \"%s\". =====\n", needle, haystack);
384
385
20
    Py_ssize_t gap = p->gap;
386
20
    Py_ssize_t gap_jump_end = Py_MIN(len_needle, cut + gap);
387
20
    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
20
    else {
454
20
        period = Py_MAX(gap, period);
455
20
        LOG("Needle is not periodic.\n");
456
12.2k
      windowloop:
457
12.2k
        while (window_last < haystack_end) {
458
67.5k
            for (;;) {
459
67.5k
                LOG_LINEUP();
460
67.5k
                Py_ssize_t shift = table[(*window_last) & TABLE_MASK];
461
67.5k
                window_last += shift;
462
67.5k
                if (shift == 0) {
463
12.2k
                    break;
464
12.2k
                }
465
55.3k
                if (window_last >= haystack_end) {
466
17
                    return -1;
467
17
                }
468
55.3k
                LOG("Horspool skip\n");
469
55.3k
            }
470
12.2k
            window = window_last - len_needle + 1;
471
12.2k
            assert((window[len_needle - 1] & TABLE_MASK) ==
472
12.2k
                   (needle[len_needle - 1] & TABLE_MASK));
473
12.2k
            Py_ssize_t i = cut;
474
12.4k
            for (; i < len_needle; i++) {
475
12.3k
                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.3k
            }
489
107
            for (Py_ssize_t i = 0; i < cut; i++) {
490
105
                if (needle[i] != window[i]) {
491
89
                    LOG("Left half does not match.\n");
492
89
                    window_last += period;
493
89
                    goto windowloop;
494
89
                }
495
105
            }
496
2
            LOG("Found a match!\n");
497
2
            return window - haystack;
498
91
        }
499
12.2k
    }
500
1
    LOG("Not found. Returning -1.\n");
501
1
    return -1;
502
20
}
Unexecuted instantiation: bytesobject.c:stringlib__two_way
Unexecuted instantiation: unicodeobject.c:asciilib__two_way
unicodeobject.c:ucs1lib__two_way
Line
Count
Source
372
20
{
373
    // Crochemore and Perrin's (1991) Two-Way algorithm.
374
    // See http://www-igm.univ-mlv.fr/~lecroq/string/node26.html#SECTION00260
375
20
    const Py_ssize_t len_needle = p->len_needle;
376
20
    const Py_ssize_t cut = p->cut;
377
20
    Py_ssize_t period = p->period;
378
20
    const STRINGLIB_CHAR *const needle = p->needle;
379
20
    const STRINGLIB_CHAR *window_last = haystack + len_needle - 1;
380
20
    const STRINGLIB_CHAR *const haystack_end = haystack + len_haystack;
381
20
    SHIFT_TYPE *table = p->table;
382
20
    const STRINGLIB_CHAR *window;
383
20
    LOG("===== Two-way: \"%s\" in \"%s\". =====\n", needle, haystack);
384
385
20
    Py_ssize_t gap = p->gap;
386
20
    Py_ssize_t gap_jump_end = Py_MIN(len_needle, cut + gap);
387
20
    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
20
    else {
454
20
        period = Py_MAX(gap, period);
455
20
        LOG("Needle is not periodic.\n");
456
12.2k
      windowloop:
457
12.2k
        while (window_last < haystack_end) {
458
67.5k
            for (;;) {
459
67.5k
                LOG_LINEUP();
460
67.5k
                Py_ssize_t shift = table[(*window_last) & TABLE_MASK];
461
67.5k
                window_last += shift;
462
67.5k
                if (shift == 0) {
463
12.2k
                    break;
464
12.2k
                }
465
55.3k
                if (window_last >= haystack_end) {
466
17
                    return -1;
467
17
                }
468
55.3k
                LOG("Horspool skip\n");
469
55.3k
            }
470
12.2k
            window = window_last - len_needle + 1;
471
12.2k
            assert((window[len_needle - 1] & TABLE_MASK) ==
472
12.2k
                   (needle[len_needle - 1] & TABLE_MASK));
473
12.2k
            Py_ssize_t i = cut;
474
12.4k
            for (; i < len_needle; i++) {
475
12.3k
                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.3k
            }
489
107
            for (Py_ssize_t i = 0; i < cut; i++) {
490
105
                if (needle[i] != window[i]) {
491
89
                    LOG("Left half does not match.\n");
492
89
                    window_last += period;
493
89
                    goto windowloop;
494
89
                }
495
105
            }
496
2
            LOG("Found a match!\n");
497
2
            return window - haystack;
498
91
        }
499
12.2k
    }
500
1
    LOG("Not found. Returning -1.\n");
501
1
    return -1;
502
20
}
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
20
{
511
20
    LOG("###### Finding \"%s\" in \"%s\".\n", needle, haystack);
512
20
    STRINGLIB(prework) p;
513
20
    STRINGLIB(_preprocess)(needle, len_needle, &p);
514
20
    return STRINGLIB(_two_way)(haystack, len_haystack, &p);
515
20
}
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
20
{
511
20
    LOG("###### Finding \"%s\" in \"%s\".\n", needle, haystack);
512
20
    STRINGLIB(prework) p;
513
20
    STRINGLIB(_preprocess)(needle, len_needle, &p);
514
20
    return STRINGLIB(_two_way)(haystack, len_haystack, &p);
515
20
}
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.0M
{
561
16.0M
    const Py_ssize_t w = n - m;
562
16.0M
    Py_ssize_t mlast = m - 1, count = 0;
563
16.0M
    Py_ssize_t gap = mlast;
564
16.0M
    const STRINGLIB_CHAR last = p[mlast];
565
16.0M
    const STRINGLIB_CHAR *const ss = &s[mlast];
566
567
16.0M
    unsigned long mask = 0;
568
32.2M
    for (Py_ssize_t i = 0; i < mlast; i++) {
569
16.1M
        STRINGLIB_BLOOM_ADD(mask, p[i]);
570
16.1M
        if (p[i] == last) {
571
356k
            gap = mlast - i - 1;
572
356k
        }
573
16.1M
    }
574
16.0M
    STRINGLIB_BLOOM_ADD(mask, last);
575
576
5.44G
    for (Py_ssize_t i = 0; i <= w; i++) {
577
5.43G
        if (ss[i] == last) {
578
            /* candidate match */
579
65.5M
            Py_ssize_t j;
580
88.2M
            for (j = 0; j < mlast; j++) {
581
65.5M
                if (s[i+j] != p[j]) {
582
42.9M
                    break;
583
42.9M
                }
584
65.5M
            }
585
65.5M
            if (j == mlast) {
586
                /* got a match! */
587
22.6M
                if (mode != FAST_COUNT) {
588
11.3M
                    return i;
589
11.3M
                }
590
11.2M
                count++;
591
11.2M
                if (count == maxcount) {
592
0
                    return maxcount;
593
0
                }
594
11.2M
                i = i + mlast;
595
11.2M
                continue;
596
11.2M
            }
597
            /* miss: check if next character is part of pattern */
598
42.9M
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
599
8.17M
                i = i + m;
600
8.17M
            }
601
34.7M
            else {
602
34.7M
                i = i + gap;
603
34.7M
            }
604
42.9M
        }
605
5.37G
        else {
606
            /* skip: check if next character is part of pattern */
607
5.37G
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
608
5.32G
                i = i + m;
609
5.32G
            }
610
5.37G
        }
611
5.43G
    }
612
4.70M
    return mode == FAST_COUNT ? count : -1;
613
16.0M
}
Unexecuted instantiation: bytesobject.c:stringlib_default_find
unicodeobject.c:asciilib_default_find
Line
Count
Source
560
1.74M
{
561
1.74M
    const Py_ssize_t w = n - m;
562
1.74M
    Py_ssize_t mlast = m - 1, count = 0;
563
1.74M
    Py_ssize_t gap = mlast;
564
1.74M
    const STRINGLIB_CHAR last = p[mlast];
565
1.74M
    const STRINGLIB_CHAR *const ss = &s[mlast];
566
567
1.74M
    unsigned long mask = 0;
568
3.48M
    for (Py_ssize_t i = 0; i < mlast; i++) {
569
1.74M
        STRINGLIB_BLOOM_ADD(mask, p[i]);
570
1.74M
        if (p[i] == last) {
571
17.6k
            gap = mlast - i - 1;
572
17.6k
        }
573
1.74M
    }
574
1.74M
    STRINGLIB_BLOOM_ADD(mask, last);
575
576
150M
    for (Py_ssize_t i = 0; i <= w; i++) {
577
150M
        if (ss[i] == last) {
578
            /* candidate match */
579
3.34M
            Py_ssize_t j;
580
5.05M
            for (j = 0; j < mlast; j++) {
581
3.34M
                if (s[i+j] != p[j]) {
582
1.62M
                    break;
583
1.62M
                }
584
3.34M
            }
585
3.34M
            if (j == mlast) {
586
                /* got a match! */
587
1.71M
                if (mode != FAST_COUNT) {
588
1.71M
                    return i;
589
1.71M
                }
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.62M
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
599
45.0k
                i = i + m;
600
45.0k
            }
601
1.57M
            else {
602
1.57M
                i = i + gap;
603
1.57M
            }
604
1.62M
        }
605
147M
        else {
606
            /* skip: check if next character is part of pattern */
607
147M
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
608
142M
                i = i + m;
609
142M
            }
610
147M
        }
611
150M
    }
612
28.7k
    return mode == FAST_COUNT ? count : -1;
613
1.74M
}
unicodeobject.c:ucs1lib_default_find
Line
Count
Source
560
6.03M
{
561
6.03M
    const Py_ssize_t w = n - m;
562
6.03M
    Py_ssize_t mlast = m - 1, count = 0;
563
6.03M
    Py_ssize_t gap = mlast;
564
6.03M
    const STRINGLIB_CHAR last = p[mlast];
565
6.03M
    const STRINGLIB_CHAR *const ss = &s[mlast];
566
567
6.03M
    unsigned long mask = 0;
568
12.1M
    for (Py_ssize_t i = 0; i < mlast; i++) {
569
6.08M
        STRINGLIB_BLOOM_ADD(mask, p[i]);
570
6.08M
        if (p[i] == last) {
571
282k
            gap = mlast - i - 1;
572
282k
        }
573
6.08M
    }
574
6.03M
    STRINGLIB_BLOOM_ADD(mask, last);
575
576
2.85G
    for (Py_ssize_t i = 0; i <= w; i++) {
577
2.85G
        if (ss[i] == last) {
578
            /* candidate match */
579
20.0M
            Py_ssize_t j;
580
24.8M
            for (j = 0; j < mlast; j++) {
581
20.0M
                if (s[i+j] != p[j]) {
582
15.3M
                    break;
583
15.3M
                }
584
20.0M
            }
585
20.0M
            if (j == mlast) {
586
                /* got a match! */
587
4.74M
                if (mode != FAST_COUNT) {
588
1.50M
                    return i;
589
1.50M
                }
590
3.23M
                count++;
591
3.23M
                if (count == maxcount) {
592
0
                    return maxcount;
593
0
                }
594
3.23M
                i = i + mlast;
595
3.23M
                continue;
596
3.23M
            }
597
            /* miss: check if next character is part of pattern */
598
15.3M
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
599
4.60M
                i = i + m;
600
4.60M
            }
601
10.7M
            else {
602
10.7M
                i = i + gap;
603
10.7M
            }
604
15.3M
        }
605
2.83G
        else {
606
            /* skip: check if next character is part of pattern */
607
2.83G
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
608
2.81G
                i = i + m;
609
2.81G
            }
610
2.83G
        }
611
2.85G
    }
612
4.52M
    return mode == FAST_COUNT ? count : -1;
613
6.03M
}
unicodeobject.c:ucs2lib_default_find
Line
Count
Source
560
3.54M
{
561
3.54M
    const Py_ssize_t w = n - m;
562
3.54M
    Py_ssize_t mlast = m - 1, count = 0;
563
3.54M
    Py_ssize_t gap = mlast;
564
3.54M
    const STRINGLIB_CHAR last = p[mlast];
565
3.54M
    const STRINGLIB_CHAR *const ss = &s[mlast];
566
567
3.54M
    unsigned long mask = 0;
568
7.10M
    for (Py_ssize_t i = 0; i < mlast; i++) {
569
3.56M
        STRINGLIB_BLOOM_ADD(mask, p[i]);
570
3.56M
        if (p[i] == last) {
571
32.6k
            gap = mlast - i - 1;
572
32.6k
        }
573
3.56M
    }
574
3.54M
    STRINGLIB_BLOOM_ADD(mask, last);
575
576
1.04G
    for (Py_ssize_t i = 0; i <= w; i++) {
577
1.04G
        if (ss[i] == last) {
578
            /* candidate match */
579
14.9M
            Py_ssize_t j;
580
21.7M
            for (j = 0; j < mlast; j++) {
581
14.9M
                if (s[i+j] != p[j]) {
582
8.12M
                    break;
583
8.12M
                }
584
14.9M
            }
585
14.9M
            if (j == mlast) {
586
                /* got a match! */
587
6.79M
                if (mode != FAST_COUNT) {
588
3.43M
                    return i;
589
3.43M
                }
590
3.35M
                count++;
591
3.35M
                if (count == maxcount) {
592
0
                    return maxcount;
593
0
                }
594
3.35M
                i = i + mlast;
595
3.35M
                continue;
596
3.35M
            }
597
            /* miss: check if next character is part of pattern */
598
8.12M
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
599
1.39M
                i = i + m;
600
1.39M
            }
601
6.72M
            else {
602
6.72M
                i = i + gap;
603
6.72M
            }
604
8.12M
        }
605
1.03G
        else {
606
            /* skip: check if next character is part of pattern */
607
1.03G
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
608
1.02G
                i = i + m;
609
1.02G
            }
610
1.03G
        }
611
1.04G
    }
612
108k
    return mode == FAST_COUNT ? count : -1;
613
3.54M
}
unicodeobject.c:ucs4lib_default_find
Line
Count
Source
560
4.75M
{
561
4.75M
    const Py_ssize_t w = n - m;
562
4.75M
    Py_ssize_t mlast = m - 1, count = 0;
563
4.75M
    Py_ssize_t gap = mlast;
564
4.75M
    const STRINGLIB_CHAR last = p[mlast];
565
4.75M
    const STRINGLIB_CHAR *const ss = &s[mlast];
566
567
4.75M
    unsigned long mask = 0;
568
9.50M
    for (Py_ssize_t i = 0; i < mlast; i++) {
569
4.75M
        STRINGLIB_BLOOM_ADD(mask, p[i]);
570
4.75M
        if (p[i] == last) {
571
19.8k
            gap = mlast - i - 1;
572
19.8k
        }
573
4.75M
    }
574
4.75M
    STRINGLIB_BLOOM_ADD(mask, last);
575
576
1.38G
    for (Py_ssize_t i = 0; i <= w; i++) {
577
1.38G
        if (ss[i] == last) {
578
            /* candidate match */
579
27.2M
            Py_ssize_t j;
580
36.6M
            for (j = 0; j < mlast; j++) {
581
27.2M
                if (s[i+j] != p[j]) {
582
17.8M
                    break;
583
17.8M
                }
584
27.2M
            }
585
27.2M
            if (j == mlast) {
586
                /* got a match! */
587
9.40M
                if (mode != FAST_COUNT) {
588
4.71M
                    return i;
589
4.71M
                }
590
4.69M
                count++;
591
4.69M
                if (count == maxcount) {
592
0
                    return maxcount;
593
0
                }
594
4.69M
                i = i + mlast;
595
4.69M
                continue;
596
4.69M
            }
597
            /* miss: check if next character is part of pattern */
598
17.8M
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
599
2.12M
                i = i + m;
600
2.12M
            }
601
15.6M
            else {
602
15.6M
                i = i + gap;
603
15.6M
            }
604
17.8M
        }
605
1.36G
        else {
606
            /* skip: check if next character is part of pattern */
607
1.36G
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
608
1.34G
                i = i + m;
609
1.34G
            }
610
1.36G
        }
611
1.38G
    }
612
40.2k
    return mode == FAST_COUNT ? count : -1;
613
4.75M
}
bytes_methods.c:stringlib_default_find
Line
Count
Source
560
3.02k
{
561
3.02k
    const Py_ssize_t w = n - m;
562
3.02k
    Py_ssize_t mlast = m - 1, count = 0;
563
3.02k
    Py_ssize_t gap = mlast;
564
3.02k
    const STRINGLIB_CHAR last = p[mlast];
565
3.02k
    const STRINGLIB_CHAR *const ss = &s[mlast];
566
567
3.02k
    unsigned long mask = 0;
568
12.0k
    for (Py_ssize_t i = 0; i < mlast; i++) {
569
9.06k
        STRINGLIB_BLOOM_ADD(mask, p[i]);
570
9.06k
        if (p[i] == last) {
571
3.02k
            gap = mlast - i - 1;
572
3.02k
        }
573
9.06k
    }
574
3.02k
    STRINGLIB_BLOOM_ADD(mask, last);
575
576
944k
    for (Py_ssize_t i = 0; i <= w; i++) {
577
944k
        if (ss[i] == last) {
578
            /* candidate match */
579
8.27k
            Py_ssize_t j;
580
16.9k
            for (j = 0; j < mlast; j++) {
581
14.1k
                if (s[i+j] != p[j]) {
582
5.45k
                    break;
583
5.45k
                }
584
14.1k
            }
585
8.27k
            if (j == mlast) {
586
                /* got a match! */
587
2.81k
                if (mode != FAST_COUNT) {
588
2.81k
                    return i;
589
2.81k
                }
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.45k
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
599
636
                i = i + m;
600
636
            }
601
4.82k
            else {
602
4.82k
                i = i + gap;
603
4.82k
            }
604
5.45k
        }
605
935k
        else {
606
            /* skip: check if next character is part of pattern */
607
935k
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
608
43.0k
                i = i + m;
609
43.0k
            }
610
935k
        }
611
944k
    }
612
208
    return mode == FAST_COUNT ? count : -1;
613
3.02k
}
Unexecuted instantiation: bytearrayobject.c:stringlib_default_find
614
615
616
static Py_ssize_t
617
STRINGLIB(adaptive_find)(const STRINGLIB_CHAR* s, Py_ssize_t n,
618
                         const STRINGLIB_CHAR* p, Py_ssize_t m,
619
                         Py_ssize_t maxcount, int mode)
620
0
{
621
0
    const Py_ssize_t w = n - m;
622
0
    Py_ssize_t mlast = m - 1, count = 0;
623
0
    Py_ssize_t gap = mlast;
624
0
    Py_ssize_t hits = 0, res;
625
0
    const STRINGLIB_CHAR last = p[mlast];
626
0
    const STRINGLIB_CHAR *const ss = &s[mlast];
627
628
0
    unsigned long mask = 0;
629
0
    for (Py_ssize_t i = 0; i < mlast; i++) {
630
0
        STRINGLIB_BLOOM_ADD(mask, p[i]);
631
0
        if (p[i] == last) {
632
0
            gap = mlast - i - 1;
633
0
        }
634
0
    }
635
0
    STRINGLIB_BLOOM_ADD(mask, last);
636
637
0
    for (Py_ssize_t i = 0; i <= w; i++) {
638
0
        if (ss[i] == last) {
639
            /* candidate match */
640
0
            Py_ssize_t j;
641
0
            for (j = 0; j < mlast; j++) {
642
0
                if (s[i+j] != p[j]) {
643
0
                    break;
644
0
                }
645
0
            }
646
0
            if (j == mlast) {
647
                /* got a match! */
648
0
                if (mode != FAST_COUNT) {
649
0
                    return i;
650
0
                }
651
0
                count++;
652
0
                if (count == maxcount) {
653
0
                    return maxcount;
654
0
                }
655
0
                i = i + mlast;
656
0
                continue;
657
0
            }
658
0
            hits += j + 1;
659
0
            if (hits > m / 4 && w - i > 2000) {
660
0
                if (mode == FAST_SEARCH) {
661
0
                    res = STRINGLIB(_two_way_find)(s + i, n - i, p, m);
662
0
                    return res == -1 ? -1 : res + i;
663
0
                }
664
0
                else {
665
0
                    res = STRINGLIB(_two_way_count)(s + i, n - i, p, m,
666
0
                                                    maxcount - count);
667
0
                    return res + count;
668
0
                }
669
0
            }
670
            /* miss: check if next character is part of pattern */
671
0
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
672
0
                i = i + m;
673
0
            }
674
0
            else {
675
0
                i = i + gap;
676
0
            }
677
0
        }
678
0
        else {
679
            /* skip: check if next character is part of pattern */
680
0
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
681
0
                i = i + m;
682
0
            }
683
0
        }
684
0
    }
685
0
    return mode == FAST_COUNT ? count : -1;
686
0
}
Unexecuted instantiation: bytesobject.c:stringlib_adaptive_find
Unexecuted instantiation: unicodeobject.c:asciilib_adaptive_find
Unexecuted instantiation: unicodeobject.c:ucs1lib_adaptive_find
Unexecuted instantiation: unicodeobject.c:ucs2lib_adaptive_find
Unexecuted instantiation: unicodeobject.c:ucs4lib_adaptive_find
Unexecuted instantiation: bytes_methods.c:stringlib_adaptive_find
Unexecuted instantiation: bytearrayobject.c:stringlib_adaptive_find
687
688
689
static Py_ssize_t
690
STRINGLIB(default_rfind)(const STRINGLIB_CHAR* s, Py_ssize_t n,
691
                         const STRINGLIB_CHAR* p, Py_ssize_t m,
692
                         Py_ssize_t maxcount, int mode)
693
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
61.7M
{
762
61.7M
    Py_ssize_t count = 0;
763
13.1G
    for (Py_ssize_t i = 0; i < n; i++) {
764
13.0G
        if (s[i] == p0) {
765
256M
            count++;
766
256M
        }
767
13.0G
    }
768
61.7M
    return count;
769
61.7M
}
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
53.9M
{
762
53.9M
    Py_ssize_t count = 0;
763
8.74G
    for (Py_ssize_t i = 0; i < n; i++) {
764
8.68G
        if (s[i] == p0) {
765
55.6M
            count++;
766
55.6M
        }
767
8.68G
    }
768
53.9M
    return count;
769
53.9M
}
unicodeobject.c:ucs2lib_count_char_no_maxcount
Line
Count
Source
761
6.90M
{
762
6.90M
    Py_ssize_t count = 0;
763
2.22G
    for (Py_ssize_t i = 0; i < n; i++) {
764
2.21G
        if (s[i] == p0) {
765
75.8M
            count++;
766
75.8M
        }
767
2.21G
    }
768
6.90M
    return count;
769
6.90M
}
unicodeobject.c:ucs4lib_count_char_no_maxcount
Line
Count
Source
761
857k
{
762
857k
    Py_ssize_t count = 0;
763
2.14G
    for (Py_ssize_t i = 0; i < n; i++) {
764
2.14G
        if (s[i] == p0) {
765
124M
            count++;
766
124M
        }
767
2.14G
    }
768
857k
    return count;
769
857k
}
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
226M
{
777
226M
    if (n < m || (mode == FAST_COUNT && maxcount == 0)) {
778
7.15k
        return -1;
779
7.15k
    }
780
781
    /* look for special cases */
782
226M
    if (m <= 1) {
783
210M
        if (m <= 0) {
784
0
            return -1;
785
0
        }
786
        /* use special case for 1-character strings */
787
210M
        if (mode == FAST_SEARCH)
788
148M
            return STRINGLIB(find_char)(s, n, p[0]);
789
61.7M
        else if (mode == FAST_RSEARCH)
790
9.64k
            return STRINGLIB(rfind_char)(s, n, p[0]);
791
61.7M
        else {
792
61.7M
            if (maxcount == PY_SSIZE_T_MAX) {
793
61.7M
                return STRINGLIB(count_char_no_maxcount)(s, n, p[0]);
794
61.7M
            }
795
0
            return STRINGLIB(count_char)(s, n, p[0], maxcount);
796
61.7M
        }
797
210M
    }
798
799
16.0M
    if (mode != FAST_RSEARCH) {
800
16.0M
        if (n < 2500 || (m < 100 && n < 30000) || m < 6) {
801
16.0M
            return STRINGLIB(default_find)(s, n, p, m, maxcount, mode);
802
16.0M
        }
803
20
        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
20
            if (mode == FAST_SEARCH) {
810
20
                return STRINGLIB(_two_way_find)(s, n, p, m);
811
20
            }
812
0
            else {
813
0
                return STRINGLIB(_two_way_count)(s, n, p, m, maxcount);
814
0
            }
815
20
        }
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.0M
    }
825
4
    else {
826
        /* FAST_RSEARCH */
827
4
        return STRINGLIB(default_rfind)(s, n, p, m, maxcount, mode);
828
4
    }
829
16.0M
}
Unexecuted instantiation: bytesobject.c:fastsearch
unicodeobject.c:asciilib_fastsearch
Line
Count
Source
776
18.7M
{
777
18.7M
    if (n < m || (mode == FAST_COUNT && maxcount == 0)) {
778
0
        return -1;
779
0
    }
780
781
    /* look for special cases */
782
18.7M
    if (m <= 1) {
783
16.9M
        if (m <= 0) {
784
0
            return -1;
785
0
        }
786
        /* use special case for 1-character strings */
787
16.9M
        if (mode == FAST_SEARCH)
788
16.9M
            return STRINGLIB(find_char)(s, n, p[0]);
789
9.64k
        else if (mode == FAST_RSEARCH)
790
9.64k
            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
16.9M
    }
798
799
1.74M
    if (mode != FAST_RSEARCH) {
800
1.74M
        if (n < 2500 || (m < 100 && n < 30000) || m < 6) {
801
1.74M
            return STRINGLIB(default_find)(s, n, p, m, maxcount, mode);
802
1.74M
        }
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.74M
    }
825
0
    else {
826
        /* FAST_RSEARCH */
827
0
        return STRINGLIB(default_rfind)(s, n, p, m, maxcount, mode);
828
0
    }
829
1.74M
}
unicodeobject.c:ucs1lib_fastsearch
Line
Count
Source
776
66.5M
{
777
66.5M
    if (n < m || (mode == FAST_COUNT && maxcount == 0)) {
778
0
        return -1;
779
0
    }
780
781
    /* look for special cases */
782
66.5M
    if (m <= 1) {
783
60.4M
        if (m <= 0) {
784
0
            return -1;
785
0
        }
786
        /* use special case for 1-character strings */
787
60.4M
        if (mode == FAST_SEARCH)
788
6.51M
            return STRINGLIB(find_char)(s, n, p[0]);
789
53.9M
        else if (mode == FAST_RSEARCH)
790
0
            return STRINGLIB(rfind_char)(s, n, p[0]);
791
53.9M
        else {
792
53.9M
            if (maxcount == PY_SSIZE_T_MAX) {
793
53.9M
                return STRINGLIB(count_char_no_maxcount)(s, n, p[0]);
794
53.9M
            }
795
0
            return STRINGLIB(count_char)(s, n, p[0], maxcount);
796
53.9M
        }
797
60.4M
    }
798
799
6.03M
    if (mode != FAST_RSEARCH) {
800
6.03M
        if (n < 2500 || (m < 100 && n < 30000) || m < 6) {
801
6.03M
            return STRINGLIB(default_find)(s, n, p, m, maxcount, mode);
802
6.03M
        }
803
20
        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
20
            if (mode == FAST_SEARCH) {
810
20
                return STRINGLIB(_two_way_find)(s, n, p, m);
811
20
            }
812
0
            else {
813
0
                return STRINGLIB(_two_way_count)(s, n, p, m, maxcount);
814
0
            }
815
20
        }
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
6.03M
    }
825
0
    else {
826
        /* FAST_RSEARCH */
827
0
        return STRINGLIB(default_rfind)(s, n, p, m, maxcount, mode);
828
0
    }
829
6.03M
}
unicodeobject.c:ucs2lib_fastsearch
Line
Count
Source
776
64.4M
{
777
64.4M
    if (n < m || (mode == FAST_COUNT && maxcount == 0)) {
778
7.14k
        return -1;
779
7.14k
    }
780
781
    /* look for special cases */
782
64.4M
    if (m <= 1) {
783
60.9M
        if (m <= 0) {
784
0
            return -1;
785
0
        }
786
        /* use special case for 1-character strings */
787
60.9M
        if (mode == FAST_SEARCH)
788
53.9M
            return STRINGLIB(find_char)(s, n, p[0]);
789
6.90M
        else if (mode == FAST_RSEARCH)
790
0
            return STRINGLIB(rfind_char)(s, n, p[0]);
791
6.90M
        else {
792
6.90M
            if (maxcount == PY_SSIZE_T_MAX) {
793
6.90M
                return STRINGLIB(count_char_no_maxcount)(s, n, p[0]);
794
6.90M
            }
795
0
            return STRINGLIB(count_char)(s, n, p[0], maxcount);
796
6.90M
        }
797
60.9M
    }
798
799
3.54M
    if (mode != FAST_RSEARCH) {
800
3.54M
        if (n < 2500 || (m < 100 && n < 30000) || m < 6) {
801
3.54M
            return STRINGLIB(default_find)(s, n, p, m, maxcount, mode);
802
3.54M
        }
803
0
        else if ((m >> 2) * 3 < (n >> 2)) {
804
            /* 33% threshold, but don't overflow. */
805
            /* For larger problems where the needle isn't a huge
806
               percentage of the size of the haystack, the relatively
807
               expensive O(m) startup cost of the two-way algorithm
808
               will surely pay off. */
809
0
            if (mode == FAST_SEARCH) {
810
0
                return STRINGLIB(_two_way_find)(s, n, p, m);
811
0
            }
812
0
            else {
813
0
                return STRINGLIB(_two_way_count)(s, n, p, m, maxcount);
814
0
            }
815
0
        }
816
0
        else {
817
            /* To ensure that we have good worst-case behavior,
818
               here's an adaptive version of the algorithm, where if
819
               we match O(m) characters without any matches of the
820
               entire needle, then we predict that the startup cost of
821
               the two-way algorithm will probably be worth it. */
822
0
            return STRINGLIB(adaptive_find)(s, n, p, m, maxcount, mode);
823
0
        }
824
3.54M
    }
825
0
    else {
826
        /* FAST_RSEARCH */
827
0
        return STRINGLIB(default_rfind)(s, n, p, m, maxcount, mode);
828
0
    }
829
3.54M
}
unicodeobject.c:ucs4lib_fastsearch
Line
Count
Source
776
76.4M
{
777
76.4M
    if (n < m || (mode == FAST_COUNT && maxcount == 0)) {
778
0
        return -1;
779
0
    }
780
781
    /* look for special cases */
782
76.4M
    if (m <= 1) {
783
71.7M
        if (m <= 0) {
784
0
            return -1;
785
0
        }
786
        /* use special case for 1-character strings */
787
71.7M
        if (mode == FAST_SEARCH)
788
70.8M
            return STRINGLIB(find_char)(s, n, p[0]);
789
857k
        else if (mode == FAST_RSEARCH)
790
0
            return STRINGLIB(rfind_char)(s, n, p[0]);
791
857k
        else {
792
857k
            if (maxcount == PY_SSIZE_T_MAX) {
793
857k
                return STRINGLIB(count_char_no_maxcount)(s, n, p[0]);
794
857k
            }
795
0
            return STRINGLIB(count_char)(s, n, p[0], maxcount);
796
857k
        }
797
71.7M
    }
798
799
4.75M
    if (mode != FAST_RSEARCH) {
800
4.75M
        if (n < 2500 || (m < 100 && n < 30000) || m < 6) {
801
4.75M
            return STRINGLIB(default_find)(s, n, p, m, maxcount, mode);
802
4.75M
        }
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.75M
    }
825
0
    else {
826
        /* FAST_RSEARCH */
827
0
        return STRINGLIB(default_rfind)(s, n, p, m, maxcount, mode);
828
0
    }
829
4.75M
}
bytes_methods.c:fastsearch
Line
Count
Source
776
3.03k
{
777
3.03k
    if (n < m || (mode == FAST_COUNT && maxcount == 0)) {
778
12
        return -1;
779
12
    }
780
781
    /* look for special cases */
782
3.02k
    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
3.02k
    if (mode != FAST_RSEARCH) {
800
3.02k
        if (n < 2500 || (m < 100 && n < 30000) || m < 6) {
801
3.02k
            return STRINGLIB(default_find)(s, n, p, m, maxcount, mode);
802
3.02k
        }
803
0
        else if ((m >> 2) * 3 < (n >> 2)) {
804
            /* 33% threshold, but don't overflow. */
805
            /* For larger problems where the needle isn't a huge
806
               percentage of the size of the haystack, the relatively
807
               expensive O(m) startup cost of the two-way algorithm
808
               will surely pay off. */
809
0
            if (mode == FAST_SEARCH) {
810
0
                return STRINGLIB(_two_way_find)(s, n, p, m);
811
0
            }
812
0
            else {
813
0
                return STRINGLIB(_two_way_count)(s, n, p, m, maxcount);
814
0
            }
815
0
        }
816
0
        else {
817
            /* To ensure that we have good worst-case behavior,
818
               here's an adaptive version of the algorithm, where if
819
               we match O(m) characters without any matches of the
820
               entire needle, then we predict that the startup cost of
821
               the two-way algorithm will probably be worth it. */
822
0
            return STRINGLIB(adaptive_find)(s, n, p, m, maxcount, mode);
823
0
        }
824
3.02k
    }
825
4
    else {
826
        /* FAST_RSEARCH */
827
4
        return STRINGLIB(default_rfind)(s, n, p, m, maxcount, mode);
828
4
    }
829
3.02k
}
Unexecuted instantiation: bytearrayobject.c:fastsearch
830