Coverage Report

Created: 2025-10-12 06:48

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
551M
#define FAST_COUNT 0
25
387M
#define FAST_SEARCH 1
26
71.9M
#define FAST_RSEARCH 2
27
28
#if LONG_BIT >= 128
29
#define STRINGLIB_BLOOM_WIDTH 128
30
#elif LONG_BIT >= 64
31
6.06G
#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.1M
    ((mask |= (1UL << ((ch) & (STRINGLIB_BLOOM_WIDTH -1)))))
40
#define STRINGLIB_BLOOM(mask, ch)     \
41
6.02G
    ((mask &  (1UL << ((ch) & (STRINGLIB_BLOOM_WIDTH -1)))))
42
43
#ifdef STRINGLIB_FAST_MEMCHR
44
199M
#  define MEMCHR_CUT_OFF 15
45
#else
46
67.5M
#  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
266M
{
52
266M
    const STRINGLIB_CHAR *p, *e;
53
54
266M
    p = s;
55
266M
    e = s + n;
56
266M
    if (n > MEMCHR_CUT_OFF) {
57
#ifdef STRINGLIB_FAST_MEMCHR
58
108M
        p = STRINGLIB_FAST_MEMCHR(s, ch, n);
59
108M
        if (p != NULL)
60
107M
            return (p - s);
61
1.63M
        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
58.9M
        if (needle != 0) {
71
58.6M
            do {
72
58.6M
                void *candidate = memchr(p, needle,
73
58.6M
                                         (e - p) * sizeof(STRINGLIB_CHAR));
74
58.6M
                if (candidate == NULL)
75
390k
                    return -1;
76
58.2M
                s1 = p;
77
58.2M
                p = (const STRINGLIB_CHAR *)
78
58.2M
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
79
58.2M
                if (*p == ch)
80
58.1M
                    return (p - s);
81
                /* False positive */
82
113k
                p++;
83
113k
                if (p - s1 > MEMCHR_CUT_OFF)
84
52.8k
                    continue;
85
60.6k
                if (e - p <= MEMCHR_CUT_OFF)
86
3.50k
                    break;
87
57.1k
                e1 = p + MEMCHR_CUT_OFF;
88
1.72M
                while (p != e1) {
89
1.69M
                    if (*p == ch)
90
24.1k
                        return (p - s);
91
1.66M
                    p++;
92
1.66M
                }
93
57.1k
            }
94
58.5M
            while (e - p > MEMCHR_CUT_OFF);
95
58.5M
        }
96
#endif
97
167M
    }
98
393M
    while (p < e) {
99
309M
        if (*p == ch)
100
15.2M
            return (p - s);
101
294M
        p++;
102
294M
    }
103
83.7M
    return -1;
104
98.9M
}
Unexecuted instantiation: bytesobject.c:stringlib_find_char
unicodeobject.c:ucs1lib_find_char
Line
Count
Source
51
104M
{
52
104M
    const STRINGLIB_CHAR *p, *e;
53
54
104M
    p = s;
55
104M
    e = s + n;
56
104M
    if (n > MEMCHR_CUT_OFF) {
57
18.4M
#ifdef STRINGLIB_FAST_MEMCHR
58
18.4M
        p = STRINGLIB_FAST_MEMCHR(s, ch, n);
59
18.4M
        if (p != NULL)
60
17.4M
            return (p - s);
61
1.05M
        return -1;
62
#else
63
        /* use memchr if we can choose a needle without too many likely
64
           false positives */
65
        const STRINGLIB_CHAR *s1, *e1;
66
        unsigned char needle = ch & 0xff;
67
        /* If looking for a multiple of 256, we'd have too
68
           many false positives looking for the '\0' byte in UCS2
69
           and UCS4 representations. */
70
        if (needle != 0) {
71
            do {
72
                void *candidate = memchr(p, needle,
73
                                         (e - p) * sizeof(STRINGLIB_CHAR));
74
                if (candidate == NULL)
75
                    return -1;
76
                s1 = p;
77
                p = (const STRINGLIB_CHAR *)
78
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
79
                if (*p == ch)
80
                    return (p - s);
81
                /* False positive */
82
                p++;
83
                if (p - s1 > MEMCHR_CUT_OFF)
84
                    continue;
85
                if (e - p <= MEMCHR_CUT_OFF)
86
                    break;
87
                e1 = p + MEMCHR_CUT_OFF;
88
                while (p != e1) {
89
                    if (*p == ch)
90
                        return (p - s);
91
                    p++;
92
                }
93
            }
94
            while (e - p > MEMCHR_CUT_OFF);
95
        }
96
#endif
97
18.4M
    }
98
243M
    while (p < e) {
99
161M
        if (*p == ch)
100
4.54M
            return (p - s);
101
157M
        p++;
102
157M
    }
103
81.2M
    return -1;
104
85.7M
}
unicodeobject.c:ucs2lib_find_char
Line
Count
Source
51
67.2M
{
52
67.2M
    const STRINGLIB_CHAR *p, *e;
53
54
67.2M
    p = s;
55
67.2M
    e = s + n;
56
67.2M
    if (n > MEMCHR_CUT_OFF) {
57
#ifdef STRINGLIB_FAST_MEMCHR
58
        p = STRINGLIB_FAST_MEMCHR(s, ch, n);
59
        if (p != NULL)
60
            return (p - s);
61
        return -1;
62
#else
63
        /* use memchr if we can choose a needle without too many likely
64
           false positives */
65
58.9M
        const STRINGLIB_CHAR *s1, *e1;
66
58.9M
        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
58.9M
        if (needle != 0) {
71
58.6M
            do {
72
58.6M
                void *candidate = memchr(p, needle,
73
58.6M
                                         (e - p) * sizeof(STRINGLIB_CHAR));
74
58.6M
                if (candidate == NULL)
75
390k
                    return -1;
76
58.2M
                s1 = p;
77
58.2M
                p = (const STRINGLIB_CHAR *)
78
58.2M
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
79
58.2M
                if (*p == ch)
80
58.1M
                    return (p - s);
81
                /* False positive */
82
113k
                p++;
83
113k
                if (p - s1 > MEMCHR_CUT_OFF)
84
52.8k
                    continue;
85
60.6k
                if (e - p <= MEMCHR_CUT_OFF)
86
3.50k
                    break;
87
57.1k
                e1 = p + MEMCHR_CUT_OFF;
88
1.72M
                while (p != e1) {
89
1.69M
                    if (*p == ch)
90
24.1k
                        return (p - s);
91
1.66M
                    p++;
92
1.66M
                }
93
57.1k
            }
94
58.5M
            while (e - p > MEMCHR_CUT_OFF);
95
58.5M
        }
96
58.9M
#endif
97
58.9M
    }
98
137M
    while (p < e) {
99
134M
        if (*p == ch)
100
6.23M
            return (p - s);
101
128M
        p++;
102
128M
    }
103
2.44M
    return -1;
104
8.67M
}
unicodeobject.c:ucs4lib_find_char
Line
Count
Source
51
70.8M
{
52
70.8M
    const STRINGLIB_CHAR *p, *e;
53
54
70.8M
    p = s;
55
70.8M
    e = s + n;
56
70.8M
    if (n > MEMCHR_CUT_OFF) {
57
70.7M
#ifdef STRINGLIB_FAST_MEMCHR
58
70.7M
        p = STRINGLIB_FAST_MEMCHR(s, ch, n);
59
70.7M
        if (p != NULL)
60
70.7M
            return (p - s);
61
27.7k
        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
70.7M
    }
98
258k
    while (p < e) {
99
227k
        if (*p == ch)
100
34.2k
            return (p - s);
101
193k
        p++;
102
193k
    }
103
30.8k
    return -1;
104
65.1k
}
unicodeobject.c:asciilib_find_char
Line
Count
Source
51
24.0M
{
52
24.0M
    const STRINGLIB_CHAR *p, *e;
53
54
24.0M
    p = s;
55
24.0M
    e = s + n;
56
24.0M
    if (n > MEMCHR_CUT_OFF) {
57
19.5M
#ifdef STRINGLIB_FAST_MEMCHR
58
19.5M
        p = STRINGLIB_FAST_MEMCHR(s, ch, n);
59
19.5M
        if (p != NULL)
60
19.0M
            return (p - s);
61
552k
        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
19.5M
    }
98
12.6M
    while (p < e) {
99
12.6M
        if (*p == ch)
100
4.40M
            return (p - s);
101
8.22M
        p++;
102
8.22M
    }
103
51.0k
    return -1;
104
4.45M
}
bytes_methods.c:stringlib_find_char
Line
Count
Source
51
1.71k
{
52
1.71k
    const STRINGLIB_CHAR *p, *e;
53
54
1.71k
    p = s;
55
1.71k
    e = s + n;
56
1.71k
    if (n > MEMCHR_CUT_OFF) {
57
1.71k
#ifdef STRINGLIB_FAST_MEMCHR
58
1.71k
        p = STRINGLIB_FAST_MEMCHR(s, ch, n);
59
1.71k
        if (p != NULL)
60
1.45k
            return (p - s);
61
258
        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.71k
    }
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
39.5k
#  define MEMRCHR_CUT_OFF 15
110
#else
111
125k
#  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
147k
{
118
147k
    const STRINGLIB_CHAR *p;
119
147k
#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
147k
    if (n > MEMRCHR_CUT_OFF) {
126
#if STRINGLIB_SIZEOF_CHAR == 1
127
        p = memrchr(s, ch, n);
128
11.3k
        if (p != NULL)
129
7.97k
            return (p - s);
130
3.42k
        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
70.1k
        if (needle != 0) {
141
73.9k
            do {
142
73.9k
                void *candidate = memrchr(s, needle,
143
73.9k
                                          n * sizeof(STRINGLIB_CHAR));
144
73.9k
                if (candidate == NULL)
145
606
                    return -1;
146
73.3k
                n1 = n;
147
73.3k
                p = (const STRINGLIB_CHAR *)
148
73.3k
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
149
73.3k
                n = p - s;
150
73.3k
                if (*p == ch)
151
66.7k
                    return n;
152
                /* False positive */
153
6.62k
                if (n1 - n > MEMRCHR_CUT_OFF)
154
3.08k
                    continue;
155
3.54k
                if (n <= MEMRCHR_CUT_OFF)
156
1.31k
                    break;
157
2.22k
                s1 = p - MEMRCHR_CUT_OFF;
158
76.9k
                while (p > s1) {
159
75.2k
                    p--;
160
75.2k
                    if (*p == ch)
161
573
                        return (p - s);
162
75.2k
                }
163
1.65k
                n = p - s;
164
1.65k
            }
165
70.1k
            while (n > MEMRCHR_CUT_OFF);
166
70.1k
        }
167
#endif
168
81.5k
    }
169
68.1k
#endif  /* HAVE_MEMRCHR */
170
68.1k
    p = s + n;
171
447k
    while (p > s) {
172
429k
        p--;
173
429k
        if (*p == ch)
174
50.2k
            return (p - s);
175
429k
    }
176
17.8k
    return -1;
177
68.1k
}
Unexecuted instantiation: bytesobject.c:stringlib_rfind_char
unicodeobject.c:ucs1lib_rfind_char
Line
Count
Source
117
8.01k
{
118
8.01k
    const STRINGLIB_CHAR *p;
119
8.01k
#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.01k
    if (n > MEMRCHR_CUT_OFF) {
126
4.71k
#if STRINGLIB_SIZEOF_CHAR == 1
127
4.71k
        p = memrchr(s, ch, n);
128
4.71k
        if (p != NULL)
129
3.69k
            return (p - s);
130
1.02k
        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.71k
    }
169
3.30k
#endif  /* HAVE_MEMRCHR */
170
3.30k
    p = s + n;
171
10.0k
    while (p > s) {
172
9.30k
        p--;
173
9.30k
        if (*p == ch)
174
2.58k
            return (p - s);
175
9.30k
    }
176
718
    return -1;
177
3.30k
}
unicodeobject.c:ucs2lib_rfind_char
Line
Count
Source
117
23.7k
{
118
23.7k
    const STRINGLIB_CHAR *p;
119
23.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
23.7k
    if (n > MEMRCHR_CUT_OFF) {
126
#if STRINGLIB_SIZEOF_CHAR == 1
127
        p = memrchr(s, ch, n);
128
        if (p != NULL)
129
            return (p - s);
130
        return -1;
131
#else
132
        /* use memrchr if we can choose a needle without too many likely
133
           false positives */
134
12.7k
        const STRINGLIB_CHAR *s1;
135
12.7k
        Py_ssize_t n1;
136
12.7k
        unsigned char needle = ch & 0xff;
137
        /* If looking for a multiple of 256, we'd have too
138
           many false positives looking for the '\0' byte in UCS2
139
           and UCS4 representations. */
140
12.7k
        if (needle != 0) {
141
13.9k
            do {
142
13.9k
                void *candidate = memrchr(s, needle,
143
13.9k
                                          n * sizeof(STRINGLIB_CHAR));
144
13.9k
                if (candidate == NULL)
145
347
                    return -1;
146
13.5k
                n1 = n;
147
13.5k
                p = (const STRINGLIB_CHAR *)
148
13.5k
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
149
13.5k
                n = p - s;
150
13.5k
                if (*p == ch)
151
11.3k
                    return n;
152
                /* False positive */
153
2.24k
                if (n1 - n > MEMRCHR_CUT_OFF)
154
798
                    continue;
155
1.44k
                if (n <= MEMRCHR_CUT_OFF)
156
552
                    break;
157
890
                s1 = p - MEMRCHR_CUT_OFF;
158
31.2k
                while (p > s1) {
159
30.6k
                    p--;
160
30.6k
                    if (*p == ch)
161
220
                        return (p - s);
162
30.6k
                }
163
670
                n = p - s;
164
670
            }
165
12.7k
            while (n > MEMRCHR_CUT_OFF);
166
12.7k
        }
167
12.7k
#endif
168
12.7k
    }
169
11.8k
#endif  /* HAVE_MEMRCHR */
170
11.8k
    p = s + n;
171
95.8k
    while (p > s) {
172
94.2k
        p--;
173
94.2k
        if (*p == ch)
174
10.1k
            return (p - s);
175
94.2k
    }
176
1.60k
    return -1;
177
11.8k
}
unicodeobject.c:ucs4lib_rfind_char
Line
Count
Source
117
84.2k
{
118
84.2k
    const STRINGLIB_CHAR *p;
119
84.2k
#ifdef HAVE_MEMRCHR
120
    /* memrchr() is a GNU extension, available since glibc 2.1.91.  it
121
       doesn't seem as optimized as memchr(), but is still quite
122
       faster than our hand-written loop below. There is no wmemrchr
123
       for 4-byte chars. */
124
125
84.2k
    if (n > MEMRCHR_CUT_OFF) {
126
#if STRINGLIB_SIZEOF_CHAR == 1
127
        p = memrchr(s, ch, n);
128
        if (p != NULL)
129
            return (p - s);
130
        return -1;
131
#else
132
        /* use memrchr if we can choose a needle without too many likely
133
           false positives */
134
57.4k
        const STRINGLIB_CHAR *s1;
135
57.4k
        Py_ssize_t n1;
136
57.4k
        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
57.4k
        if (needle != 0) {
141
60.0k
            do {
142
60.0k
                void *candidate = memrchr(s, needle,
143
60.0k
                                          n * sizeof(STRINGLIB_CHAR));
144
60.0k
                if (candidate == NULL)
145
259
                    return -1;
146
59.7k
                n1 = n;
147
59.7k
                p = (const STRINGLIB_CHAR *)
148
59.7k
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
149
59.7k
                n = p - s;
150
59.7k
                if (*p == ch)
151
55.3k
                    return n;
152
                /* False positive */
153
4.38k
                if (n1 - n > MEMRCHR_CUT_OFF)
154
2.28k
                    continue;
155
2.10k
                if (n <= MEMRCHR_CUT_OFF)
156
766
                    break;
157
1.33k
                s1 = p - MEMRCHR_CUT_OFF;
158
45.6k
                while (p > s1) {
159
44.6k
                    p--;
160
44.6k
                    if (*p == ch)
161
353
                        return (p - s);
162
44.6k
                }
163
986
                n = p - s;
164
986
            }
165
57.4k
            while (n > MEMRCHR_CUT_OFF);
166
57.4k
        }
167
57.4k
#endif
168
57.4k
    }
169
28.2k
#endif  /* HAVE_MEMRCHR */
170
28.2k
    p = s + n;
171
212k
    while (p > s) {
172
210k
        p--;
173
210k
        if (*p == ch)
174
26.9k
            return (p - s);
175
210k
    }
176
1.26k
    return -1;
177
28.2k
}
unicodeobject.c:asciilib_rfind_char
Line
Count
Source
117
11.1k
{
118
11.1k
    const STRINGLIB_CHAR *p;
119
11.1k
#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
11.1k
    if (n > MEMRCHR_CUT_OFF) {
126
3.50k
#if STRINGLIB_SIZEOF_CHAR == 1
127
3.50k
        p = memrchr(s, ch, n);
128
3.50k
        if (p != NULL)
129
3.41k
            return (p - s);
130
86
        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.50k
    }
169
7.60k
#endif  /* HAVE_MEMRCHR */
170
7.60k
    p = s + n;
171
39.5k
    while (p > s) {
172
37.4k
        p--;
173
37.4k
        if (*p == ch)
174
5.49k
            return (p - s);
175
37.4k
    }
176
2.10k
    return -1;
177
7.60k
}
bytes_methods.c:stringlib_rfind_char
Line
Count
Source
117
20.4k
{
118
20.4k
    const STRINGLIB_CHAR *p;
119
20.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
20.4k
    if (n > MEMRCHR_CUT_OFF) {
126
3.17k
#if STRINGLIB_SIZEOF_CHAR == 1
127
3.17k
        p = memrchr(s, ch, n);
128
3.17k
        if (p != NULL)
129
859
            return (p - s);
130
2.31k
        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.17k
    }
169
17.2k
#endif  /* HAVE_MEMRCHR */
170
17.2k
    p = s + n;
171
89.8k
    while (p > s) {
172
77.6k
        p--;
173
77.6k
        if (*p == ch)
174
5.07k
            return (p - s);
175
77.6k
    }
176
12.1k
    return -1;
177
17.2k
}
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
42
{
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
42
    Py_ssize_t max_suffix = 0;
204
42
    Py_ssize_t candidate = 1;
205
42
    Py_ssize_t k = 0;
206
    // The period of the right half.
207
42
    Py_ssize_t period = 1;
208
209
420
    while (candidate + k < len_needle) {
210
        // each loop increases candidate + k + max_suffix
211
378
        STRINGLIB_CHAR a = needle[candidate + k];
212
378
        STRINGLIB_CHAR b = needle[max_suffix + k];
213
        // check if the suffix at candidate is better than max_suffix
214
378
        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
273
            candidate += k + 1;
219
273
            k = 0;
220
            // We've ruled out any period smaller than what's
221
            // been scanned since max_suffix.
222
273
            period = candidate - max_suffix;
223
273
        }
224
105
        else if (a == b) {
225
21
            if (k + 1 != period) {
226
                // Keep scanning the equal strings
227
0
                k++;
228
0
            }
229
21
            else {
230
                // Matched a whole period.
231
                // Start matching the next period.
232
21
                candidate += period;
233
21
                k = 0;
234
21
            }
235
21
        }
236
84
        else {
237
            // Did better than max_suffix, so replace it.
238
84
            max_suffix = candidate;
239
84
            candidate++;
240
84
            k = 0;
241
84
            period = 1;
242
84
        }
243
378
    }
244
42
    *return_period = period;
245
42
    return max_suffix;
246
42
}
Unexecuted instantiation: bytesobject.c:stringlib__lex_search
Unexecuted instantiation: unicodeobject.c:asciilib__lex_search
unicodeobject.c:ucs1lib__lex_search
Line
Count
Source
199
42
{
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
42
    Py_ssize_t max_suffix = 0;
204
42
    Py_ssize_t candidate = 1;
205
42
    Py_ssize_t k = 0;
206
    // The period of the right half.
207
42
    Py_ssize_t period = 1;
208
209
420
    while (candidate + k < len_needle) {
210
        // each loop increases candidate + k + max_suffix
211
378
        STRINGLIB_CHAR a = needle[candidate + k];
212
378
        STRINGLIB_CHAR b = needle[max_suffix + k];
213
        // check if the suffix at candidate is better than max_suffix
214
378
        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
273
            candidate += k + 1;
219
273
            k = 0;
220
            // We've ruled out any period smaller than what's
221
            // been scanned since max_suffix.
222
273
            period = candidate - max_suffix;
223
273
        }
224
105
        else if (a == b) {
225
21
            if (k + 1 != period) {
226
                // Keep scanning the equal strings
227
0
                k++;
228
0
            }
229
21
            else {
230
                // Matched a whole period.
231
                // Start matching the next period.
232
21
                candidate += period;
233
21
                k = 0;
234
21
            }
235
21
        }
236
84
        else {
237
            // Did better than max_suffix, so replace it.
238
84
            max_suffix = candidate;
239
84
            candidate++;
240
84
            k = 0;
241
84
            period = 1;
242
84
        }
243
378
    }
