Coverage Report

Created: 2025-11-11 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cpython/Objects/stringlib/fastsearch.h
Line
Count
Source
1
/* stringlib: fastsearch implementation */
2
3
#define STRINGLIB_FASTSEARCH_H
4
5
/* fast search/count implementation, based on a mix between boyer-
6
   moore and horspool, with a few more bells and whistles on the top.
7
   for some more background, see:
8
   https://web.archive.org/web/20201107074620/http://effbot.org/zone/stringlib.htm */
9
10
/* note: fastsearch may access s[n], which isn't a problem when using
11
   Python's ordinary string types, but may cause problems if you're
12
   using this code in other contexts.  also, the count mode returns -1
13
   if there cannot possibly be a match in the target string, and 0 if
14
   it has actually checked for matches, but didn't find any.  callers
15
   beware! */
16
17
/* If the strings are long enough, use Crochemore and Perrin's Two-Way
18
   algorithm, which has worst-case O(n) runtime and best-case O(n/k).
19
   Also compute a table of shifts to achieve O(n/k) in more cases,
20
   and often (data dependent) deduce larger shifts than pure C&P can
21
   deduce. See stringlib_find_two_way_notes.txt in this folder for a
22
   detailed explanation. */
23
24
622M
#define FAST_COUNT 0
25
449M
#define FAST_SEARCH 1
26
76.4M
#define FAST_RSEARCH 2
27
28
#if LONG_BIT >= 128
29
#define STRINGLIB_BLOOM_WIDTH 128
30
#elif LONG_BIT >= 64
31
3.14G
#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
31.2M
    ((mask |= (1UL << ((ch) & (STRINGLIB_BLOOM_WIDTH -1)))))
40
#define STRINGLIB_BLOOM(mask, ch)     \
41
3.11G
    ((mask &  (1UL << ((ch) & (STRINGLIB_BLOOM_WIDTH -1)))))
42
43
#ifdef STRINGLIB_FAST_MEMCHR
44
222M
#  define MEMCHR_CUT_OFF 15
45
#else
46
82.6M
#  define MEMCHR_CUT_OFF 40
47
#endif
48
49
Py_LOCAL_INLINE(Py_ssize_t)
50
STRINGLIB(find_char)(const STRINGLIB_CHAR* s, Py_ssize_t n, STRINGLIB_CHAR ch)
51
304M
{
52
304M
    const STRINGLIB_CHAR *p, *e;
53
54
304M
    p = s;
55
304M
    e = s + n;
56
304M
    if (n > MEMCHR_CUT_OFF) {
57
#ifdef STRINGLIB_FAST_MEMCHR
58
130M
        p = STRINGLIB_FAST_MEMCHR(s, ch, n);
59
130M
        if (p != NULL)
60
128M
            return (p - s);
61
2.19M
        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
72.4M
        if (needle != 0) {
71
72.0M
            do {
72
72.0M
                void *candidate = memchr(p, needle,
73
72.0M
                                         (e - p) * sizeof(STRINGLIB_CHAR));
74
72.0M
                if (candidate == NULL)
75
517k
                    return -1;
76
71.4M
                s1 = p;
77
71.4M
                p = (const STRINGLIB_CHAR *)
78
71.4M
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
79
71.4M
                if (*p == ch)
80
71.3M
                    return (p - s);
81
                /* False positive */
82
102k
                p++;
83
102k
                if (p - s1 > MEMCHR_CUT_OFF)
84
47.6k
                    continue;
85
55.1k
                if (e - p <= MEMCHR_CUT_OFF)
86
4.40k
                    break;
87
50.7k
                e1 = p + MEMCHR_CUT_OFF;
88
1.50M
                while (p != e1) {
89
1.47M
                    if (*p == ch)
90
21.8k
                        return (p - s);
91
1.45M
                    p++;
92
1.45M
                }
93
50.7k
            }
94
71.9M
            while (e - p > MEMCHR_CUT_OFF);
95
71.9M
        }
96
#endif
97
203M
    }
98
438M
    while (p < e) {
99
351M
        if (*p == ch)
100
15.5M
            return (p - s);
101
336M
        p++;
102
336M
    }
103
86.3M
    return -1;
104
101M
}
Unexecuted instantiation: bytesobject.c:stringlib_find_char
unicodeobject.c:ucs1lib_find_char
Line
Count
Source
51
111M
{
52
111M
    const STRINGLIB_CHAR *p, *e;
53
54
111M
    p = s;
55
111M
    e = s + n;
56
111M
    if (n > MEMCHR_CUT_OFF) {
57
24.3M
#ifdef STRINGLIB_FAST_MEMCHR
58
24.3M
        p = STRINGLIB_FAST_MEMCHR(s, ch, n);
59
24.3M
        if (p != NULL)
60
22.9M
            return (p - s);
61
1.41M
        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
24.3M
    }
98
246M
    while (p < e) {
99
164M
        if (*p == ch)
100
4.24M
            return (p - s);
101
159M
        p++;
102
159M
    }
103
82.8M
    return -1;
104
87.1M
}
unicodeobject.c:ucs2lib_find_char
Line
Count
Source
51
82.3M
{
52
82.3M
    const STRINGLIB_CHAR *p, *e;
53
54
82.3M
    p = s;
55
82.3M
    e = s + n;
56
82.3M
    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
72.4M
        const STRINGLIB_CHAR *s1, *e1;
66
72.4M
        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
72.4M
        if (needle != 0) {
71
72.0M
            do {
72
72.0M
                void *candidate = memchr(p, needle,
73
72.0M
                                         (e - p) * sizeof(STRINGLIB_CHAR));
74
72.0M
                if (candidate == NULL)
75
517k
                    return -1;
76
71.4M
                s1 = p;
77
71.4M
                p = (const STRINGLIB_CHAR *)
78
71.4M
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
79
71.4M
                if (*p == ch)
80
71.3M
                    return (p - s);
81
                /* False positive */
82
102k
                p++;
83
102k
                if (p - s1 > MEMCHR_CUT_OFF)
84
47.6k
                    continue;
85
55.1k
                if (e - p <= MEMCHR_CUT_OFF)
86
4.40k
                    break;
87
50.7k
                e1 = p + MEMCHR_CUT_OFF;
88
1.50M
                while (p != e1) {
89
1.47M
                    if (*p == ch)
90
21.8k
                        return (p - s);
91
1.45M
                    p++;
92
1.45M
                }
93
50.7k
            }
94
71.9M
            while (e - p > MEMCHR_CUT_OFF);
95
71.9M
        }
96
72.4M
#endif
97
72.4M
    }
98
179M
    while (p < e) {
99
176M
        if (*p == ch)
100
7.13M
            return (p - s);
101
169M
        p++;
102
169M
    }
103
3.33M
    return -1;