244
42
    *return_period = period;
245
42
    return max_suffix;
246
42
}
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
21
{
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
21
    Py_ssize_t cut1, period1, cut2, period2, cut, period;
286
21
    cut1 = STRINGLIB(_lex_search)(needle, len_needle, &period1, 0);
287
21
    cut2 = STRINGLIB(_lex_search)(needle, len_needle, &period2, 1);
288
289
    // Take the later cut.
290
21
    if (cut1 > cut2) {
291
21
        period = period1;
292
21
        cut = cut1;
293
21
    }
294
0
    else {
295
0
        period = period2;
296
0
        cut = cut2;
297
0
    }
298
299
21
    LOG("split: "); LOG_STRING(needle, cut);
300
21
    LOG(" + "); LOG_STRING(needle + cut, len_needle - cut);
301
21
    LOG("\n");
302
303
21
    *return_period = period;
304
21
    return cut;
305
21
}
Unexecuted instantiation: bytesobject.c:stringlib__factorize
Unexecuted instantiation: unicodeobject.c:asciilib__factorize
unicodeobject.c:ucs1lib__factorize
Line
Count
Source
252
21
{
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
21
    Py_ssize_t cut1, period1, cut2, period2, cut, period;
286
21
    cut1 = STRINGLIB(_lex_search)(needle, len_needle, &period1, 0);
287
21
    cut2 = STRINGLIB(_lex_search)(needle, len_needle, &period2, 1);
288
289
    // Take the later cut.
290
21
    if (cut1 > cut2) {
291
21
        period = period1;
292
21
        cut = cut1;
293
21
    }
294
0
    else {
295
0
        period = period2;
296
0
        cut = cut2;
297
0
    }
298
299
21
    LOG("split: "); LOG_STRING(needle, cut);
300
21
    LOG(" + "); LOG_STRING(needle + cut, len_needle - cut);
301
21
    LOG("\n");
302
303
21
    *return_period = period;
304
21
    return cut;
305
21
}
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
231
#define SHIFT_TYPE uint8_t
309
#define MAX_SHIFT UINT8_MAX
310
311
73.2k
#define TABLE_SIZE_BITS 6u
312
73.2k
#define TABLE_SIZE (1U << TABLE_SIZE_BITS)
313
71.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
21
{
330
21
    p->needle = needle;
331
21
    p->len_needle = len_needle;
332
21
    p->cut = STRINGLIB(_factorize)(needle, len_needle, &(p->period));
333
21
    assert(p->period + p->cut <= len_needle);
334
21
    p->is_periodic = (0 == memcmp(needle,
335
21
                                  needle + p->period,
336
21
                                  p->cut * STRINGLIB_SIZEOF_CHAR));
337
21
    if (p->is_periodic) {
338
0
        assert(p->cut <= len_needle/2);
339
0
        assert(p->cut < p->period);
340
0
    }
341
21
    else {
342
        // A lower bound on the period
343
21
        p->period = Py_MAX(p->cut, len_needle - p->cut) + 1;
344
21
    }
345
    // The gap between the last character and the previous
346
    // occurrence of an equivalent character (modulo TABLE_SIZE)
347
21
    p->gap = len_needle;
348
21
    STRINGLIB_CHAR last = needle[len_needle - 1] & TABLE_MASK;
349
147
    for (Py_ssize_t i = len_needle - 2; i >= 0; i--) {
350
147
        STRINGLIB_CHAR x = needle[i] & TABLE_MASK;
351
147
        if (x == last) {
352
21
            p->gap = len_needle - 1 - i;
353
21
            break;
354
21
        }
355
147
    }
356
    // Fill up a compressed Boyer-Moore "Bad Character" table
357
21
    Py_ssize_t not_found_shift = Py_MIN(len_needle, MAX_SHIFT);
358
1.36k
    for (Py_ssize_t i = 0; i < (Py_ssize_t)TABLE_SIZE; i++) {
359
1.34k
        p->table[i] = Py_SAFE_DOWNCAST(not_found_shift,
360
1.34k
                                       Py_ssize_t, SHIFT_TYPE);
361
1.34k
    }
362
231
    for (Py_ssize_t i = len_needle - not_found_shift; i < len_needle; i++) {
363
210
        SHIFT_TYPE shift = Py_SAFE_DOWNCAST(len_needle - 1 - i,
364
210
                                            Py_ssize_t, SHIFT_TYPE);
365
210
        p->table[needle[i] & TABLE_MASK] = shift;
366
210
    }
367
21
}
Unexecuted instantiation: bytesobject.c:stringlib__preprocess
Unexecuted instantiation: unicodeobject.c:asciilib__preprocess
unicodeobject.c:ucs1lib__preprocess
Line
Count
Source
329
21
{
330
21
    p->needle = needle;
331
21
    p->len_needle = len_needle;
332
21
    p->cut = STRINGLIB(_factorize)(needle, len_needle, &(p->period));
333
21
    assert(p->period + p->cut <= len_needle);
334
21
    p->is_periodic = (0 == memcmp(needle,
335
21
                                  needle + p->period,
336
21
                                  p->cut * STRINGLIB_SIZEOF_CHAR));
337
21
    if (p->is_periodic) {
338
0
        assert(p->cut <= len_needle/2);
339
0
        assert(p->cut < p->period);
340
0
    }
341
21
    else {
342
        // A lower bound on the period
343
21
        p->period = Py_MAX(p->cut, len_needle - p->cut) + 1;
344
21
    }
345
    // The gap between the last character and the previous
346
    // occurrence of an equivalent character (modulo TABLE_SIZE)
347
21
    p->gap = len_needle;
348
21
    STRINGLIB_CHAR last = needle[len_needle - 1] & TABLE_MASK;
349
147
    for (Py_ssize_t i = len_needle - 2; i >= 0; i--) {
350
147
        STRINGLIB_CHAR x = needle[i] & TABLE_MASK;
351
147
        if (x == last) {
352
21
            p->gap = len_needle - 1 - i;
353
21
            break;
354
21
        }
355
147
    }
356
    // Fill up a compressed Boyer-Moore "Bad Character" table
357
21
    Py_ssize_t not_found_shift = Py_MIN(len_needle, MAX_SHIFT);
358
1.36k
    for (Py_ssize_t i = 0; i < (Py_ssize_t)TABLE_SIZE; i++) {
359
1.34k
        p->table[i] = Py_SAFE_DOWNCAST(not_found_shift,
360
1.34k
                                       Py_ssize_t, SHIFT_TYPE);
361
1.34k
    }
362
231
    for (Py_ssize_t i = len_needle - not_found_shift; i < len_needle; i++) {
363
210
        SHIFT_TYPE shift = Py_SAFE_DOWNCAST(len_needle - 1 - i,
364
210
                                            Py_ssize_t, SHIFT_TYPE);
365
210
        p->table[needle[i] & TABLE_MASK] = shift;
366
210
    }
367
21
}
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
21
{
373
    // Crochemore and Perrin's (1991) Two-Way algorithm.
374
    // See http://www-igm.univ-mlv.fr/~lecroq/string/node26.html#SECTION00260
375
21
    const Py_ssize_t len_needle = p->len_needle;
376
21
    const Py_ssize_t cut = p->cut;
377
21
    Py_ssize_t period = p->period;
378
21
    const STRINGLIB_CHAR *const needle = p->needle;
379
21
    const STRINGLIB_CHAR *window_last = haystack + len_needle - 1;
380
21
    const STRINGLIB_CHAR *const haystack_end = haystack + len_haystack;
381
21
    SHIFT_TYPE *table = p->table;
382
21
    const STRINGLIB_CHAR *window;
383
21
    LOG("===== Two-way: \"%s\" in \"%s\". =====\n", needle, haystack);
384
385
21
    Py_ssize_t gap = p->gap;
386
21
    Py_ssize_t gap_jump_end = Py_MIN(len_needle, cut + gap);
387
21
    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
21
    else {
454
21
        period = Py_MAX(gap, period);
455
21
        LOG("Needle is not periodic.\n");
456
14.0k
      windowloop:
457
14.0k
        while (window_last < haystack_end) {
458
71.4k
            for (;;) {
459
71.4k
                LOG_LINEUP();
460
71.4k
                Py_ssize_t shift = table[(*window_last) & TABLE_MASK];
461
71.4k
                window_last += shift;
462
71.4k
                if (shift == 0) {
463
14.0k
                    break;
464
14.0k
                }
465
57.4k
                if (window_last >= haystack_end) {
466
18
                    return -1;
467
18
                }
468
57.4k
                LOG("Horspool skip\n");
469
57.4k
            }
470
14.0k
            window = window_last - len_needle + 1;
471
14.0k
            assert((window[len_needle - 1] & TABLE_MASK) ==
472
14.0k
                   (needle[len_needle - 1] & TABLE_MASK));
473
14.0k
            Py_ssize_t i = cut;
474
14.2k
            for (; i < len_needle; i++) {
475
14.1k
                if (needle[i] != window[i]) {
476
13.9k
                    if (i < gap_jump_end) {
477
13.9k
                        LOG("Early right half mismatch: jump by gap.\n");
478
13.9k
                        assert(gap >= i - cut + 1);
479
13.9k
                        window_last += gap;
480
13.9k
                    }
481
0
                    else {
482
0
                        LOG("Late right half mismatch: jump by n (>gap)\n");
483
0
                        assert(i - cut + 1 > gap);
484
0
                        window_last += i - cut + 1;
485
0
                    }
486
13.9k
                    goto windowloop;
487
13.9k
                }
488
14.1k
            }
489
104
            for (Py_ssize_t i = 0; i < cut; i++) {
490
102
                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
102
            }
496
2
            LOG("Found a match!\n");
497
2
            return window - haystack;
498
88
        }
499
14.0k
    }
500
1
    LOG("Not found. Returning -1.\n");
501
1
    return -1;
502
21
}
Unexecuted instantiation: bytesobject.c:stringlib__two_way
Unexecuted instantiation: unicodeobject.c:asciilib__two_way
unicodeobject.c:ucs1lib__two_way
Line
Count
Source
372
21
{
373
    // Crochemore and Perrin's (1991) Two-Way algorithm.
374
    // See http://www-igm.univ-mlv.fr/~lecroq/string/node26.html#SECTION00260
375
21
    const Py_ssize_t len_needle = p->len_needle;
376
21
    const Py_ssize_t cut = p->cut;
377
21
    Py_ssize_t period = p->period;
378
21
    const STRINGLIB_CHAR *const needle = p->needle;
379
21
    const STRINGLIB_CHAR *window_last = haystack + len_needle - 1;
380
21
    const STRINGLIB_CHAR *const haystack_end = haystack + len_haystack;
381
21
    SHIFT_TYPE *table = p->table;
382
21
    const STRINGLIB_CHAR *window;
383
21
    LOG("===== Two-way: \"%s\" in \"%s\". =====\n", needle, haystack);
384
385
21
    Py_ssize_t gap = p->gap;
386
21
    Py_ssize_t gap_jump_end = Py_MIN(len_needle, cut + gap);
387
21
    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
21
    else {
454
21
        period = Py_MAX(gap, period);
455
21
        LOG("Needle is not periodic.\n");
456
14.0k
      windowloop:
457
14.0k
        while (window_last < haystack_end) {
458
71.4k
            for (;;) {
459
71.4k
                LOG_LINEUP();
460
71.4k
                Py_ssize_t shift = table[(*window_last) & TABLE_MASK];
461
71.4k
                window_last += shift;
462
71.4k
                if (shift == 0) {
463
14.0k
                    break;
464
14.0k
                }
465
57.4k
                if (window_last >= haystack_end) {
466
18
                    return -1;
467
18
                }
468
57.4k
                LOG("Horspool skip\n");
469
57.4k
            }
470
14.0k
            window = window_last - len_needle + 1;
471
14.0k
            assert((window[len_needle - 1] & TABLE_MASK) ==
472
14.0k
                   (needle[len_needle - 1] & TABLE_MASK));
473
14.0k
            Py_ssize_t i = cut;
474
14.2k
            for (; i < len_needle; i++) {
475
14.1k
                if (needle[i] != window[i]) {
476
13.9k
                    if (i < gap_jump_end) {
477
13.9k
                        LOG("Early right half mismatch: jump by gap.\n");
478
13.9k
                        assert(gap >= i - cut + 1);
479
13.9k
                        window_last += gap;
480
13.9k
                    }
481
0
                    else {
482
0
                        LOG("Late right half mismatch: jump by n (>gap)\n");
483
0
                        assert(i - cut + 1 > gap);
484
0
                        window_last += i - cut + 1;
485
0
                    }
486
13.9k
                    goto windowloop;
487
13.9k
                }
488
14.1k
            }
489
104
            for (Py_ssize_t i = 0; i < cut; i++) {
490
102
                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
102
            }
496
2
            LOG("Found a match!\n");
497
2
            return window - haystack;
498
88
        }
499
14.0k
    }
500
1
    LOG("Not found. Returning -1.\n");
501
1
    return -1;
502
21
}
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
21
{
511
21
    LOG("###### Finding \"%s\" in \"%s\".\n", needle, haystack);
512
21
    STRINGLIB(prework) p;
513
21
    STRINGLIB(_preprocess)(needle, len_needle, &p);
514
21
    return STRINGLIB(_two_way)(haystack, len_haystack, &p);
515
21
}
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
21
{
511
21
    LOG("###### Finding \"%s\" in \"%s\".\n", needle, haystack);
512
21
    STRINGLIB(prework) p;
513
21
    STRINGLIB(_preprocess)(needle, len_needle, &p);
514
21
    return STRINGLIB(_two_way)(haystack, len_haystack, &p);
515
21
}
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.5M
{
561
15.5M
    const Py_ssize_t w = n - m;
562
15.5M
    Py_ssize_t mlast = m - 1, count = 0;
563
15.5M
    Py_ssize_t gap = mlast;
564
15.5M
    const STRINGLIB_CHAR last = p[mlast];
565
15.5M
    const STRINGLIB_CHAR *const ss = &s[mlast];
566
567
15.5M
    unsigned long mask = 0;
568
31.1M
    for (Py_ssize_t i = 0; i < mlast; i++) {
569
15.6M
        STRINGLIB_BLOOM_ADD(mask, p[i]);
570
15.6M
        if (p[i] == last) {
571
470k
            gap = mlast - i - 1;
572
470k
        }
573
15.6M
    }
574
15.5M
    STRINGLIB_BLOOM_ADD(mask, last);
575
576
6.05G
    for (Py_ssize_t i = 0; i <= w; i++) {
577
6.04G
        if (ss[i] == last) {
578
            /* candidate match */
579
64.5M
            Py_ssize_t j;
580
84.8M
            for (j = 0; j < mlast; j++) {
581
64.6M
                if (s[i+j] != p[j]) {
582
44.3M
                    break;
583
44.3M
                }
584
64.6M
            }
585
64.5M
            if (j == mlast) {
586
                /* got a match! */
587
20.2M
                if (mode != FAST_COUNT) {
588
10.2M
                    return i;
589
10.2M
                }
590
10.0M
                count++;
591
10.0M
                if (count == maxcount) {
592
0
                    return maxcount;
593
0
                }
594
10.0M
                i = i + mlast;
595
10.0M
                continue;
596
10.0M
            }
597
            /* miss: check if next character is part of pattern */
598
44.3M
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
599
8.50M
                i = i + m;
600
8.50M
            }
601
35.8M
            else {
602
35.8M
                i = i + gap;
603
35.8M
            }
604
44.3M
        }
605
5.98G
        else {
606
            /* skip: check if next character is part of pattern */
607
5.98G
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
608
5.92G
                i = i + m;
609
5.92G
            }
610
5.98G
        }
611
6.04G
    }
612
5.28M
    return mode == FAST_COUNT ? count : -1;
613
15.5M
}
Unexecuted instantiation: bytesobject.c:stringlib_default_find
unicodeobject.c:asciilib_default_find
Line
Count
Source
560
1.58M
{
561
1.58M
    const Py_ssize_t w = n - m;
562
1.58M
    Py_ssize_t mlast = m - 1, count = 0;
563
1.58M
    Py_ssize_t gap = mlast;
564
1.58M
    const STRINGLIB_CHAR last = p[mlast];
565
1.58M
    const STRINGLIB_CHAR *const ss = &s[mlast];
566
567
1.58M
    unsigned long mask = 0;
568
3.17M
    for (Py_ssize_t i = 0; i < mlast; i++) {
569
1.59M
        STRINGLIB_BLOOM_ADD(mask, p[i]);
570
1.59M
        if (p[i] == last) {
571
17.5k
            gap = mlast - i - 1;
572
17.5k
        }
573
1.59M
    }
574
1.58M
    STRINGLIB_BLOOM_ADD(mask, last);
575
576
169M
    for (Py_ssize_t i = 0; i <= w; i++) {
577
169M
        if (ss[i] == last) {
578
            /* candidate match */
579
4.50M
            Py_ssize_t j;
580
6.06M
            for (j = 0; j < mlast; j++) {
581
4.51M
                if (s[i+j] != p[j]) {
582
2.94M
                    break;
583
2.94M
                }
584
4.51M
            }
585
4.50M
            if (j == mlast) {
586
                /* got a match! */
587
1.55M
                if (mode != FAST_COUNT) {
588
1.55M
                    return i;
589
1.55M
                }
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.94M
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
599
61.9k
                i = i + m;
600
61.9k
            }
601
2.88M
            else {
602
2.88M
                i = i + gap;
603
2.88M
            }
604
2.94M
        }
605
164M
        else {
606
            /* skip: check if next character is part of pattern */
607
164M
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
608
157M
                i = i + m;
609
157M
            }
610
164M
        }
611
169M
    }