104
10.4M
}
unicodeobject.c:ucs4lib_find_char
Line
Count
Source
51
88.7M
{
52
88.7M
    const STRINGLIB_CHAR *p, *e;
53
54
88.7M
    p = s;
55
88.7M
    e = s + n;
56
88.7M
    if (n > MEMCHR_CUT_OFF) {
57
88.5M
#ifdef STRINGLIB_FAST_MEMCHR
58
88.5M
        p = STRINGLIB_FAST_MEMCHR(s, ch, n);
59
88.5M
        if (p != NULL)
60
88.5M
            return (p - s);
61
34.0k
        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
88.5M
    }
98
473k
    while (p < e) {
99
385k
        if (*p == ch)
100
42.9k
            return (p - s);
101
342k
        p++;
102
342k
    }
103
88.4k
    return -1;
104
131k
}
unicodeobject.c:asciilib_find_char
Line
Count
Source
51
21.8M
{
52
21.8M
    const STRINGLIB_CHAR *p, *e;
53
54
21.8M
    p = s;
55
21.8M
    e = s + n;
56
21.8M
    if (n > MEMCHR_CUT_OFF) {
57
17.6M
#ifdef STRINGLIB_FAST_MEMCHR
58
17.6M
        p = STRINGLIB_FAST_MEMCHR(s, ch, n);
59
17.6M
        if (p != NULL)
60
16.9M
            return (p - s);
61
745k
        return -1;
62
#else
63
        /* use memchr if we can choose a needle without too many likely
64
           false positives */
65
        const STRINGLIB_CHAR *s1, *e1;
66
        unsigned char needle = ch & 0xff;
67
        /* If looking for a multiple of 256, we'd have too
68
           many false positives looking for the '\0' byte in UCS2
69
           and UCS4 representations. */
70
        if (needle != 0) {
71
            do {
72
                void *candidate = memchr(p, needle,
73
                                         (e - p) * sizeof(STRINGLIB_CHAR));
74
                if (candidate == NULL)
75
                    return -1;
76
                s1 = p;
77
                p = (const STRINGLIB_CHAR *)
78
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
79
                if (*p == ch)
80
                    return (p - s);
81
                /* False positive */
82
                p++;
83
                if (p - s1 > MEMCHR_CUT_OFF)
84
                    continue;
85
                if (e - p <= MEMCHR_CUT_OFF)
86
                    break;
87
                e1 = p + MEMCHR_CUT_OFF;
88
                while (p != e1) {
89
                    if (*p == ch)
90
                        return (p - s);
91
                    p++;
92
                }
93
            }
94
            while (e - p > MEMCHR_CUT_OFF);
95
        }
96
#endif
97
17.6M
    }
98
11.3M
    while (p < e) {
99
11.2M
        if (*p == ch)
100
4.13M
            return (p - s);
101
7.14M
        p++;
102
7.14M
    }
103
60.9k
    return -1;
104
4.19M
}
bytes_methods.c:stringlib_find_char
Line
Count
Source
51
1.97k
{
52
1.97k
    const STRINGLIB_CHAR *p, *e;
53
54
1.97k
    p = s;
55
1.97k
    e = s + n;
56
1.97k
    if (n > MEMCHR_CUT_OFF) {
57
1.97k
#ifdef STRINGLIB_FAST_MEMCHR
58
1.97k
        p = STRINGLIB_FAST_MEMCHR(s, ch, n);
59
1.97k
        if (p != NULL)
60
1.64k
            return (p - s);
61
327
        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.97k
    }
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
41.2k
#  define MEMRCHR_CUT_OFF 15
110
#else
111
162k
#  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
178k
{
118
178k
    const STRINGLIB_CHAR *p;
119
178k
#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
178k
    if (n > MEMRCHR_CUT_OFF) {
126
#if STRINGLIB_SIZEOF_CHAR == 1
127
        p = memrchr(s, ch, n);
128
11.5k
        if (p != NULL)
129
8.21k
            return (p - s);
130
3.38k
        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
84.2k
        if (needle != 0) {
141
90.4k
            do {
142
90.4k
                void *candidate = memrchr(s, needle,
143
90.4k
                                          n * sizeof(STRINGLIB_CHAR));
144
90.4k
                if (candidate == NULL)
145
719
                    return -1;
146
89.7k
                n1 = n;
147
89.7k
                p = (const STRINGLIB_CHAR *)
148
89.7k
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
149
89.7k
                n = p - s;
150
89.7k
                if (*p == ch)
151
80.8k
                    return n;
152
                /* False positive */
153
8.86k
                if (n1 - n > MEMRCHR_CUT_OFF)
154
3.51k
                    continue;
155
5.35k
                if (n <= MEMRCHR_CUT_OFF)
156
1.19k
                    break;
157
4.15k
                s1 = p - MEMRCHR_CUT_OFF;
158
155k
                while (p > s1) {
159
151k
                    p--;
160
151k
                    if (*p == ch)
161
579
                        return (p - s);
162
151k
                }
163
3.57k
                n = p - s;
164
3.57k
            }
165
84.2k
            while (n > MEMRCHR_CUT_OFF);
166
84.2k
        }
167
#endif
168
95.8k
    }
169
84.2k
#endif  /* HAVE_MEMRCHR */
170
84.2k
    p = s + n;
171
605k
    while (p > s) {
172
585k
        p--;
173
585k
        if (*p == ch)
174
64.8k
            return (p - s);
175
585k
    }
176
19.4k
    return -1;
177
84.2k
}
Unexecuted instantiation: bytesobject.c:stringlib_rfind_char
unicodeobject.c:ucs1lib_rfind_char
Line
Count
Source
117
8.06k
{
118
8.06k
    const STRINGLIB_CHAR *p;
119
8.06k
#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
8.06k
    if (n > MEMRCHR_CUT_OFF) {
126
4.92k
#if STRINGLIB_SIZEOF_CHAR == 1
127
4.92k
        p = memrchr(s, ch, n);
128
4.92k
        if (p != NULL)
129
3.88k
            return (p - s);
130
1.04k
        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
4.92k
    }
169
3.13k
#endif  /* HAVE_MEMRCHR */
170
3.13k
    p = s + n;
171
9.54k
    while (p > s) {
172
8.71k
        p--;
173
8.71k
        if (*p == ch)
174
2.30k
            return (p - s);
175
8.71k
    }
176
832
    return -1;
177
3.13k
}
unicodeobject.c:ucs2lib_rfind_char
Line
Count
Source
117
26.6k
{
118
26.6k
    const STRINGLIB_CHAR *p;
119
26.6k
#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
26.6k
    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
16.8k
        const STRINGLIB_CHAR *s1;
135
16.8k
        Py_ssize_t n1;
136
16.8k
        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
16.8k
        if (needle != 0) {
141
18.4k
            do {
142
18.4k
                void *candidate = memrchr(s, needle,
143
18.4k
                                          n * sizeof(STRINGLIB_CHAR));
144
18.4k
                if (candidate == NULL)
145
429
                    return -1;
146
18.0k
                n1 = n;
147
18.0k
                p = (const STRINGLIB_CHAR *)
148
18.0k
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
149
18.0k
                n = p - s;
150
18.0k
                if (*p == ch)
151
15.2k
                    return n;
152
                /* False positive */
153
2.80k
                if (n1 - n > MEMRCHR_CUT_OFF)
154
1.09k
                    continue;
155
1.70k
                if (n <= MEMRCHR_CUT_OFF)
156
562
                    break;
157
1.14k
                s1 = p - MEMRCHR_CUT_OFF;
158
40.5k
                while (p > s1) {
159
39.6k
                    p--;
160
39.6k
                    if (*p == ch)
161
246
                        return (p - s);
162
39.6k
                }
163
900
                n = p - s;
164
900
            }
165
16.8k
            while (n > MEMRCHR_CUT_OFF);
166
16.8k
        }
167
16.8k
#endif
168
16.8k
    }
169
10.7k
#endif  /* HAVE_MEMRCHR */
170
10.7k
    p = s + n;
171
75.4k
    while (p > s) {
172
73.7k
        p--;
173
73.7k
        if (*p == ch)
174
9.07k
            return (p - s);
175
73.7k
    }
176
1.65k
    return -1;
177
10.7k
}
unicodeobject.c:ucs4lib_rfind_char
Line
Count
Source
117
110k
{
118
110k
    const STRINGLIB_CHAR *p;
119
110k
#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
110k
    if (n > MEMRCHR_CUT_OFF) {
126
#if STRINGLIB_SIZEOF_CHAR == 1
127
        p = memrchr(s, ch, n);
128
        if (p != NULL)
129
            return (p - s);
130
        return -1;
131
#else
132
        /* use memrchr if we can choose a needle without too many likely
133
           false positives */
134
67.3k
        const STRINGLIB_CHAR *s1;
135
67.3k
        Py_ssize_t n1;
136
67.3k
        unsigned char needle = ch & 0xff;
137
        /* If looking for a multiple of 256, we'd have too
138
           many false positives looking for the '\0' byte in UCS2
139
           and UCS4 representations. */
140
67.3k
        if (needle != 0) {
141
71.9k
            do {
142
71.9k
                void *candidate = memrchr(s, needle,
143
71.9k
                                          n * sizeof(STRINGLIB_CHAR));
144
71.9k
                if (candidate == NULL)
145
290
                    return -1;
146
71.6k
                n1 = n;
147
71.6k
                p = (const STRINGLIB_CHAR *)
148
71.6k
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
149
71.6k
                n = p - s;
150
71.6k
                if (*p == ch)
151
65.5k
                    return n;
152
                /* False positive */
153
6.06k
                if (n1 - n > MEMRCHR_CUT_OFF)
154
2.41k
                    continue;
155
3.64k
                if (n <= MEMRCHR_CUT_OFF)
156
637
                    break;
157
3.00k
                s1 = p - MEMRCHR_CUT_OFF;
158
114k
                while (p > s1) {
159
112k
                    p--;
160
112k
                    if (*p == ch)
161
333
                        return (p - s);
162
112k
                }
163
2.67k
                n = p - s;
164
2.67k
            }
165
67.3k
            while (n > MEMRCHR_CUT_OFF);
166
67.3k
        }
167
67.3k
#endif
168
67.3k
    }
169
43.8k
#endif  /* HAVE_MEMRCHR */
170
43.8k
    p = s + n;
171
380k
    while (p > s) {
172
379k
        p--;
173
379k
        if (*p == ch)
174
42.4k
            return (p - s);
175
379k
    }
176
1.35k
    return -1;
177
43.8k
}
unicodeobject.c:asciilib_rfind_char
Line
Count
Source
117
12.4k
{
118
12.4k
    const STRINGLIB_CHAR *p;
119
12.4k
#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
12.4k
    if (n > MEMRCHR_CUT_OFF) {
126
3.58k
#if STRINGLIB_SIZEOF_CHAR == 1
127
3.58k
        p = memrchr(s, ch, n);
128
3.58k
        if (p != NULL)
129
3.47k
            return (p - s);
130
116
        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.58k
    }
169
8.89k
#endif  /* HAVE_MEMRCHR */
170
8.89k
    p = s + n;
171
48.3k
    while (p > s) {
172
45.4k
        p--;
173
45.4k
        if (*p == ch)
174
6.00k
            return (p - s);
175
45.4k
    }
176
2.89k
    return -1;
177
8.89k
}
bytes_methods.c:stringlib_rfind_char
Line
Count
Source
117
20.7k
{
118
20.7k
    const STRINGLIB_CHAR *p;
119
20.7k
#ifdef HAVE_MEMRCHR
120
    /* memrchr() is a GNU extension, available since glibc 2.1.91.  it
121
       doesn't seem as optimized as memchr(), but is still quite
122
       faster than our hand-written loop below. There is no wmemrchr
123
       for 4-byte chars. */
124
125
20.7k
    if (n > MEMRCHR_CUT_OFF) {
126
3.08k
#if STRINGLIB_SIZEOF_CHAR == 1
127
3.08k
        p = memrchr(s, ch, n);
128
3.08k
        if (p != NULL)
129
857
            return (p - s);
130
2.22k
        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.08k
    }
169
17.6k
#endif  /* HAVE_MEMRCHR */
170
17.6k
    p = s + n;
171
91.0k
    while (p > s) {
172
78.3k
        p--;
173
78.3k
        if (*p == ch)
174
4.93k
            return (p - s);
175
78.3k
    }
176
12.6k
    return -1;
177
17.6k
}
Unexecuted instantiation: bytearrayobject.c:stringlib_rfind_char
178
179
#undef MEMRCHR_CUT_OFF
180
181
/* Change to a 1 to see logging comments walk through the algorithm. */
182
#if 0 && STRINGLIB_SIZEOF_CHAR == 1
183
# define LOG(...) printf(__VA_ARGS__)
184
# define LOG_STRING(s, n) printf("\"%.*s\"", (int)(n), s)
185
# define LOG_LINEUP() do {                                         \
186
    LOG("> "); LOG_STRING(haystack, len_haystack); LOG("\n> ");    \
187
    LOG("%*s",(int)(window_last - haystack + 1 - len_needle), ""); \
188
    LOG_STRING(needle, len_needle); LOG("\n");                     \