612
27.9k
    return mode == FAST_COUNT ? count : -1;
613
1.58M
}
unicodeobject.c:ucs1lib_default_find
Line
Count
Source
560
6.77M
{
561
6.77M
    const Py_ssize_t w = n - m;
562
6.77M
    Py_ssize_t mlast = m - 1, count = 0;
563
6.77M
    Py_ssize_t gap = mlast;
564
6.77M
    const STRINGLIB_CHAR last = p[mlast];
565
6.77M
    const STRINGLIB_CHAR *const ss = &s[mlast];
566
567
6.77M
    unsigned long mask = 0;
568
13.5M
    for (Py_ssize_t i = 0; i < mlast; i++) {
569
6.82M
        STRINGLIB_BLOOM_ADD(mask, p[i]);
570
6.82M
        if (p[i] == last) {
571
389k
            gap = mlast - i - 1;
572
389k
        }
573
6.82M
    }
574
6.77M
    STRINGLIB_BLOOM_ADD(mask, last);
575
576
3.38G
    for (Py_ssize_t i = 0; i <= w; i++) {
577
3.38G
        if (ss[i] == last) {
578
            /* candidate match */
579
24.0M
            Py_ssize_t j;
580
28.9M
            for (j = 0; j < mlast; j++) {
581
24.0M
                if (s[i+j] != p[j]) {
582
19.1M
                    break;
583
19.1M
                }
584
24.0M
            }
585
24.0M
            if (j == mlast) {
586
                /* got a match! */
587
4.86M
                if (mode != FAST_COUNT) {
588
1.69M
                    return i;
589
1.69M
                }
590
3.16M
                count++;
591
3.16M
                if (count == maxcount) {
592
0
                    return maxcount;
593
0
                }
594
3.16M
                i = i + mlast;
595
3.16M
                continue;
596
3.16M
            }
597
            /* miss: check if next character is part of pattern */
598
19.1M
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
599
5.77M
                i = i + m;
600
5.77M
            }
601
13.4M
            else {
602
13.4M
                i = i + gap;
603
13.4M
            }
604
19.1M
        }
605
3.35G
        else {
606
            /* skip: check if next character is part of pattern */
607
3.35G
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
608
3.33G
                i = i + m;
609
3.33G
            }
610
3.35G
        }
611
3.38G
    }
612
5.07M
    return mode == FAST_COUNT ? count : -1;
613
6.77M
}
unicodeobject.c:ucs2lib_default_find
Line
Count
Source
560
3.02M
{
561
3.02M
    const Py_ssize_t w = n - m;
562
3.02M
    Py_ssize_t mlast = m - 1, count = 0;
563
3.02M
    Py_ssize_t gap = mlast;
564
3.02M
    const STRINGLIB_CHAR last = p[mlast];
565
3.02M
    const STRINGLIB_CHAR *const ss = &s[mlast];
566
567
3.02M
    unsigned long mask = 0;
568
6.09M
    for (Py_ssize_t i = 0; i < mlast; i++) {
569
3.06M
        STRINGLIB_BLOOM_ADD(mask, p[i]);
570
3.06M
        if (p[i] == last) {
571
38.5k
            gap = mlast - i - 1;
572
38.5k
        }
573
3.06M
    }
574
3.02M
    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
12.5M
            Py_ssize_t j;
580
18.3M
            for (j = 0; j < mlast; j++) {
581
12.5M
                if (s[i+j] != p[j]) {
582
6.82M
                    break;
583
6.82M
                }
584
12.5M
            }
585
12.5M
            if (j == mlast) {
586
                /* got a match! */
587
5.73M
                if (mode != FAST_COUNT) {
588
2.92M
                    return i;
589
2.92M
                }
590
2.81M
                count++;
591
2.81M
                if (count == maxcount) {
592
0
                    return maxcount;
593
0
                }
594
2.81M
                i = i + mlast;
595
2.81M
                continue;
596
2.81M
            }
597
            /* miss: check if next character is part of pattern */
598
6.82M
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
599
1.06M
                i = i + m;
600
1.06M
            }
601
5.76M
            else {
602
5.76M
                i = i + gap;
603
5.76M
            }
604
6.82M
        }
605
1.02G
        else {
606
            /* skip: check if next character is part of pattern */
607
1.02G
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
608
1.01G
                i = i + m;
609
1.01G
            }
610
1.02G
        }
611
1.03G
    }
612
106k
    return mode == FAST_COUNT ? count : -1;