189
} while(0)
190
#else
191
# define LOG(...)
192
# define LOG_STRING(s, n)
193
# define LOG_LINEUP()
194
#endif
195
196
Py_LOCAL_INLINE(Py_ssize_t)
197
STRINGLIB(_lex_search)(const STRINGLIB_CHAR *needle, Py_ssize_t len_needle,
198
                       Py_ssize_t *return_period, int invert_alphabet)
199
46
{
200
    /* Do a lexicographic search. Essentially this:
201
           >>> max(needle[i:] for i in range(len(needle)+1))
202
       Also find the period of the right half.   */
203
46
    Py_ssize_t max_suffix = 0;
204
46
    Py_ssize_t candidate = 1;
205
46
    Py_ssize_t k = 0;
206
    // The period of the right half.
207
46
    Py_ssize_t period = 1;
208
209
460
    while (candidate + k < len_needle) {
210
        // each loop increases candidate + k + max_suffix
211
414
        STRINGLIB_CHAR a = needle[candidate + k];
212
414
        STRINGLIB_CHAR b = needle[max_suffix + k];
213
        // check if the suffix at candidate is better than max_suffix
214
414
        if (invert_alphabet ? (b < a) : (a < b)) {
215
            // Fell short of max_suffix.
216
            // The next k + 1 characters are non-increasing
217
            // from candidate, so they won't start a maximal suffix.
218
299
            candidate += k + 1;
219
299
            k = 0;
220
            // We've ruled out any period smaller than what's
221
            // been scanned since max_suffix.
222
299
            period = candidate - max_suffix;
223
299
        }
224
115
        else if (a == b) {
225
23
            if (k + 1 != period) {
226
                // Keep scanning the equal strings
227
0
                k++;
228
0
            }
229
23
            else {
230
                // Matched a whole period.
231
                // Start matching the next period.
232
23
                candidate += period;
233
23
                k = 0;
234
23
            }
235
23
        }
236
92
        else {
237
            // Did better than max_suffix, so replace it.
238
92
            max_suffix = candidate;
239
92
            candidate++;
240
92
            k = 0;
241
92
            period = 1;
242
92
        }
243
414
    }
244
46
    *return_period = period;
245
46
    return max_suffix;
246
46
}
Unexecuted instantiation: bytesobject.c:stringlib__lex_search
Unexecuted instantiation: unicodeobject.c:asciilib__lex_search
unicodeobject.c:ucs1lib__lex_search
Line
Count
Source
199
46
{
200
    /* Do a lexicographic search. Essentially this:
201
           >>> max(needle[i:] for i in range(len(needle)+1))
202
       Also find the period of the right half.   */
203
46
    Py_ssize_t max_suffix = 0;
204
46
    Py_ssize_t candidate = 1;
205
46
    Py_ssize_t k = 0;
206
    // The period of the right half.
207
46
    Py_ssize_t period = 1;
208
209
460
    while (candidate + k < len_needle) {
210
        // each loop increases candidate + k + max_suffix
211
414
        STRINGLIB_CHAR a = needle[candidate + k];
212
414
        STRINGLIB_CHAR b = needle[max_suffix + k];
213
        // check if the suffix at candidate is better than max_suffix
214
414
        if (invert_alphabet ? (b < a) : (a < b)) {
215
            // Fell short of max_suffix.
216
            // The next k + 1 characters are non-increasing
217
            // from candidate, so they won't start a maximal suffix.
218
299
            candidate += k + 1;
219
299
            k = 0;
220
            // We've ruled out any period smaller than what's
221
            // been scanned since max_suffix.
222
299
            period = candidate - max_suffix;
223
299
        }
224
115
        else if (a == b) {
225
23
            if (k + 1 != period) {
226
                // Keep scanning the equal strings
227
0
                k++;
228
0
            }
229
23
            else {
230
                // Matched a whole period.
231
                // Start matching the next period.
232
23
                candidate += period;
233
23
                k = 0;
234
23
            }
235
23
        }
236
92
        else {
237
            // Did better than max_suffix, so replace it.
238
92
            max_suffix = candidate;
239
92
            candidate++;
240
92
            k = 0;
241
92
            period = 1;
242
92
        }
243
414
    }
244
46
    *return_period = period;
245
46
    return max_suffix;
246
46
}
Unexecuted instantiation: unicodeobject.c:ucs2lib__lex_search
Unexecuted instantiation: unicodeobject.c:ucs4lib__lex_search
Unexecuted instantiation: bytes_methods.c:stringlib__lex_search
Unexecuted instantiation: bytearrayobject.c:stringlib__lex_search
247
248
Py_LOCAL_INLINE(Py_ssize_t)
249
STRINGLIB(_factorize)(const STRINGLIB_CHAR *needle,
250
                      Py_ssize_t len_needle,
251
                      Py_ssize_t *return_period)
252
23
{
253
    /* Do a "critical factorization", making it so that:
254
       >>> needle = (left := needle[:cut]) + (right := needle[cut:])
255
       where the "local period" of the cut is maximal.
256
257
       The local period of the cut is the minimal length of a string w
258
       such that (left endswith w or w endswith left)
259
       and (right startswith w or w startswith right).
260
261
       The Critical Factorization Theorem says that this maximal local
262
       period is the global period of the string.
263
264
       Crochemore and Perrin (1991) show that this cut can be computed
265
       as the later of two cuts: one that gives a lexicographically
266
       maximal right half, and one that gives the same with the
267
       with respect to a reversed alphabet-ordering.
268
269
       This is what we want to happen:
270
           >>> x = "GCAGAGAG"
271
           >>> cut, period = factorize(x)
272
           >>> x[:cut], (right := x[cut:])
273
           ('GC', 'AGAGAG')
274
           >>> period  # right half period
275
           2
276
           >>> right[period:] == right[:-period]
277
           True
278
279
       This is how the local period lines up in the above example:
280
                GC | AGAGAG
281
           AGAGAGC = AGAGAGC
282
       The length of this minimal repetition is 7, which is indeed the
283
       period of the original string. */
284
285
23
    Py_ssize_t cut1, period1, cut2, period2, cut, period;
286
23
    cut1 = STRINGLIB(_lex_search)(needle, len_needle, &period1, 0);
287
23
    cut2 = STRINGLIB(_lex_search)(needle, len_needle, &period2, 1);
288
289
    // Take the later cut.
290
23
    if (cut1 > cut2) {
291
23
        period = period1;
292
23
        cut = cut1;
293
23
    }
294
0
    else {
295
0
        period = period2;
296
0
        cut = cut2;
297
0
    }
298
299
23
    LOG("split: "); LOG_STRING(needle, cut);
300
23
    LOG(" + "); LOG_STRING(needle + cut, len_needle - cut);
301
23
    LOG("\n");
302
303
23
    *return_period = period;
304
23
    return cut;
305
23
}
Unexecuted instantiation: bytesobject.c:stringlib__factorize
Unexecuted instantiation: unicodeobject.c:asciilib__factorize
unicodeobject.c:ucs1lib__factorize
Line
Count
Source
252
23
{
253
    /* Do a "critical factorization", making it so that:
254
       >>> needle = (left := needle[:cut]) + (right := needle[cut:])
255
       where the "local period" of the cut is maximal.
256
257
       The local period of the cut is the minimal length of a string w
258
       such that (left endswith w or w endswith left)
259
       and (right startswith w or w startswith right).
260
261
       The Critical Factorization Theorem says that this maximal local
262
       period is the global period of the string.
263
264
       Crochemore and Perrin (1991) show that this cut can be computed
265
       as the later of two cuts: one that gives a lexicographically
266
       maximal right half, and one that gives the same with the
267
       with respect to a reversed alphabet-ordering.
268
269
       This is what we want to happen:
270
           >>> x = "GCAGAGAG"
271
           >>> cut, period = factorize(x)
272
           >>> x[:cut], (right := x[cut:])
273
           ('GC', 'AGAGAG')
274
           >>> period  # right half period
275
           2
276
           >>> right[period:] == right[:-period]
277
           True
278
279
       This is how the local period lines up in the above example:
280
                GC | AGAGAG
281
           AGAGAGC = AGAGAGC
282
       The length of this minimal repetition is 7, which is indeed the
283
       period of the original string. */
284
285
23
    Py_ssize_t cut1, period1, cut2, period2, cut, period;
286
23
    cut1 = STRINGLIB(_lex_search)(needle, len_needle, &period1, 0);
287
23
    cut2 = STRINGLIB(_lex_search)(needle, len_needle, &period2, 1);
288
289
    // Take the later cut.
290
23
    if (cut1 > cut2) {
291
23
        period = period1;
292
23
        cut = cut1;
293
23
    }
294
0
    else {
295
0
        period = period2;
296
0
        cut = cut2;
297
0
    }
298
299
23
    LOG("split: "); LOG_STRING(needle, cut);
300
23
    LOG(" + "); LOG_STRING(needle + cut, len_needle - cut);
301
23
    LOG("\n");
302
303
23
    *return_period = period;
304
23
    return cut;
305
23
}
Unexecuted instantiation: unicodeobject.c:ucs2lib__factorize
Unexecuted instantiation: unicodeobject.c:ucs4lib__factorize
Unexecuted instantiation: bytes_methods.c:stringlib__factorize
Unexecuted instantiation: bytearrayobject.c:stringlib__factorize
306
307
308
253
#define SHIFT_TYPE uint8_t
309
#define MAX_SHIFT UINT8_MAX
310
311
79.3k
#define TABLE_SIZE_BITS 6u
312
79.3k
#define TABLE_SIZE (1U << TABLE_SIZE_BITS)
313
77.8k
#define TABLE_MASK (TABLE_SIZE - 1U)
314
315
typedef struct STRINGLIB(_pre) {
316
    const STRINGLIB_CHAR *needle;
317
    Py_ssize_t len_needle;
318
    Py_ssize_t cut;
319
    Py_ssize_t period;
320
    Py_ssize_t gap;
321
    int is_periodic;
322
    SHIFT_TYPE table[TABLE_SIZE];
323
} STRINGLIB(prework);
324
325
326
static void
327
STRINGLIB(_preprocess)(const STRINGLIB_CHAR *needle, Py_ssize_t len_needle,
328
                       STRINGLIB(prework) *p)