613
3.02M
}
unicodeobject.c:ucs4lib_default_find
Line
Count
Source
560
4.12M
{
561
4.12M
    const Py_ssize_t w = n - m;
562
4.12M
    Py_ssize_t mlast = m - 1, count = 0;
563
4.12M
    Py_ssize_t gap = mlast;
564
4.12M
    const STRINGLIB_CHAR last = p[mlast];
565
4.12M
    const STRINGLIB_CHAR *const ss = &s[mlast];
566
567
4.12M
    unsigned long mask = 0;
568
8.25M
    for (Py_ssize_t i = 0; i < mlast; i++) {
569
4.13M
        STRINGLIB_BLOOM_ADD(mask, p[i]);
570
4.13M
        if (p[i] == last) {
571
22.2k
            gap = mlast - i - 1;
572
22.2k
        }
573
4.13M
    }
574
4.12M
    STRINGLIB_BLOOM_ADD(mask, last);
575
576
1.45G
    for (Py_ssize_t i = 0; i <= w; i++) {
577
1.45G
        if (ss[i] == last) {
578
            /* candidate match */
579
23.4M
            Py_ssize_t j;
580
31.5M
            for (j = 0; j < mlast; j++) {
581
23.4M
                if (s[i+j] != p[j]) {
582
15.3M
                    break;
583
15.3M
                }
584
23.4M
            }
585
23.4M
            if (j == mlast) {
586
                /* got a match! */
587
8.07M
                if (mode != FAST_COUNT) {
588
4.05M
                    return i;
589
4.05M
                }
590
4.02M
                count++;
591
4.02M
                if (count == maxcount) {
592
0
                    return maxcount;
593
0
                }
594
4.02M
                i = i + mlast;
595
4.02M
                continue;
596
4.02M
            }
597
            /* miss: check if next character is part of pattern */
598
15.3M
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
599
1.60M
                i = i + m;
600
1.60M
            }
601
13.7M
            else {
602
13.7M
                i = i + gap;
603
13.7M
            }
604
15.3M
        }
605
1.43G
        else {
606
            /* skip: check if next character is part of pattern */
607
1.43G
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
608
1.42G
                i = i + m;
609
1.42G
            }
610
1.43G
        }
611
1.45G
    }
612
69.5k
    return mode == FAST_COUNT ? count : -1;
613
4.12M
}
bytes_methods.c:stringlib_default_find
Line
Count
Source
560
3.05k
{
561
3.05k
    const Py_ssize_t w = n - m;
562
3.05k
    Py_ssize_t mlast = m - 1, count = 0;
563
3.05k
    Py_ssize_t gap = mlast;
564
3.05k
    const STRINGLIB_CHAR last = p[mlast];
565
3.05k
    const STRINGLIB_CHAR *const ss = &s[mlast];
566
567
3.05k
    unsigned long mask = 0;
568
12.2k
    for (Py_ssize_t i = 0; i < mlast; i++) {
569
9.16k
        STRINGLIB_BLOOM_ADD(mask, p[i]);
570
9.16k
        if (p[i] == last) {
571
3.05k
            gap = mlast - i - 1;
572
3.05k
        }
573
9.16k
    }
574
3.05k
    STRINGLIB_BLOOM_ADD(mask, last);
575
576
1.21M
    for (Py_ssize_t i = 0; i <= w; i++) {
577
1.21M
        if (ss[i] == last) {
578
            /* candidate match */
579
12.6k
            Py_ssize_t j;
580
21.3k
            for (j = 0; j < mlast; j++) {
581
18.4k
                if (s[i+j] != p[j]) {
582
9.81k
                    break;
583
9.81k
                }
584
18.4k
            }
585
12.6k
            if (j == mlast) {
586
                /* got a match! */
587
2.80k
                if (mode != FAST_COUNT) {
588
2.80k
                    return i;
589
2.80k
                }
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
9.81k
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
599
767
                i = i + m;
600
767
            }
601
9.04k
            else {
602
9.04k
                i = i + gap;
603
9.04k
            }
604
9.81k
        }
605
1.20M
        else {
606
            /* skip: check if next character is part of pattern */
607
1.20M
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
608
58.8k
                i = i + m;
609
58.8k
            }
610
1.20M
        }
611
1.21M
    }
612
252
    return mode == FAST_COUNT ? count : -1;
613
3.05k
}
Unexecuted instantiation: bytearrayobject.c:stringlib_default_find
614
615
616
static Py_ssize_t
617
STRINGLIB(adaptive_find)(const STRINGLIB_CHAR* s, Py_ssize_t n,
618
                         const STRINGLIB_CHAR* p, Py_ssize_t m,
619
                         Py_ssize_t maxcount, int mode)
620
0
{
621
0
    const Py_ssize_t w = n - m;
622
0
    Py_ssize_t mlast = m - 1, count = 0;
623
0
    Py_ssize_t gap = mlast;
624
0
    Py_ssize_t hits = 0, res;
625
0
    const STRINGLIB_CHAR last = p[mlast];
626
0
    const STRINGLIB_CHAR *const ss = &s[mlast];
627
628
0
    unsigned long mask = 0;
629
0
    for (Py_ssize_t i = 0; i < mlast; i++) {
630
0
        STRINGLIB_BLOOM_ADD(mask, p[i]);
631
0
        if (p[i] == last) {
632
0
            gap = mlast - i - 1;
633
0
        }
634
0
    }
635
0
    STRINGLIB_BLOOM_ADD(mask, last);
636
637
0
    for (Py_ssize_t i = 0; i <= w; i++) {
638
0
        if (ss[i] == last) {
639
            /* candidate match */
640
0
            Py_ssize_t j;
641
0
            for (j = 0; j < mlast; j++) {
642
0
                if (s[i+j] != p[j]) {
643
0
                    break;
644
0
                }
645
0
            }
646
0
            if (j == mlast) {
647
                /* got a match! */
648
0
                if (mode != FAST_COUNT) {
649
0
                    return i;
650
0
                }
651
0
                count++;
652
0
                if (count == maxcount) {
653
0
                    return maxcount;
654
0
                }
655
0
                i = i + mlast;
656
0
                continue;
657
0
            }
658
0
            hits += j + 1;
659
0
            if (hits > m / 4 && w - i > 2000) {
660
0
                if (mode == FAST_SEARCH) {
661
0
                    res = STRINGLIB(_two_way_find)(s + i, n - i, p, m);
662
0
                    return res == -1 ? -1 : res + i;
663
0
                }
664
0
                else {
665
0
                    res = STRINGLIB(_two_way_count)(s + i, n - i, p, m,
666
0
                                                    maxcount - count);
667
0
                    return res + count;
668
0
                }
669
0
            }
670
            /* miss: check if next character is part of pattern */
671
0
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
672
0
                i = i + m;
673
0
            }
674
0
            else {
675
0
                i = i + gap;
676
0
            }
677
0
        }
678
0
        else {
679
            /* skip: check if next character is part of pattern */
680
0
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
681
0
                i = i + m;
682
0
            }
683
0
        }
684
0
    }
685
0
    return mode == FAST_COUNT ? count : -1;
686
0
}
Unexecuted instantiation: bytesobject.c:stringlib_adaptive_find
Unexecuted instantiation: unicodeobject.c:asciilib_adaptive_find
Unexecuted instantiation: unicodeobject.c:ucs1lib_adaptive_find
Unexecuted instantiation: unicodeobject.c:ucs2lib_adaptive_find
Unexecuted instantiation: unicodeobject.c:ucs4lib_adaptive_find
Unexecuted instantiation: bytes_methods.c:stringlib_adaptive_find
Unexecuted instantiation: bytearrayobject.c:stringlib_adaptive_find
687
688
689
static Py_ssize_t
690
STRINGLIB(default_rfind)(const STRINGLIB_CHAR* s, Py_ssize_t n,
691
                         const STRINGLIB_CHAR* p, Py_ssize_t m,
692
                         Py_ssize_t maxcount, int mode)