329
23
{
330
23
    p->needle = needle;
331
23
    p->len_needle = len_needle;
332
23
    p->cut = STRINGLIB(_factorize)(needle, len_needle, &(p->period));
333
23
    assert(p->period + p->cut <= len_needle);
334
23
    p->is_periodic = (0 == memcmp(needle,
335
23
                                  needle + p->period,
336
23
                                  p->cut * STRINGLIB_SIZEOF_CHAR));
337
23
    if (p->is_periodic) {
338
0
        assert(p->cut <= len_needle/2);
339
0
        assert(p->cut < p->period);
340
0
    }
341
23
    else {
342
        // A lower bound on the period
343
23
        p->period = Py_MAX(p->cut, len_needle - p->cut) + 1;
344
23
    }
345
    // The gap between the last character and the previous
346
    // occurrence of an equivalent character (modulo TABLE_SIZE)
347
23
    p->gap = len_needle;
348
23
    STRINGLIB_CHAR last = needle[len_needle - 1] & TABLE_MASK;
349
161
    for (Py_ssize_t i = len_needle - 2; i >= 0; i--) {
350
161
        STRINGLIB_CHAR x = needle[i] & TABLE_MASK;
351
161
        if (x == last) {
352
23
            p->gap = len_needle - 1 - i;
353
23
            break;
354
23
        }
355
161
    }
356
    // Fill up a compressed Boyer-Moore "Bad Character" table
357
23
    Py_ssize_t not_found_shift = Py_MIN(len_needle, MAX_SHIFT);
358
1.49k
    for (Py_ssize_t i = 0; i < (Py_ssize_t)TABLE_SIZE; i++) {
359
1.47k
        p->table[i] = Py_SAFE_DOWNCAST(not_found_shift,
360
1.47k
                                       Py_ssize_t, SHIFT_TYPE);
361
1.47k
    }
362
253
    for (Py_ssize_t i = len_needle - not_found_shift; i < len_needle; i++) {
363
230
        SHIFT_TYPE shift = Py_SAFE_DOWNCAST(len_needle - 1 - i,
364
230
                                            Py_ssize_t, SHIFT_TYPE);
365
230
        p->table[needle[i] & TABLE_MASK] = shift;
366
230
    }
367
23
}
Unexecuted instantiation: bytesobject.c:stringlib__preprocess
Unexecuted instantiation: unicodeobject.c:asciilib__preprocess
unicodeobject.c:ucs1lib__preprocess
Line
Count
Source
329
23
{
330
23
    p->needle = needle;
331
23
    p->len_needle = len_needle;
332
23
    p->cut = STRINGLIB(_factorize)(needle, len_needle, &(p->period));
333
23
    assert(p->period + p->cut <= len_needle);
334
23
    p->is_periodic = (0 == memcmp(needle,
335
23
                                  needle + p->period,
336
23
                                  p->cut * STRINGLIB_SIZEOF_CHAR));
337
23
    if (p->is_periodic) {
338
0
        assert(p->cut <= len_needle/2);
339
0
        assert(p->cut < p->period);
340
0
    }
341
23
    else {
342
        // A lower bound on the period
343
23
        p->period = Py_MAX(p->cut, len_needle - p->cut) + 1;
344
23
    }
345
    // The gap between the last character and the previous
346
    // occurrence of an equivalent character (modulo TABLE_SIZE)
347
23
    p->gap = len_needle;
348
23
    STRINGLIB_CHAR last = needle[len_needle - 1] & TABLE_MASK;
349
161
    for (Py_ssize_t i = len_needle - 2; i >= 0; i--) {
350
161
        STRINGLIB_CHAR x = needle[i] & TABLE_MASK;
351
161
        if (x == last) {
352
23
            p->gap = len_needle - 1 - i;
353
23
            break;
354
23
        }
355
161
    }
356
    // Fill up a compressed Boyer-Moore "Bad Character" table
357
23
    Py_ssize_t not_found_shift = Py_MIN(len_needle, MAX_SHIFT);
358
1.49k
    for (Py_ssize_t i = 0; i < (Py_ssize_t)TABLE_SIZE; i++) {
359
1.47k
        p->table[i] = Py_SAFE_DOWNCAST(not_found_shift,
360
1.47k
                                       Py_ssize_t, SHIFT_TYPE);
361
1.47k
    }
362
253
    for (Py_ssize_t i = len_needle - not_found_shift; i < len_needle; i++) {
363
230
        SHIFT_TYPE shift = Py_SAFE_DOWNCAST(len_needle - 1 - i,
364
230
                                            Py_ssize_t, SHIFT_TYPE);
365
230
        p->table[needle[i] & TABLE_MASK] = shift;
366
230
    }
367
23
}
Unexecuted instantiation: unicodeobject.c:ucs2lib__preprocess
Unexecuted instantiation: unicodeobject.c:ucs4lib__preprocess
Unexecuted instantiation: bytes_methods.c:stringlib__preprocess
Unexecuted instantiation: bytearrayobject.c:stringlib__preprocess
368
369
static Py_ssize_t
370
STRINGLIB(_two_way)(const STRINGLIB_CHAR *haystack, Py_ssize_t len_haystack,
371
                    STRINGLIB(prework) *p)