693
4
{
694
    /* create compressed boyer-moore delta 1 table */
695
4
    unsigned long mask = 0;
696
4
    Py_ssize_t i, j, mlast = m - 1, skip = m - 1, w = n - m;
697
698
    /* process pattern[0] outside the loop */
699
4
    STRINGLIB_BLOOM_ADD(mask, p[0]);
700
    /* process pattern[:0:-1] */
701
16
    for (i = mlast; i > 0; i--) {
702
12
        STRINGLIB_BLOOM_ADD(mask, p[i]);
703
12
        if (p[i] == p[0]) {
704
0
            skip = i - 1;
705
0
        }
706
12
    }
707
708
356
    for (i = w; i >= 0; i--) {
709
352
        if (s[i] == p[0]) {
710
            /* candidate match */
711
8
            for (j = mlast; j > 0; j--) {
712
8
                if (s[i+j] != p[j]) {
713
8
                    break;
714
8
                }
715
8
            }
716
8
            if (j == 0) {
717
                /* got a match! */
718
0
                return i;
719
0
            }
720
            /* miss: check if previous character is part of pattern */
721
8
            if (i > 0 && !STRINGLIB_BLOOM(mask, s[i-1])) {
722
8
                i = i - m;
723
8
            }
724
0
            else {
725
0
                i = i - skip;
726
0
            }
727
8
        }
728
344
        else {
729
            /* skip: check if previous character is part of pattern */
730
344
            if (i > 0 && !STRINGLIB_BLOOM(mask, s[i-1])) {
731
336
                i = i - m;
732
336
            }
733
344
        }
734
352
    }
735
4
    return -1;
736
4
}
Unexecuted instantiation: bytesobject.c:stringlib_default_rfind
Unexecuted instantiation: unicodeobject.c:asciilib_default_rfind
Unexecuted instantiation: unicodeobject.c:ucs1lib_default_rfind
Unexecuted instantiation: unicodeobject.c:ucs2lib_default_rfind
Unexecuted instantiation: unicodeobject.c:ucs4lib_default_rfind
bytes_methods.c:stringlib_default_rfind
Line
Count
Source
693
4
{
694
    /* create compressed boyer-moore delta 1 table */
695
4
    unsigned long mask = 0;
696
4
    Py_ssize_t i, j, mlast = m - 1, skip = m - 1, w = n - m;
697
698
    /* process pattern[0] outside the loop */
699
4
    STRINGLIB_BLOOM_ADD(mask, p[0]);
700
    /* process pattern[:0:-1] */
701
16
    for (i = mlast; i > 0; i--) {
702
12
        STRINGLIB_BLOOM_ADD(mask, p[i]);
703
12
        if (p[i] == p[0]) {
704
0
            skip = i - 1;
705
0
        }
706
12
    }
707
708
356
    for (i = w; i >= 0; i--) {
709
352
        if (s[i] == p[0]) {
710
            /* candidate match */
711
8
            for (j = mlast; j > 0; j--) {
712
8
                if (s[i+j] != p[j]) {
713
8
                    break;
714
8
                }
715
8
            }
716
8
            if (j == 0) {
717
                /* got a match! */
718
0
                return i;
719
0
            }
720
            /* miss: check if previous character is part of pattern */
721
8
            if (i > 0 && !STRINGLIB_BLOOM(mask, s[i-1])) {
722
8
                i = i - m;
723
8
            }
724
0
            else {
725
0
                i = i - skip;
726
0
            }
727
8
        }
728
344
        else {
729
            /* skip: check if previous character is part of pattern */
730
344
            if (i > 0 && !STRINGLIB_BLOOM(mask, s[i-1])) {
731
336
                i = i - m;
732
336
            }
733
344
        }
734
352
    }
735
4
    return -1;
736
4
}
Unexecuted instantiation: bytearrayobject.c:stringlib_default_rfind
737
738
739
static inline Py_ssize_t
740
STRINGLIB(count_char)(const STRINGLIB_CHAR *s, Py_ssize_t n,
741
                      const STRINGLIB_CHAR p0, Py_ssize_t maxcount)
742
0
{
743
0
    Py_ssize_t i, count = 0;
744
0
    for (i = 0; i < n; i++) {
745
0
        if (s[i] == p0) {
746
0
            count++;
747
0
            if (count == maxcount) {
748
0
                return maxcount;
749
0
            }
750
0
        }
751
0
    }
752
0
    return count;
753
0
}
Unexecuted instantiation: bytesobject.c:stringlib_count_char
Unexecuted instantiation: unicodeobject.c:asciilib_count_char
Unexecuted instantiation: unicodeobject.c:ucs1lib_count_char
Unexecuted instantiation: unicodeobject.c:ucs2lib_count_char
Unexecuted instantiation: unicodeobject.c:ucs4lib_count_char
Unexecuted instantiation: bytes_methods.c:stringlib_count_char
Unexecuted instantiation: bytearrayobject.c:stringlib_count_char
754
755
756
static inline Py_ssize_t
757
STRINGLIB(count_char_no_maxcount)(const STRINGLIB_CHAR *s, Py_ssize_t n,
758
                                  const STRINGLIB_CHAR p0)
759
/* A specialized function of count_char that does not cut off at a maximum.
760
   As a result, the compiler is able to vectorize the loop. */
761
56.4M
{
762
56.4M
    Py_ssize_t count = 0;
763
14.6G
    for (Py_ssize_t i = 0; i < n; i++) {
764
14.5G
        if (s[i] == p0) {
765
259M
            count++;
766
259M
        }
767
14.5G
    }
768
56.4M
    return count;
769
56.4M
}
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.1M
{
762
47.1M
    Py_ssize_t count = 0;
763
10.1G
    for (Py_ssize_t i = 0; i < n; i++) {
764
10.1G
        if (s[i] == p0) {
765
64.6M
            count++;
766
64.6M
        }
767
10.1G
    }
768
47.1M
    return count;
769
47.1M
}
unicodeobject.c:ucs2lib_count_char_no_maxcount
Line
Count
Source
761
8.41M
{
762
8.41M
    Py_ssize_t count = 0;
763
2.07G
    for (Py_ssize_t i = 0; i < n; i++) {
764
2.06G
        if (s[i] == p0) {
765
76.4M
            count++;
766
76.4M
        }
767
2.06G
    }
768
8.41M
    return count;
769
8.41M
}
unicodeobject.c:ucs4lib_count_char_no_maxcount
Line
Count
Source
761
897k
{
762
897k
    Py_ssize_t count = 0;
763
2.35G
    for (Py_ssize_t i = 0; i < n; i++) {
764
2.35G
        if (s[i] == p0) {
765
117M
            count++;
766
117M
        }
767
2.35G
    }
768
897k
    return count;
769
897k
}
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
232M
{
777
232M
    if (n < m || (mode == FAST_COUNT && maxcount == 0)) {
778
9.26k
        return -1;
779
9.26k
    }
780
781
    /* look for special cases */
782
232M
    if (m <= 1) {
783
216M
        if (m <= 0) {
784
0
            return -1;
785
0
        }
786
        /* use special case for 1-character strings */
787
216M
        if (mode == FAST_SEARCH)
788
160M
            return STRINGLIB(find_char)(s, n, p[0]);
789
56.4M
        else if (mode == FAST_RSEARCH)
790
11.1k
            return STRINGLIB(rfind_char)(s, n, p[0]);
791
56.4M
        else {
792
56.4M
            if (maxcount == PY_SSIZE_T_MAX) {
793
56.4M
                return STRINGLIB(count_char_no_maxcount)(s, n, p[0]);
794
56.4M
            }
795
0
            return STRINGLIB(count_char)(s, n, p[0], maxcount);
796
56.4M
        }
797
216M
    }
798
799
15.5M
    if (mode != FAST_RSEARCH) {
800
15.5M
        if (n < 2500 || (m < 100 && n < 30000) || m < 6) {
801
15.5M
            return STRINGLIB(default_find)(s, n, p, m, maxcount, mode);
802
15.5M
        }
803
21
        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
21
            if (mode == FAST_SEARCH) {
810
21
                return STRINGLIB(_two_way_find)(s, n, p, m);
811
21
            }
812
0
            else {
813
0
                return STRINGLIB(_two_way_count)(s, n, p, m, maxcount);
814
0
            }
815
21
        }
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.5M
    }
825
4
    else {
826
        /* FAST_RSEARCH */
827
4
        return STRINGLIB(default_rfind)(s, n, p, m, maxcount, mode);
828
4
    }
829
15.5M
}
Unexecuted instantiation: bytesobject.c:fastsearch
unicodeobject.c:asciilib_fastsearch
Line
Count
Source
776
25.6M
{
777
25.6M
    if (n < m || (mode == FAST_COUNT && maxcount == 0)) {
778
0
        return -1;
779
0
    }
780
781
    /* look for special cases */
782
25.6M
    if (m <= 1) {
783
24.0M
        if (m <= 0) {
784
0
            return -1;
785
0
        }
786
        /* use special case for 1-character strings */
787
24.0M
        if (mode == FAST_SEARCH)
788
24.0M
            return STRINGLIB(find_char)(s, n, p[0]);
789
11.1k
        else if (mode == FAST_RSEARCH)
790
11.1k
            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
24.0M
    }
798
799
1.58M
    if (mode != FAST_RSEARCH) {
800
1.58M
        if (n < 2500 || (m < 100 && n < 30000) || m < 6) {
801
1.58M
            return STRINGLIB(default_find)(s, n, p, m, maxcount, mode);
802
1.58M
        }
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.58M
    }
825
0
    else {
826
        /* FAST_RSEARCH */
827
0
        return STRINGLIB(default_rfind)(s, n, p, m, maxcount, mode);
828
0
    }
829
1.58M
}
unicodeobject.c:ucs1lib_fastsearch
Line
Count
Source
776
60.1M
{
777
60.1M
    if (n < m || (mode == FAST_COUNT && maxcount == 0)) {
778
0
        return -1;
779
0
    }
780
781
    /* look for special cases */
782
60.1M
    if (m <= 1) {
783
53.3M
        if (m <= 0) {
784
0
            return -1;
785
0
        }
786
        /* use special case for 1-character strings */
787
53.3M
        if (mode == FAST_SEARCH)
788
6.24M
            return STRINGLIB(find_char)(s, n, p[0]);
789
47.1M
        else if (mode == FAST_RSEARCH)
790
0
            return STRINGLIB(rfind_char)(s, n, p[0]);
791
47.1M
        else {
792
47.1M
            if (maxcount == PY_SSIZE_T_MAX) {
793
47.1M
                return STRINGLIB(count_char_no_maxcount)(s, n, p[0]);
794
47.1M
            }
795
0
            return STRINGLIB(count_char)(s, n, p[0], maxcount);
796
47.1M
        }
797
53.3M
    }
798
799
6.77M
    if (mode != FAST_RSEARCH) {
800
6.77M
        if (n < 2500 || (m < 100 && n < 30000) || m < 6) {
801
6.77M
            return STRINGLIB(default_find)(s, n, p, m, maxcount, mode);
802
6.77M
        }
803
21
        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
21
            if (mode == FAST_SEARCH) {
810
21
                return STRINGLIB(_two_way_find)(s, n, p, m);
811
21
            }
812
0
            else {
813
0
                return STRINGLIB(_two_way_count)(s, n, p, m, maxcount);
814
0
            }
815
21
        }
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.77M
    }
825
0
    else {
826
        /* FAST_RSEARCH */
827
0
        return STRINGLIB(default_rfind)(s, n, p, m, maxcount, mode);
828
0
    }
829
6.77M
}
unicodeobject.c:ucs2lib_fastsearch
Line
Count
Source
776
71.3M
{
777
71.3M
    if (n < m || (mode == FAST_COUNT && maxcount == 0)) {
778
9.25k
        return -1;
779
9.25k
    }
780
781
    /* look for special cases */
782
71.3M
    if (m <= 1) {
783
68.2M
        if (m <= 0) {
784
0
            return -1;
785
0
        }
786
        /* use special case for 1-character strings */
787
68.2M
        if (mode == FAST_SEARCH)
788
59.8M
            return STRINGLIB(find_char)(s, n, p[0]);
789
8.41M
        else if (mode == FAST_RSEARCH)
790
0
            return STRINGLIB(rfind_char)(s, n, p[0]);
791
8.41M
        else {
792
8.41M
            if (maxcount == PY_SSIZE_T_MAX) {
793
8.41M
                return STRINGLIB(count_char_no_maxcount)(s, n, p[0]);
794
8.41M
            }
795
0
            return STRINGLIB(count_char)(s, n, p[0], maxcount);
796
8.41M
        }
797
68.2M
    }
798
799
3.02M
    if (mode != FAST_RSEARCH) {
800
3.02M
        if (n < 2500 || (m < 100 && n < 30000) || m < 6) {
801
3.02M
            return STRINGLIB(default_find)(s, n, p, m, maxcount, mode);
802
3.02M
        }
803
0
        else if ((m >> 2) * 3 < (n >> 2)) {
804
            /* 33% threshold, but don't overflow. */
805
            /* For larger problems where the needle isn't a huge
806
               percentage of the size of the haystack, the relatively
807
               expensive O(m) startup cost of the two-way algorithm
808
               will surely pay off. */
809
0
            if (mode == FAST_SEARCH) {
810
0
                return STRINGLIB(_two_way_find)(s, n, p, m);
811
0
            }
812
0
            else {
813
0
                return STRINGLIB(_two_way_count)(s, n, p, m, maxcount);
814
0
            }
815
0
        }
816
0
        else {
817
            /* To ensure that we have good worst-case behavior,
818
               here's an adaptive version of the algorithm, where if
819
               we match O(m) characters without any matches of the
820
               entire needle, then we predict that the startup cost of
821
               the two-way algorithm will probably be worth it. */
822
0
            return STRINGLIB(adaptive_find)(s, n, p, m, maxcount, mode);
823
0
        }
824
3.02M
    }
825
0
    else {
826
        /* FAST_RSEARCH */
827
0
        return STRINGLIB(default_rfind)(s, n, p, m, maxcount, mode);
828
0
    }
829
3.02M
}
unicodeobject.c:ucs4lib_fastsearch
Line
Count
Source
776
75.1M
{
777
75.1M
    if (n < m || (mode == FAST_COUNT && maxcount == 0)) {
778
0
        return -1;
779
0
    }
780
781
    /* look for special cases */
782
75.1M
    if (m <= 1) {
783
70.9M
        if (m <= 0) {
784
0
            return -1;
785
0
        }
786
        /* use special case for 1-character strings */
787
70.9M
        if (mode == FAST_SEARCH)
788
70.0M
            return STRINGLIB(find_char)(s, n, p[0]);
789
897k
        else if (mode == FAST_RSEARCH)
790
0
            return STRINGLIB(rfind_char)(s, n, p[0]);
791
897k
        else {
792
897k
            if (maxcount == PY_SSIZE_T_MAX) {
793
897k
                return STRINGLIB(count_char_no_maxcount)(s, n, p[0]);
794
897k
            }
795
0
            return STRINGLIB(count_char)(s, n, p[0], maxcount);
796
897k
        }
797
70.9M
    }
798
799
4.12M
    if (mode != FAST_RSEARCH) {
800
4.12M
        if (n < 2500 || (m < 100 && n < 30000) || m < 6) {
801
4.12M
            return STRINGLIB(default_find)(s, n, p, m, maxcount, mode);
802
4.12M
        }
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.12M
    }
825
0
    else {
826
        /* FAST_RSEARCH */
827
0
        return STRINGLIB(default_rfind)(s, n, p, m, maxcount, mode);
828
0
    }
829
4.12M
}
bytes_methods.c:fastsearch
Line
Count
Source
776
3.07k
{
777
3.07k
    if (n < m || (mode == FAST_COUNT && maxcount == 0)) {
778
10
        return -1;
779
10
    }
780
781
    /* look for special cases */
782
3.06k
    if (m <= 1) {
783
0
        if (m <= 0) {
784
0
            return -1;
785
0
        }
786
        /* use special case for 1-character strings */
787
0
        if (mode == FAST_SEARCH)
788
0
            return STRINGLIB(find_char)(s, n, p[0]);
789
0
        else if (mode == FAST_RSEARCH)
790
0
            return STRINGLIB(rfind_char)(s, n, p[0]);
791
0
        else {
792
0
            if (maxcount == PY_SSIZE_T_MAX) {
793
0
                return STRINGLIB(count_char_no_maxcount)(s, n, p[0]);
794
0
            }
795
0
            return STRINGLIB(count_char)(s, n, p[0], maxcount);
796
0
        }
797
0
    }
798
799
3.06k
    if (mode != FAST_RSEARCH) {
800
3.05k
        if (n < 2500 || (m < 100 && n < 30000) || m < 6) {
801
3.05k
            return STRINGLIB(default_find)(s, n, p, m, maxcount, mode);
802
3.05k
        }
803
0
        else if ((m >> 2) * 3 < (n >> 2)) {
804
            /* 33% threshold, but don't overflow. */
805
            /* For larger problems where the needle isn't a huge
806
               percentage of the size of the haystack, the relatively
807
               expensive O(m) startup cost of the two-way algorithm
808
               will surely pay off. */
809
0
            if (mode == FAST_SEARCH) {
810
0
                return STRINGLIB(_two_way_find)(s, n, p, m);
811
0
            }
812
0
            else {
813
0
                return STRINGLIB(_two_way_count)(s, n, p, m, maxcount);
814
0
            }
815
0
        }
816
0
        else {
817
            /* To ensure that we have good worst-case behavior,
818
               here's an adaptive version of the algorithm, where if
819
               we match O(m) characters without any matches of the
820
               entire needle, then we predict that the startup cost of
821
               the two-way algorithm will probably be worth it. */
822
0
            return STRINGLIB(adaptive_find)(s, n, p, m, maxcount, mode);
823
0
        }
824
3.05k
    }
825
4
    else {
826
        /* FAST_RSEARCH */
827
4
        return STRINGLIB(default_rfind)(s, n, p, m, maxcount, mode);
828
4
    }
829
3.06k
}
Unexecuted instantiation: bytearrayobject.c:fastsearch
830