372
23
{
373
    // Crochemore and Perrin's (1991) Two-Way algorithm.
374
    // See http://www-igm.univ-mlv.fr/~lecroq/string/node26.html#SECTION00260
375
23
    const Py_ssize_t len_needle = p->len_needle;
376
23
    const Py_ssize_t cut = p->cut;
377
23
    Py_ssize_t period = p->period;
378
23
    const STRINGLIB_CHAR *const needle = p->needle;
379
23
    const STRINGLIB_CHAR *window_last = haystack + len_needle - 1;
380
23
    const STRINGLIB_CHAR *const haystack_end = haystack + len_haystack;
381
23
    SHIFT_TYPE *table = p->table;
382
23
    const STRINGLIB_CHAR *window;
383
23
    LOG("===== Two-way: \"%s\" in \"%s\". =====\n", needle, haystack);
384
385
23
    Py_ssize_t gap = p->gap;
386
23
    Py_ssize_t gap_jump_end = Py_MIN(len_needle, cut + gap);
387
23
    if (p->is_periodic) {
388
0
        LOG("Needle is periodic.\n");
389
0
        Py_ssize_t memory = 0;
390
0
      periodicwindowloop:
391
0
        while (window_last < haystack_end) {
392
0
            assert(memory == 0);
393
0
            for (;;) {
394
0
                LOG_LINEUP();
395
0
                Py_ssize_t shift = table[(*window_last) & TABLE_MASK];
396
0
                window_last += shift;
397
0
                if (shift == 0) {
398
0
                    break;
399
0
                }
400
0
                if (window_last >= haystack_end) {
401
0
                    return -1;
402
0
                }
403
0
                LOG("Horspool skip\n");
404
0
            }
405
0
          no_shift:
406
0
            window = window_last - len_needle + 1;
407
0
            assert((window[len_needle - 1] & TABLE_MASK) ==
408
0
                   (needle[len_needle - 1] & TABLE_MASK));
409
0
            Py_ssize_t i = Py_MAX(cut, memory);
410
0
            for (; i < len_needle; i++) {
411
0
                if (needle[i] != window[i]) {
412
0
                    if (i < gap_jump_end) {
413
0
                        LOG("Early right half mismatch: jump by gap.\n");
414
0
                        assert(gap >= i - cut + 1);
415
0
                        window_last += gap;
416
0
                    }
417
0
                    else {
418
0
                        LOG("Late right half mismatch: jump by n (>gap)\n");
419
0
                        assert(i - cut + 1 > gap);
420
0
                        window_last += i - cut + 1;
421
0
                    }
422
0
                    memory = 0;
423
0
                    goto periodicwindowloop;
424
0
                }
425
0
            }
426
0
            for (i = memory; i < cut; i++) {
427
0
                if (needle[i] != window[i]) {
428
0
                    LOG("Left half does not match.\n");
429
0
                    window_last += period;
430
0
                    memory = len_needle - period;
431
0
                    if (window_last >= haystack_end) {
432
0
                        return -1;
433
0
                    }
434
0
                    Py_ssize_t shift = table[(*window_last) & TABLE_MASK];
435
0
                    if (shift) {
436
                        // A mismatch has been identified to the right
437
                        // of where i will next start, so we can jump
438
                        // at least as far as if the mismatch occurred
439
                        // on the first comparison.
440
0
                        Py_ssize_t mem_jump = Py_MAX(cut, memory) - cut + 1;
441
0
                        LOG("Skip with Memory.\n");
442
0
                        memory = 0;
443
0
                        window_last += Py_MAX(shift, mem_jump);
444
0
                        goto periodicwindowloop;
445
0
                    }
446
0
                    goto no_shift;
447
0
                }
448
0
            }
449
0
            LOG("Found a match!\n");
450
0
            return window - haystack;
451
0
        }
452
0
    }
453
23
    else {
454
23
        period = Py_MAX(gap, period);
455
23
        LOG("Needle is not periodic.\n");
456
16.3k
      windowloop:
457
16.3k
        while (window_last < haystack_end) {
458
77.4k
            for (;;) {
459
77.4k
                LOG_LINEUP();
460
77.4k
                Py_ssize_t shift = table[(*window_last) & TABLE_MASK];
461
77.4k
                window_last += shift;
462
77.4k
                if (shift == 0) {
463
16.3k
                    break;
464
16.3k
                }
465
61.0k
                if (window_last >= haystack_end) {
466
18
                    return -1;
467
18
                }
468
61.0k
                LOG("Horspool skip\n");
469
61.0k
            }
470
16.3k
            window = window_last - len_needle + 1;
471
16.3k
            assert((window[len_needle - 1] & TABLE_MASK) ==
472
16.3k
                   (needle[len_needle - 1] & TABLE_MASK));
473
16.3k
            Py_ssize_t i = cut;
474
16.5k
            for (; i < len_needle; i++) {
475
16.4k
                if (needle[i] != window[i]) {
476
16.2k
                    if (i < gap_jump_end) {
477
16.2k
                        LOG("Early right half mismatch: jump by gap.\n");
478
16.2k
                        assert(gap >= i - cut + 1);
479
16.2k
                        window_last += gap;
480
16.2k
                    }
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
16.2k
                    goto windowloop;
487
16.2k
                }
488
16.4k
            }
489
122
            for (Py_ssize_t i = 0; i < cut; i++) {
490
118
                if (needle[i] != window[i]) {
491
86
                    LOG("Left half does not match.\n");
492
86
                    window_last += period;
493
86
                    goto windowloop;
494
86
                }
495
118
            }
496
4
            LOG("Found a match!\n");
497
4
            return window - haystack;
498
90
        }
499
16.3k
    }
500
1
    LOG("Not found. Returning -1.\n");
501
1
    return -1;
502
23
}
Unexecuted instantiation: bytesobject.c:stringlib__two_way
Unexecuted instantiation: unicodeobject.c:asciilib__two_way
unicodeobject.c:ucs1lib__two_way
Line
Count
Source
372
23
{
373
    // Crochemore and Perrin's (1991) Two-Way algorithm.
374
    // See http://www-igm.univ-mlv.fr/~lecroq/string/node26.html#SECTION00260
375
23
    const Py_ssize_t len_needle = p->len_needle;
376
23
    const Py_ssize_t cut = p->cut;
377
23
    Py_ssize_t period = p->period;
378
23
    const STRINGLIB_CHAR *const needle = p->needle;
379
23
    const STRINGLIB_CHAR *window_last = haystack + len_needle - 1;
380
23
    const STRINGLIB_CHAR *const haystack_end = haystack + len_haystack;
381
23
    SHIFT_TYPE *table = p->table;
382
23
    const STRINGLIB_CHAR *window;
383
23
    LOG("===== Two-way: \"%s\" in \"%s\". =====\n", needle, haystack);
384
385
23
    Py_ssize_t gap = p->gap;
386
23
    Py_ssize_t gap_jump_end = Py_MIN(len_needle, cut + gap);
387
23
    if (p->is_periodic) {
388
0
        LOG("Needle is periodic.\n");
389
0
        Py_ssize_t memory = 0;
390
0
      periodicwindowloop:
391
0
        while (window_last < haystack_end) {
392
0
            assert(memory == 0);
393
0
            for (;;) {
394
0
                LOG_LINEUP();
395
0
                Py_ssize_t shift = table[(*window_last) & TABLE_MASK];
396
0
                window_last += shift;
397
0
                if (shift == 0) {
398
0
                    break;
399
0
                }
400
0
                if (window_last >= haystack_end) {
401
0
                    return -1;
402
0
                }
403
0
                LOG("Horspool skip\n");
404
0
            }
405
0
          no_shift:
406
0
            window = window_last - len_needle + 1;
407
0
            assert((window[len_needle - 1] & TABLE_MASK) ==
408
0
                   (needle[len_needle - 1] & TABLE_MASK));
409
0
            Py_ssize_t i = Py_MAX(cut, memory);
410
0
            for (; i < len_needle; i++) {
411
0
                if (needle[i] != window[i]) {
412
0
                    if (i < gap_jump_end) {
413
0
                        LOG("Early right half mismatch: jump by gap.\n");
414
0
                        assert(gap >= i - cut + 1);
415
0
                        window_last += gap;
416
0
                    }
417
0
                    else {
418
0
                        LOG("Late right half mismatch: jump by n (>gap)\n");
419
0
                        assert(i - cut + 1 > gap);
420
0
                        window_last += i - cut + 1;
421
0
                    }
422
0
                    memory = 0;
423
0
                    goto periodicwindowloop;
424
0
                }
425
0
            }
426
0
            for (i = memory; i < cut; i++) {
427
0
                if (needle[i] != window[i]) {
428
0
                    LOG("Left half does not match.\n");
429
0
                    window_last += period;
430
0
                    memory = len_needle - period;
431
0
                    if (window_last >= haystack_end) {
432
0
                        return -1;
433
0
                    }
434
0
                    Py_ssize_t shift = table[(*window_last) & TABLE_MASK];
435
0
                    if (shift) {
436
                        // A mismatch has been identified to the right
437
                        // of where i will next start, so we can jump
438
                        // at least as far as if the mismatch occurred
439
                        // on the first comparison.
440
0
                        Py_ssize_t mem_jump = Py_MAX(cut, memory) - cut + 1;
441
0
                        LOG("Skip with Memory.\n");
442
0
                        memory = 0;
443
0
                        window_last += Py_MAX(shift, mem_jump);
444
0
                        goto periodicwindowloop;
445
0
                    }
446
0
                    goto no_shift;
447
0
                }
448
0
            }
449
0
            LOG("Found a match!\n");
450
0
            return window - haystack;
451
0
        }
452
0
    }
453
23
    else {
454
23
        period = Py_MAX(gap, period);
455
23
        LOG("Needle is not periodic.\n");
456
16.3k
      windowloop:
457
16.3k
        while (window_last < haystack_end) {
458
77.4k
            for (;;) {
459
77.4k
                LOG_LINEUP();
460
77.4k
                Py_ssize_t shift = table[(*window_last) & TABLE_MASK];
461
77.4k
                window_last += shift;
462
77.4k
                if (shift == 0) {
463
16.3k
                    break;
464
16.3k
                }
465
61.0k
                if (window_last >= haystack_end) {
466
18
                    return -1;
467
18
                }
468
61.0k
                LOG("Horspool skip\n");
469
61.0k
            }
470
16.3k
            window = window_last - len_needle + 1;
471
16.3k
            assert((window[len_needle - 1] & TABLE_MASK) ==
472
16.3k
                   (needle[len_needle - 1] & TABLE_MASK));
473
16.3k
            Py_ssize_t i = cut;
474
16.5k
            for (; i < len_needle; i++) {
475
16.4k
                if (needle[i] != window[i]) {
476
16.2k
                    if (i < gap_jump_end) {
477
16.2k
                        LOG("Early right half mismatch: jump by gap.\n");
478
16.2k
                        assert(gap >= i - cut + 1);
479
16.2k
                        window_last += gap;
480
16.2k
                    }
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
16.2k
                    goto windowloop;
487
16.2k
                }
488
16.4k
            }
489
122
            for (Py_ssize_t i = 0; i < cut; i++) {
490
118
                if (needle[i] != window[i]) {
491
86
                    LOG("Left half does not match.\n");
492
86
                    window_last += period;
493
86
                    goto windowloop;
494
86
                }
495
118
            }
496
4
            LOG("Found a match!\n");
497
4
            return window - haystack;
498
90
        }
499
16.3k
    }
500
1
    LOG("Not found. Returning -1.\n");
501
1
    return -1;
502
23
}
Unexecuted instantiation: unicodeobject.c:ucs2lib__two_way
Unexecuted instantiation: unicodeobject.c:ucs4lib__two_way
Unexecuted instantiation: bytes_methods.c:stringlib__two_way
Unexecuted instantiation: bytearrayobject.c:stringlib__two_way
503
504
505
static Py_ssize_t
506
STRINGLIB(_two_way_find)(const STRINGLIB_CHAR *haystack,
507
                         Py_ssize_t len_haystack,
508
                         const STRINGLIB_CHAR *needle,
509
                         Py_ssize_t len_needle)
510
23
{
511
23
    LOG("###### Finding \"%s\" in \"%s\".\n", needle, haystack);
512
23
    STRINGLIB(prework) p;
513
23
    STRINGLIB(_preprocess)(needle, len_needle, &p);
514
23
    return STRINGLIB(_two_way)(haystack, len_haystack, &p);
515
23
}
Unexecuted instantiation: bytesobject.c:stringlib__two_way_find
Unexecuted instantiation: unicodeobject.c:asciilib__two_way_find
unicodeobject.c:ucs1lib__two_way_find
Line
Count
Source
510
23
{
511
23
    LOG("###### Finding \"%s\" in \"%s\".\n", needle, haystack);
512
23
    STRINGLIB(prework) p;
513
23
    STRINGLIB(_preprocess)(needle, len_needle, &p);
514
23
    return STRINGLIB(_two_way)(haystack, len_haystack, &p);
515
23
}
Unexecuted instantiation: unicodeobject.c:ucs2lib__two_way_find
Unexecuted instantiation: unicodeobject.c:ucs4lib__two_way_find
Unexecuted instantiation: bytes_methods.c:stringlib__two_way_find
Unexecuted instantiation: bytearrayobject.c:stringlib__two_way_find
516
517
518
static Py_ssize_t
519
STRINGLIB(_two_way_count)(const STRINGLIB_CHAR *haystack,
520
                          Py_ssize_t len_haystack,
521
                          const STRINGLIB_CHAR *needle,
522
                          Py_ssize_t len_needle,
523
                          Py_ssize_t maxcount)
524
0
{
525
0
    LOG("###### Counting \"%s\" in \"%s\".\n", needle, haystack);
526
0
    STRINGLIB(prework) p;
527
0
    STRINGLIB(_preprocess)(needle, len_needle, &p);
528
0
    Py_ssize_t index = 0, count = 0;
529
0
    while (1) {
530
0
        Py_ssize_t result;
531
0
        result = STRINGLIB(_two_way)(haystack + index,
532
0
                                     len_haystack - index, &p);
533
0
        if (result == -1) {
534
0
            return count;
535
0
        }
536
0
        count++;
537
0
        if (count == maxcount) {
538
0
            return maxcount;
539
0
        }
540
0
        index += result + len_needle;
541
0
    }
542
0
    return count;
543
0
}
Unexecuted instantiation: bytesobject.c:stringlib__two_way_count
Unexecuted instantiation: unicodeobject.c:asciilib__two_way_count
Unexecuted instantiation: unicodeobject.c:ucs1lib__two_way_count
Unexecuted instantiation: unicodeobject.c:ucs2lib__two_way_count
Unexecuted instantiation: unicodeobject.c:ucs4lib__two_way_count
Unexecuted instantiation: bytes_methods.c:stringlib__two_way_count
Unexecuted instantiation: bytearrayobject.c:stringlib__two_way_count
544
545
#undef SHIFT_TYPE
546
#undef NOT_FOUND
547
#undef SHIFT_OVERFLOW
548
#undef TABLE_SIZE_BITS
549
#undef TABLE_SIZE
550
#undef TABLE_MASK
551
552
#undef LOG
553
#undef LOG_STRING
554
#undef LOG_LINEUP
555
556
static inline Py_ssize_t
557
STRINGLIB(default_find)(const STRINGLIB_CHAR* s, Py_ssize_t n,
558
                        const STRINGLIB_CHAR* p, Py_ssize_t m,
559
                        Py_ssize_t maxcount, int mode)
560
15.4M
{
561
15.4M
    const Py_ssize_t w = n - m;
562
15.4M
    Py_ssize_t mlast = m - 1, count = 0;
563
15.4M
    Py_ssize_t gap = mlast;
564
15.4M
    const STRINGLIB_CHAR last = p[mlast];
565
15.4M
    const STRINGLIB_CHAR *const ss = &s[mlast];
566
567
15.4M
    unsigned long mask = 0;
568
31.2M
    for (Py_ssize_t i = 0; i < mlast; i++) {
569
15.7M
        STRINGLIB_BLOOM_ADD(mask, p[i]);
570
15.7M
        if (p[i] == last) {
571
603k
            gap = mlast - i - 1;
572
603k
        }
573
15.7M
    }
574
15.4M
    STRINGLIB_BLOOM_ADD(mask, last);
575
576
3.13G
    for (Py_ssize_t i = 0; i <= w; i++) {
577
3.13G
        if (ss[i] == last) {
578
            /* candidate match */
579
33.3M
            Py_ssize_t j;
580
53.9M
            for (j = 0; j < mlast; j++) {
581
33.4M
                if (s[i+j] != p[j]) {
582
12.9M
                    break;
583
12.9M
                }
584
33.4M
            }
585
33.3M
            if (j == mlast) {
586
                /* got a match! */
587
20.4M
                if (mode != FAST_COUNT) {
588
10.3M
                    return i;
589
10.3M
                }
590
10.1M
                count++;
591
10.1M
                if (count == maxcount) {
592
0
                    return maxcount;
593
0
                }
594
10.1M
                i = i + mlast;
595
10.1M
                continue;
596
10.1M
            }
597
            /* miss: check if next character is part of pattern */
598
12.9M
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
599
4.64M
                i = i + m;
600
4.64M
            }
601
8.30M
            else {
602
8.30M
                i = i + gap;
603
8.30M
            }
604
12.9M
        }
605
3.09G
        else {
606
            /* skip: check if next character is part of pattern */
607
3.09G
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
608
3.05G
                i = i + m;
609
3.05G
            }
610
3.09G
        }
611
3.13G
    }
612
5.16M
    return mode == FAST_COUNT ? count : -1;
613
15.4M
}
Unexecuted instantiation: bytesobject.c:stringlib_default_find
unicodeobject.c:asciilib_default_find
Line
Count
Source
560
1.40M
{
561
1.40M
    const Py_ssize_t w = n - m;
562
1.40M
    Py_ssize_t mlast = m - 1, count = 0;
563
1.40M
    Py_ssize_t gap = mlast;
564
1.40M
    const STRINGLIB_CHAR last = p[mlast];
565
1.40M
    const STRINGLIB_CHAR *const ss = &s[mlast];
566
567
1.40M
    unsigned long mask = 0;
568
2.82M
    for (Py_ssize_t i = 0; i < mlast; i++) {
569
1.41M
        STRINGLIB_BLOOM_ADD(mask, p[i]);
570
1.41M
        if (p[i] == last) {
571
17.8k
            gap = mlast - i - 1;
572
17.8k
        }
573
1.41M
    }
574
1.40M
    STRINGLIB_BLOOM_ADD(mask, last);
575
576
153M
    for (Py_ssize_t i = 0; i <= w; i++) {
577
153M
        if (ss[i] == last) {
578
            /* candidate match */
579
3.89M
            Py_ssize_t j;
580
5.28M
            for (j = 0; j < mlast; j++) {
581
3.90M
                if (s[i+j] != p[j]) {
582
2.52M
                    break;
583
2.52M
                }
584
3.90M
            }
585
3.89M
            if (j == mlast) {
586
                /* got a match! */
587
1.37M
                if (mode != FAST_COUNT) {
588
1.37M
                    return i;
589
1.37M
                }
590
0
                count++;
591
0
                if (count == maxcount) {
592
0
                    return maxcount;
593
0
                }
594
0
                i = i + mlast;
595
0
                continue;
596
0
            }
597
            /* miss: check if next character is part of pattern */
598
2.52M
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
599
40.6k
                i = i + m;
600
40.6k
            }
601
2.48M
            else {
602
2.48M
                i = i + gap;
603
2.48M
            }
604
2.52M
        }
605
149M
        else {
606
            /* skip: check if next character is part of pattern */
607
149M
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
608
143M
                i = i + m;
609
143M
            }
610
149M
        }
611
153M
    }
612
32.7k
    return mode == FAST_COUNT ? count : -1;
613
1.40M
}
unicodeobject.c:ucs1lib_default_find
Line
Count
Source
560
6.96M
{
561
6.96M
    const Py_ssize_t w = n - m;
562
6.96M
    Py_ssize_t mlast = m - 1, count = 0;
563
6.96M
    Py_ssize_t gap = mlast;
564
6.96M
    const STRINGLIB_CHAR last = p[mlast];
565
6.96M
    const STRINGLIB_CHAR *const ss = &s[mlast];
566
567
6.96M
    unsigned long mask = 0;
568
14.0M
    for (Py_ssize_t i = 0; i < mlast; i++) {
569
7.11M
        STRINGLIB_BLOOM_ADD(mask, p[i]);
570
7.11M
        if (p[i] == last) {
571
523k
            gap = mlast - i - 1;
572
523k
        }
573
7.11M
    }
574
6.96M
    STRINGLIB_BLOOM_ADD(mask, last);
575
576
457M
    for (Py_ssize_t i = 0; i <= w; i++) {
577
452M
        if (ss[i] == last) {
578
            /* candidate match */
579
10.4M
            Py_ssize_t j;
580
15.6M
            for (j = 0; j < mlast; j++) {
581
10.4M
                if (s[i+j] != p[j]) {
582
5.26M
                    break;
583
5.26M
                }
584
10.4M
            }
585
10.4M
            if (j == mlast) {
586
                /* got a match! */
587
5.19M
                if (mode != FAST_COUNT) {
588
1.95M
                    return i;
589
1.95M
                }
590
3.24M
                count++;
591
3.24M
                if (count == maxcount) {
592
0
                    return maxcount;
593
0
                }
594
3.24M
                i = i + mlast;
595
3.24M
                continue;
596
3.24M
            }
597
            /* miss: check if next character is part of pattern */
598
5.26M
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
599
1.92M
                i = i + m;
600
1.92M
            }
601
3.34M
            else {
602
3.34M
                i = i + gap;
603
3.34M
            }
604
5.26M
        }
605
442M
        else {
606
            /* skip: check if next character is part of pattern */
607
442M
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
608
431M
                i = i + m;
609
431M
            }
610
442M
        }
611
452M
    }
612
5.00M
    return mode == FAST_COUNT ? count : -1;
613
6.96M
}
unicodeobject.c:ucs2lib_default_find
Line
Count
Source
560
2.80M
{
561
2.80M
    const Py_ssize_t w = n - m;
562
2.80M
    Py_ssize_t mlast = m - 1, count = 0;
563
2.80M
    Py_ssize_t gap = mlast;
564
2.80M
    const STRINGLIB_CHAR last = p[mlast];
565
2.80M
    const STRINGLIB_CHAR *const ss = &s[mlast];
566
567
2.80M
    unsigned long mask = 0;
568
5.64M
    for (Py_ssize_t i = 0; i < mlast; i++) {
569
2.84M
        STRINGLIB_BLOOM_ADD(mask, p[i]);
570
2.84M
        if (p[i] == last) {
571
33.7k
            gap = mlast - i - 1;
572
33.7k
        }
573
2.84M
    }
574
2.80M
    STRINGLIB_BLOOM_ADD(mask, last);
575
576
1.03G
    for (Py_ssize_t i = 0; i <= w; i++) {
577
1.03G
        if (ss[i] == last) {
578
            /* candidate match */
579
7.83M
            Py_ssize_t j;
580
13.1M
            for (j = 0; j < mlast; j++) {
581
7.86M
                if (s[i+j] != p[j]) {
582
2.51M
                    break;
583
2.51M
                }
584
7.86M
            }
585
7.83M
            if (j == mlast) {
586
                /* got a match! */
587
5.32M
                if (mode != FAST_COUNT) {
588
2.71M
                    return i;
589
2.71M
                }
590
2.61M
                count++;
591
2.61M
                if (count == maxcount) {
592
0
                    return maxcount;
593
0
                }
594
2.61M
                i = i + mlast;
595
2.61M
                continue;
596
2.61M
            }
597
            /* miss: check if next character is part of pattern */
598
2.51M
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
599
1.01M
                i = i + m;
600
1.01M
            }
601
1.49M
            else {
602
1.49M
                i = i + gap;
603
1.49M
            }
604
2.51M
        }
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.01G
                i = i + m;
609
1.01G
            }
610
1.03G
        }
611
1.03G
    }
612
96.8k
    return mode == FAST_COUNT ? count : -1;
613
2.80M
}
unicodeobject.c:ucs4lib_default_find
Line
Count
Source
560
4.31M
{
561
4.31M
    const Py_ssize_t w = n - m;
562
4.31M
    Py_ssize_t mlast = m - 1, count = 0;
563
4.31M
    Py_ssize_t gap = mlast;
564
4.31M
    const STRINGLIB_CHAR last = p[mlast];
565
4.31M
    const STRINGLIB_CHAR *const ss = &s[mlast];
566
567
4.31M
    unsigned long mask = 0;
568
8.66M
    for (Py_ssize_t i = 0; i < mlast; i++) {
569
4.34M
        STRINGLIB_BLOOM_ADD(mask, p[i]);
570
4.34M
        if (p[i] == last) {
571
25.8k
            gap = mlast - i - 1;
572
25.8k
        }
573
4.34M
    }
574
4.31M
    STRINGLIB_BLOOM_ADD(mask, last);
575
576
1.48G
    for (Py_ssize_t i = 0; i <= w; i++) {
577
1.48G
        if (ss[i] == last) {
578
            /* candidate match */
579
11.1M
            Py_ssize_t j;
580
19.7M
            for (j = 0; j < mlast; j++) {
581
11.2M
                if (s[i+j] != p[j]) {
582
2.64M
                    break;
583
2.64M
                }
584
11.2M
            }
585
11.1M
            if (j == mlast) {
586
                /* got a match! */
587
8.53M
                if (mode != FAST_COUNT) {
588
4.29M
                    return i;
589
4.29M
                }
590
4.24M
                count++;
591
4.24M
                if (count == maxcount) {
592
0
                    return maxcount;
593
0
                }
594
4.24M
                i = i + mlast;
595
4.24M
                continue;
596
4.24M
            }
597
            /* miss: check if next character is part of pattern */
598
2.64M
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
599
1.66M
                i = i + m;
600
1.66M
            }
601
980k
            else {
602
980k
                i = i + gap;
603
980k
            }
604
2.64M
        }
605
1.47G
        else {
606
            /* skip: check if next character is part of pattern */
607
1.47G
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
608
1.46G
                i = i + m;
609
1.46G
            }
610
1.47G
        }
611
1.48G
    }
612
26.5k
    return mode == FAST_COUNT ? count : -1;
613
4.31M
}
bytes_methods.c:stringlib_default_find
Line
Count
Source
560
2.96k
{
561
2.96k
    const Py_ssize_t w = n - m;
562
2.96k
    Py_ssize_t mlast = m - 1, count = 0;
563
2.96k
    Py_ssize_t gap = mlast;
564
2.96k
    const STRINGLIB_CHAR last = p[mlast];
565
2.96k
    const STRINGLIB_CHAR *const ss = &s[mlast];
566
567
2.96k
    unsigned long mask = 0;
568
11.8k
    for (Py_ssize_t i = 0; i < mlast; i++) {
569
8.89k
        STRINGLIB_BLOOM_ADD(mask, p[i]);
570
8.89k
        if (p[i] == last) {
571
2.96k
            gap = mlast - i - 1;
572
2.96k
        }
573
8.89k
    }
574
2.96k
    STRINGLIB_BLOOM_ADD(mask, last);
575
576
246k
    for (Py_ssize_t i = 0; i <= w; i++) {
577
246k
        if (ss[i] == last) {
578
            /* candidate match */
579
6.77k
            Py_ssize_t j;
580
15.2k
            for (j = 0; j < mlast; j++) {
581
12.5k
                if (s[i+j] != p[j]) {
582
4.03k
                    break;
583
4.03k
                }
584
12.5k
            }
585
6.77k
            if (j == mlast) {
586
                /* got a match! */
587
2.73k
                if (mode != FAST_COUNT) {
588
2.73k
                    return i;
589
2.73k
                }
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
4.03k
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
599
550
                i = i + m;
600
550
            }
601
3.48k
            else {
602
3.48k
                i = i + gap;
603
3.48k
            }
604
4.03k
        }
605
239k
        else {
606
            /* skip: check if next character is part of pattern */
607
239k
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
608
24.0k
                i = i + m;
609
24.0k
            }
610
239k
        }
611
246k
    }
612
230
    return mode == FAST_COUNT ? count : -1;
613
2.96k
}
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
8
{
694
    /* create compressed boyer-moore delta 1 table */
695
8
    unsigned long mask = 0;
696
8
    Py_ssize_t i, j, mlast = m - 1, skip = m - 1, w = n - m;
697
698
    /* process pattern[0] outside the loop */
699
8
    STRINGLIB_BLOOM_ADD(mask, p[0]);
700
    /* process pattern[:0:-1] */
701
32
    for (i = mlast; i > 0; i--) {
702
24
        STRINGLIB_BLOOM_ADD(mask, p[i]);
703
24
        if (p[i] == p[0]) {
704
0
            skip = i - 1;
705
0
        }
706
24
    }
707
708
1.00k
    for (i = w; i >= 0; i--) {
709
999
        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
991
        else {
729
            /* skip: check if previous character is part of pattern */
730
991
            if (i > 0 && !STRINGLIB_BLOOM(mask, s[i-1])) {
731
978
                i = i - m;
732
978
            }
733
991
        }
734
999
    }
735
8
    return -1;
736
8
}
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
8
{
694
    /* create compressed boyer-moore delta 1 table */
695
8
    unsigned long mask = 0;
696
8
    Py_ssize_t i, j, mlast = m - 1, skip = m - 1, w = n - m;
697
698
    /* process pattern[0] outside the loop */
699
8
    STRINGLIB_BLOOM_ADD(mask, p[0]);
700
    /* process pattern[:0:-1] */
701
32
    for (i = mlast; i > 0; i--) {
702
24
        STRINGLIB_BLOOM_ADD(mask, p[i]);
703
24
        if (p[i] == p[0]) {
704
0
            skip = i - 1;
705
0
        }
706
24
    }
707
708
1.00k
    for (i = w; i >= 0; i--) {
709
999
        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
991
        else {
729
            /* skip: check if previous character is part of pattern */
730
991
            if (i > 0 && !STRINGLIB_BLOOM(mask, s[i-1])) {
731
978
                i = i - m;
732
978
            }
733
991
        }
734
999
    }
735
8
    return -1;
736
8
}
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
60.8M
{
762
60.8M
    Py_ssize_t count = 0;
763
5.98G
    for (Py_ssize_t i = 0; i < n; i++) {
764
5.92G
        if (s[i] == p0) {
765
231M
            count++;
766
231M
        }
767
5.92G
    }
768
60.8M
    return count;
769
60.8M
}
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
47.9M
{
762
47.9M
    Py_ssize_t count = 0;
763
1.57G
    for (Py_ssize_t i = 0; i < n; i++) {
764
1.53G
        if (s[i] == p0) {
765
45.3M
            count++;
766
45.3M
        }
767
1.53G
    }
768
47.9M
    return count;
769
47.9M
}
unicodeobject.c:ucs2lib_count_char_no_maxcount
Line
Count
Source
761
11.7M
{
762
11.7M
    Py_ssize_t count = 0;
763
2.19G
    for (Py_ssize_t i = 0; i < n; i++) {
764
2.18G
        if (s[i] == p0) {
765
85.7M
            count++;
766
85.7M
        }
767
2.18G
    }
768
11.7M
    return count;
769
11.7M
}
unicodeobject.c:ucs4lib_count_char_no_maxcount
Line
Count
Source
761
1.24M
{
762
1.24M
    Py_ssize_t count = 0;
763
2.21G
    for (Py_ssize_t i = 0; i < n; i++) {
764
2.21G
        if (s[i] == p0) {
765
100M
            count++;
766
100M
        }
767
2.21G
    }
768
1.24M
    return count;
769
1.24M
}
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
265M
{
777
265M
    if (n < m || (mode == FAST_COUNT && maxcount == 0)) {
778
15.0k
        return -1;
779
15.0k
    }
780
781
    /* look for special cases */
782
265M
    if (m <= 1) {
783
250M
        if (m <= 0) {
784
0
            return -1;
785
0
        }
786
        /* use special case for 1-character strings */
787
250M
        if (mode == FAST_SEARCH)
788
189M
            return STRINGLIB(find_char)(s, n, p[0]);
789
60.9M
        else if (mode == FAST_RSEARCH)
790
12.4k
            return STRINGLIB(rfind_char)(s, n, p[0]);
791
60.8M
        else {
792
60.8M
            if (maxcount == PY_SSIZE_T_MAX) {
793
60.8M
                return STRINGLIB(count_char_no_maxcount)(s, n, p[0]);
794
60.8M
            }
795
0
            return STRINGLIB(count_char)(s, n, p[0], maxcount);
796
60.8M
        }
797
250M
    }
798
799
15.4M
    if (mode != FAST_RSEARCH) {
800
15.4M
        if (n < 2500 || (m < 100 && n < 30000) || m < 6) {
801
15.4M
            return STRINGLIB(default_find)(s, n, p, m, maxcount, mode);
802
15.4M
        }
803
23
        else if ((m >> 2) * 3 < (n >> 2)) {
804
            /* 33% threshold, but don't overflow. */
805
            /* For larger problems where the needle isn't a huge
806
               percentage of the size of the haystack, the relatively
807
               expensive O(m) startup cost of the two-way algorithm
808
               will surely pay off. */
809
23
            if (mode == FAST_SEARCH) {
810
23
                return STRINGLIB(_two_way_find)(s, n, p, m);
811
23
            }
812
0
            else {
813
0
                return STRINGLIB(_two_way_count)(s, n, p, m, maxcount);
814
0
            }
815
23
        }
816
0
        else {
817
            /* To ensure that we have good worst-case behavior,
818
               here's an adaptive version of the algorithm, where if
819
               we match O(m) characters without any matches of the
820
               entire needle, then we predict that the startup cost of
821
               the two-way algorithm will probably be worth it. */
822
0
            return STRINGLIB(adaptive_find)(s, n, p, m, maxcount, mode);
823
0
        }
824
15.4M
    }
825
8
    else {
826
        /* FAST_RSEARCH */
827
8
        return STRINGLIB(default_rfind)(s, n, p, m, maxcount, mode);
828
8
    }
829
15.4M
}
Unexecuted instantiation: bytesobject.c:fastsearch
unicodeobject.c:asciilib_fastsearch
Line
Count
Source
776
23.2M
{
777
23.2M
    if (n < m || (mode == FAST_COUNT && maxcount == 0)) {
778
0
        return -1;
779
0
    }
780
781
    /* look for special cases */
782
23.2M
    if (m <= 1) {
783
21.8M
        if (m <= 0) {
784
0
            return -1;
785
0
        }
786
        /* use special case for 1-character strings */
787
21.8M
        if (mode == FAST_SEARCH)
788
21.8M
            return STRINGLIB(find_char)(s, n, p[0]);
789
12.4k
        else if (mode == FAST_RSEARCH)
790
12.4k
            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
21.8M
    }
798
799
1.40M
    if (mode != FAST_RSEARCH) {
800
1.40M
        if (n < 2500 || (m < 100 && n < 30000) || m < 6) {
801
1.40M
            return STRINGLIB(default_find)(s, n, p, m, maxcount, mode);
802
1.40M
        }
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.40M
    }
825
0
    else {
826
        /* FAST_RSEARCH */
827
0
        return STRINGLIB(default_rfind)(s, n, p, m, maxcount, mode);
828
0
    }
829
1.40M
}
unicodeobject.c:ucs1lib_fastsearch
Line
Count
Source
776
63.1M
{
777
63.1M
    if (n < m || (mode == FAST_COUNT && maxcount == 0)) {
778
0
        return -1;
779
0
    }
780
781
    /* look for special cases */
782
63.1M
    if (m <= 1) {
783
56.2M
        if (m <= 0) {
784
0
            return -1;
785
0
        }
786
        /* use special case for 1-character strings */
787
56.2M
        if (mode == FAST_SEARCH)
788
8.28M
            return STRINGLIB(find_char)(s, n, p[0]);
789
47.9M
        else if (mode == FAST_RSEARCH)
790
0
            return STRINGLIB(rfind_char)(s, n, p[0]);
791
47.9M
        else {
792
47.9M
            if (maxcount == PY_SSIZE_T_MAX) {
793
47.9M
                return STRINGLIB(count_char_no_maxcount)(s, n, p[0]);
794
47.9M
            }
795
0
            return STRINGLIB(count_char)(s, n, p[0], maxcount);
796
47.9M
        }
797
56.2M
    }
798
799
6.96M
    if (mode != FAST_RSEARCH) {
800
6.96M
        if (n < 2500 || (m < 100 && n < 30000) || m < 6) {
801
6.96M
            return STRINGLIB(default_find)(s, n, p, m, maxcount, mode);
802
6.96M
        }
803
23
        else if ((m >> 2) * 3 < (n >> 2)) {
804
            /* 33% threshold, but don't overflow. */
805
            /* For larger problems where the needle isn't a huge
806
               percentage of the size of the haystack, the relatively
807
               expensive O(m) startup cost of the two-way algorithm
808
               will surely pay off. */
809
23
            if (mode == FAST_SEARCH) {
810
23
                return STRINGLIB(_two_way_find)(s, n, p, m);
811
23
            }
812
0
            else {
813
0
                return STRINGLIB(_two_way_count)(s, n, p, m, maxcount);
814
0
            }
815
23
        }
816
0
        else {
817
            /* To ensure that we have good worst-case behavior,
818
               here's an adaptive version of the algorithm, where if
819
               we match O(m) characters without any matches of the
820
               entire needle, then we predict that the startup cost of
821
               the two-way algorithm will probably be worth it. */
822
0
            return STRINGLIB(adaptive_find)(s, n, p, m, maxcount, mode);
823
0
        }
824
6.96M
    }
825
0
    else {
826
        /* FAST_RSEARCH */
827
0
        return STRINGLIB(default_rfind)(s, n, p, m, maxcount, mode);
828
0
    }
829
6.96M
}
unicodeobject.c:ucs2lib_fastsearch
Line
Count
Source
776
86.3M
{
777
86.3M
    if (n < m || (mode == FAST_COUNT && maxcount == 0)) {
778
14.9k
        return -1;
779
14.9k
    }
780
781
    /* look for special cases */
782
86.3M
    if (m <= 1) {
783
83.5M
        if (m <= 0) {
784
0
            return -1;
785
0
        }
786
        /* use special case for 1-character strings */
787
83.5M
        if (mode == FAST_SEARCH)
788
71.8M
            return STRINGLIB(find_char)(s, n, p[0]);
789
11.7M
        else if (mode == FAST_RSEARCH)
790
0
            return STRINGLIB(rfind_char)(s, n, p[0]);
791
11.7M
        else {
792
11.7M
            if (maxcount == PY_SSIZE_T_MAX) {
793
11.7M
                return STRINGLIB(count_char_no_maxcount)(s, n, p[0]);
794
11.7M
            }
795
0
            return STRINGLIB(count_char)(s, n, p[0], maxcount);
796
11.7M
        }
797
83.5M
    }
798
799
2.80M
    if (mode != FAST_RSEARCH) {
800
2.80M
        if (n < 2500 || (m < 100 && n < 30000) || m < 6) {
801
2.80M
            return STRINGLIB(default_find)(s, n, p, m, maxcount, mode);
802
2.80M
        }
803
0
        else if ((m >> 2) * 3 < (n >> 2)) {
804
            /* 33% threshold, but don't overflow. */
805
            /* For larger problems where the needle isn't a huge
806
               percentage of the size of the haystack, the relatively
807
               expensive O(m) startup cost of the two-way algorithm
808
               will surely pay off. */
809
0
            if (mode == FAST_SEARCH) {
810
0
                return STRINGLIB(_two_way_find)(s, n, p, m);
811
0
            }
812
0
            else {
813
0
                return STRINGLIB(_two_way_count)(s, n, p, m, maxcount);
814
0
            }
815
0
        }
816
0
        else {
817
            /* To ensure that we have good worst-case behavior,
818
               here's an adaptive version of the algorithm, where if
819
               we match O(m) characters without any matches of the
820
               entire needle, then we predict that the startup cost of
821
               the two-way algorithm will probably be worth it. */
822
0
            return STRINGLIB(adaptive_find)(s, n, p, m, maxcount, mode);
823
0
        }
824
2.80M
    }
825
0
    else {
826
        /* FAST_RSEARCH */
827
0
        return STRINGLIB(default_rfind)(s, n, p, m, maxcount, mode);
828
0
    }
829
2.80M
}
unicodeobject.c:ucs4lib_fastsearch
Line
Count
Source
776
92.8M
{
777
92.8M
    if (n < m || (mode == FAST_COUNT && maxcount == 0)) {
778
0
        return -1;
779
0
    }
780
781
    /* look for special cases */
782
92.8M
    if (m <= 1) {
783
88.5M
        if (m <= 0) {
784
0
            return -1;
785
0
        }
786
        /* use special case for 1-character strings */
787
88.5M
        if (mode == FAST_SEARCH)
788
87.3M
            return STRINGLIB(find_char)(s, n, p[0]);
789
1.24M
        else if (mode == FAST_RSEARCH)
790
0
            return STRINGLIB(rfind_char)(s, n, p[0]);
791
1.24M
        else {
792
1.24M
            if (maxcount == PY_SSIZE_T_MAX) {
793
1.24M
                return STRINGLIB(count_char_no_maxcount)(s, n, p[0]);
794
1.24M
            }
795
0
            return STRINGLIB(count_char)(s, n, p[0], maxcount);
796
1.24M
        }
797
88.5M
    }
798
799
4.31M
    if (mode != FAST_RSEARCH) {
800
4.31M
        if (n < 2500 || (m < 100 && n < 30000) || m < 6) {
801
4.31M
            return STRINGLIB(default_find)(s, n, p, m, maxcount, mode);
802
4.31M
        }
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.31M
    }
825
0
    else {
826
        /* FAST_RSEARCH */
827
0
        return STRINGLIB(default_rfind)(s, n, p, m, maxcount, mode);
828
0
    }
829
4.31M
}
bytes_methods.c:fastsearch
Line
Count
Source
776
2.98k
{
777
2.98k
    if (n < m || (mode == FAST_COUNT && maxcount == 0)) {
778
12
        return -1;
779
12
    }
780
781
    /* look for special cases */
782
2.97k
    if (m <= 1) {
783
0
        if (m <= 0) {
784
0
            return -1;
785
0
        }
786
        /* use special case for 1-character strings */
787
0
        if (mode == FAST_SEARCH)
788
0
            return STRINGLIB(find_char)(s, n, p[0]);
789
0
        else if (mode == FAST_RSEARCH)
790
0
            return STRINGLIB(rfind_char)(s, n, p[0]);
791
0
        else {
792
0
            if (maxcount == PY_SSIZE_T_MAX) {
793
0
                return STRINGLIB(count_char_no_maxcount)(s, n, p[0]);
794
0
            }
795
0
            return STRINGLIB(count_char)(s, n, p[0], maxcount);
796
0
        }
797
0
    }
798
799
2.97k
    if (mode != FAST_RSEARCH) {
800
2.96k
        if (n < 2500 || (m < 100 && n < 30000) || m < 6) {
801
2.96k
            return STRINGLIB(default_find)(s, n, p, m, maxcount, mode);
802
2.96k
        }
803
0
        else if ((m >> 2) * 3 < (n >> 2)) {
804
            /* 33% threshold, but don't overflow. */
805
            /* For larger problems where the needle isn't a huge
806
               percentage of the size of the haystack, the relatively
807
               expensive O(m) startup cost of the two-way algorithm
808
               will surely pay off. */
809
0
            if (mode == FAST_SEARCH) {
810
0
                return STRINGLIB(_two_way_find)(s, n, p, m);
811
0
            }
812
0
            else {
813
0
                return STRINGLIB(_two_way_count)(s, n, p, m, maxcount);
814
0
            }
815
0
        }
816
0
        else {
817
            /* To ensure that we have good worst-case behavior,
818
               here's an adaptive version of the algorithm, where if
819
               we match O(m) characters without any matches of the
820
               entire needle, then we predict that the startup cost of
821
               the two-way algorithm will probably be worth it. */
822
0
            return STRINGLIB(adaptive_find)(s, n, p, m, maxcount, mode);
823
0
        }
824
2.96k
    }
825
8
    else {
826
        /* FAST_RSEARCH */
827
8
        return STRINGLIB(default_rfind)(s, n, p, m, maxcount, mode);
828
8
    }
829
2.97k
}
Unexecuted instantiation: bytearrayobject.c:fastsearch
830