Coverage Report

Created: 2026-03-08 06:40

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
575M
#define FAST_COUNT 0
25
357M
#define FAST_SEARCH 1
26
97.3M
#define FAST_RSEARCH 2
27
28
#if LONG_BIT >= 128
29
#define STRINGLIB_BLOOM_WIDTH 128
30
#elif LONG_BIT >= 64
31
3.54G
#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
156M
    ((mask |= (1UL << ((ch) & (STRINGLIB_BLOOM_WIDTH -1)))))
40
#define STRINGLIB_BLOOM(mask, ch)     \
41
3.38G
    ((mask &  (1UL << ((ch) & (STRINGLIB_BLOOM_WIDTH -1)))))
42
43
#ifdef STRINGLIB_FAST_MEMCHR
44
285M
#  define MEMCHR_CUT_OFF 15
45
#else
46
66.2M
#  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
351M
{
52
351M
    const STRINGLIB_CHAR *p, *e;
53
54
351M
    p = s;
55
351M
    e = s + n;
56
351M
    if (n > MEMCHR_CUT_OFF) {
57
#ifdef STRINGLIB_FAST_MEMCHR
58
84.8M
        p = STRINGLIB_FAST_MEMCHR(s, ch, n);
59
84.8M
        if (p != NULL)
60
82.6M
            return (p - s);
61
2.18M
        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
54.8M
        if (needle != 0) {
71
54.3M
            do {
72
54.3M
                const void *candidate = memchr(p, needle,
73
54.3M
                                               (e - p) * sizeof(STRINGLIB_CHAR));
74
54.3M
                if (candidate == NULL)
75
571k
                    return -1;
76
53.8M
                s1 = p;
77
53.8M
                p = (const STRINGLIB_CHAR *)
78
53.8M
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
79
53.8M
                if (*p == ch)
80
53.7M
                    return (p - s);
81
                /* False positive */
82
120k
                p++;
83
120k
                if (p - s1 > MEMCHR_CUT_OFF)
84
57.5k
                    continue;
85
63.2k
                if (e - p <= MEMCHR_CUT_OFF)
86
3.75k
                    break;
87
59.4k
                e1 = p + MEMCHR_CUT_OFF;
88
1.73M
                while (p != e1) {
89
1.70M
                    if (*p == ch)
90
27.4k
                        return (p - s);
91
1.67M
                    p++;
92
1.67M
                }
93
59.4k
            }
94
54.3M
            while (e - p > MEMCHR_CUT_OFF);
95
54.3M
        }
96
#endif
97
139M
    }
98
1.18G
    while (p < e) {
99
997M
        if (*p == ch)
100
25.3M
            return (p - s);
101
972M
        p++;
102
972M
    }
103
187M
    return -1;
104
212M
}
Unexecuted instantiation: bytesobject.c:stringlib_find_char
unicodeobject.c:ucs1lib_find_char
Line
Count
Source
51
219M
{
52
219M
    const STRINGLIB_CHAR *p, *e;
53
54
219M
    p = s;
55
219M
    e = s + n;
56
219M
    if (n > MEMCHR_CUT_OFF) {
57
22.5M
#ifdef STRINGLIB_FAST_MEMCHR
58
22.5M
        p = STRINGLIB_FAST_MEMCHR(s, ch, n);
59
22.5M
        if (p != NULL)
60
21.0M
            return (p - s);
61
1.44M
        return -1;
62
#else
63
        /* use memchr if we can choose a needle without too many likely
64
           false positives */
65
        const STRINGLIB_CHAR *s1, *e1;
66
        unsigned char needle = ch & 0xff;
67
        /* If looking for a multiple of 256, we'd have too
68
           many false positives looking for the '\0' byte in UCS2
69
           and UCS4 representations. */
70
        if (needle != 0) {
71
            do {
72
                const void *candidate = memchr(p, needle,
73
                                               (e - p) * sizeof(STRINGLIB_CHAR));
74
                if (candidate == NULL)
75
                    return -1;
76
                s1 = p;
77
                p = (const STRINGLIB_CHAR *)
78
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
79
                if (*p == ch)
80
                    return (p - s);
81
                /* False positive */
82
                p++;
83
                if (p - s1 > MEMCHR_CUT_OFF)
84
                    continue;
85
                if (e - p <= MEMCHR_CUT_OFF)
86
                    break;
87
                e1 = p + MEMCHR_CUT_OFF;
88
                while (p != e1) {
89
                    if (*p == ch)
90
                        return (p - s);
91
                    p++;
92
                }
93
            }
94
            while (e - p > MEMCHR_CUT_OFF);
95
        }
96
#endif
97
22.5M
    }
98
1.02G
    while (p < e) {
99
839M
        if (*p == ch)
100
12.7M
            return (p - s);
101
827M
        p++;
102
827M
    }
103
184M
    return -1;
104
196M
}
unicodeobject.c:ucs2lib_find_char
Line
Count
Source
51
65.8M
{
52
65.8M
    const STRINGLIB_CHAR *p, *e;
53
54
65.8M
    p = s;
55
65.8M
    e = s + n;
56
65.8M
    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
54.8M
        const STRINGLIB_CHAR *s1, *e1;
66
54.8M
        unsigned char needle = ch & 0xff;
67
        /* If looking for a multiple of 256, we'd have too
68
           many false positives looking for the '\0' byte in UCS2
69
           and UCS4 representations. */
70
54.8M
        if (needle != 0) {
71
54.3M
            do {
72
54.3M
                const void *candidate = memchr(p, needle,
73
54.3M
                                               (e - p) * sizeof(STRINGLIB_CHAR));
74
54.3M
                if (candidate == NULL)
75
571k
                    return -1;
76
53.8M
                s1 = p;
77
53.8M
                p = (const STRINGLIB_CHAR *)
78
53.8M
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
79
53.8M
                if (*p == ch)
80
53.7M
                    return (p - s);
81
                /* False positive */
82
120k
                p++;
83
120k
                if (p - s1 > MEMCHR_CUT_OFF)
84
57.5k
                    continue;
85
63.2k
                if (e - p <= MEMCHR_CUT_OFF)
86
3.75k
                    break;
87
59.4k
                e1 = p + MEMCHR_CUT_OFF;
88
1.73M
                while (p != e1) {
89
1.70M
                    if (*p == ch)
90
27.4k
                        return (p - s);
91
1.67M
                    p++;
92
1.67M
                }
93
59.4k
            }
94
54.3M
            while (e - p > MEMCHR_CUT_OFF);
95
54.3M
        }
96
54.8M
#endif
97
54.8M
    }
98
149M
    while (p < e) {
99
145M
        if (*p == ch)
100
8.41M
            return (p - s);
101
137M
        p++;
102
137M
    }
103
3.17M
    return -1;
104
11.5M
}
unicodeobject.c:ucs4lib_find_char
Line
Count
Source
51
40.9M
{
52
40.9M
    const STRINGLIB_CHAR *p, *e;
53
54
40.9M
    p = s;
55
40.9M
    e = s + n;
56
40.9M
    if (n > MEMCHR_CUT_OFF) {
57
40.8M
#ifdef STRINGLIB_FAST_MEMCHR
58
40.8M
        p = STRINGLIB_FAST_MEMCHR(s, ch, n);
59
40.8M
        if (p != NULL)
60
40.8M
            return (p - s);
61
33.9k
        return -1;
62
#else
63
        /* use memchr if we can choose a needle without too many likely
64
           false positives */
65
        const STRINGLIB_CHAR *s1, *e1;
66
        unsigned char needle = ch & 0xff;
67
        /* If looking for a multiple of 256, we'd have too
68
           many false positives looking for the '\0' byte in UCS2
69
           and UCS4 representations. */
70
        if (needle != 0) {
71
            do {
72
                const void *candidate = memchr(p, needle,
73
                                               (e - p) * sizeof(STRINGLIB_CHAR));
74
                if (candidate == NULL)
75
                    return -1;
76
                s1 = p;
77
                p = (const STRINGLIB_CHAR *)
78
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
79
                if (*p == ch)
80
                    return (p - s);
81
                /* False positive */
82
                p++;
83
                if (p - s1 > MEMCHR_CUT_OFF)
84
                    continue;
85
                if (e - p <= MEMCHR_CUT_OFF)
86
                    break;
87
                e1 = p + MEMCHR_CUT_OFF;
88
                while (p != e1) {
89
                    if (*p == ch)
90
                        return (p - s);
91
                    p++;
92
                }
93
            }
94
            while (e - p > MEMCHR_CUT_OFF);
95
        }
96
#endif
97
40.8M
    }
98
315k
    while (p < e) {
99
276k
        if (*p == ch)
100
36.0k
            return (p - s);
101
240k
        p++;
102
240k
    }
103
38.1k
    return -1;
104
74.1k
}
unicodeobject.c:asciilib_find_char
Line
Count
Source
51
25.2M
{
52
25.2M
    const STRINGLIB_CHAR *p, *e;
53
54
25.2M
    p = s;
55
25.2M
    e = s + n;
56
25.2M
    if (n > MEMCHR_CUT_OFF) {
57
20.9M
#ifdef STRINGLIB_FAST_MEMCHR
58
20.9M
        p = STRINGLIB_FAST_MEMCHR(s, ch, n);
59
20.9M
        if (p != NULL)
60
20.2M
            return (p - s);
61
683k
        return -1;
62
#else
63
        /* use memchr if we can choose a needle without too many likely
64
           false positives */
65
        const STRINGLIB_CHAR *s1, *e1;
66
        unsigned char needle = ch & 0xff;
67
        /* If looking for a multiple of 256, we'd have too
68
           many false positives looking for the '\0' byte in UCS2
69
           and UCS4 representations. */
70
        if (needle != 0) {
71
            do {
72
                const void *candidate = memchr(p, needle,
73
                                               (e - p) * sizeof(STRINGLIB_CHAR));
74
                if (candidate == NULL)
75
                    return -1;
76
                s1 = p;
77
                p = (const STRINGLIB_CHAR *)
78
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
79
                if (*p == ch)
80
                    return (p - s);
81
                /* False positive */
82
                p++;
83
                if (p - s1 > MEMCHR_CUT_OFF)
84
                    continue;
85
                if (e - p <= MEMCHR_CUT_OFF)
86
                    break;
87
                e1 = p + MEMCHR_CUT_OFF;
88
                while (p != e1) {
89
                    if (*p == ch)
90
                        return (p - s);
91
                    p++;
92
                }
93
            }
94
            while (e - p > MEMCHR_CUT_OFF);
95
        }
96
#endif
97
20.9M
    }
98
11.7M
    while (p < e) {
99
11.7M
        if (*p == ch)
100
4.20M
            return (p - s);
101
7.49M
        p++;
102
7.49M
    }
103
57.8k
    return -1;
104
4.26M
}
bytes_methods.c:stringlib_find_char
Line
Count
Source
51
537k
{
52
537k
    const STRINGLIB_CHAR *p, *e;
53
54
537k
    p = s;
55
537k
    e = s + n;
56
537k
    if (n > MEMCHR_CUT_OFF) {
57
535k
#ifdef STRINGLIB_FAST_MEMCHR
58
535k
        p = STRINGLIB_FAST_MEMCHR(s, ch, n);
59
535k
        if (p != NULL)
60
512k
            return (p - s);
61
23.3k
        return -1;
62
#else
63
        /* use memchr if we can choose a needle without too many likely
64
           false positives */
65
        const STRINGLIB_CHAR *s1, *e1;
66
        unsigned char needle = ch & 0xff;
67
        /* If looking for a multiple of 256, we'd have too
68
           many false positives looking for the '\0' byte in UCS2
69
           and UCS4 representations. */
70
        if (needle != 0) {
71
            do {
72
                const void *candidate = memchr(p, needle,
73
                                               (e - p) * sizeof(STRINGLIB_CHAR));
74
                if (candidate == NULL)
75
                    return -1;
76
                s1 = p;
77
                p = (const STRINGLIB_CHAR *)
78
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
79
                if (*p == ch)
80
                    return (p - s);
81
                /* False positive */
82
                p++;
83
                if (p - s1 > MEMCHR_CUT_OFF)
84
                    continue;
85
                if (e - p <= MEMCHR_CUT_OFF)
86
                    break;
87
                e1 = p + MEMCHR_CUT_OFF;
88
                while (p != e1) {
89
                    if (*p == ch)
90
                        return (p - s);
91
                    p++;
92
                }
93
            }
94
            while (e - p > MEMCHR_CUT_OFF);
95
        }
96
#endif
97
535k
    }
98
5.91k
    while (p < e) {
99
4.72k
        if (*p == ch)
100
1.16k
            return (p - s);
101
3.55k
        p++;
102
3.55k
    }
103
1.18k
    return -1;
104
2.35k
}
Unexecuted instantiation: bytearrayobject.c:stringlib_find_char
105
106
#undef MEMCHR_CUT_OFF
107
108
#if STRINGLIB_SIZEOF_CHAR == 1
109
151k
#  define MEMRCHR_CUT_OFF 15
110
#else
111
377k
#  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
498k
{
118
498k
    const STRINGLIB_CHAR *p;
119
498k
#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
498k
    if (n > MEMRCHR_CUT_OFF) {
126
#if STRINGLIB_SIZEOF_CHAR == 1
127
        p = memrchr(s, ch, n);
128
93.0k
        if (p != NULL)
129
89.7k
            return (p - s);
130
3.28k
        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
285k
        if (needle != 0) {
141
294k
            do {
142
294k
                void *candidate = memrchr(s, needle,
143
294k
                                          n * sizeof(STRINGLIB_CHAR));
144
294k
                if (candidate == NULL)
145
1.21k
                    return -1;
146
293k
                n1 = n;
147
293k
                p = (const STRINGLIB_CHAR *)
148
293k
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
149
293k
                n = p - s;
150
293k
                if (*p == ch)
151
282k
                    return n;
152
                /* False positive */
153
11.1k
                if (n1 - n > MEMRCHR_CUT_OFF)
154
5.86k
                    continue;
155
5.33k
                if (n <= MEMRCHR_CUT_OFF)
156
911
                    break;
157
4.42k
                s1 = p - MEMRCHR_CUT_OFF;
158
165k
                while (p > s1) {
159
161k
                    p--;
160
161k
                    if (*p == ch)
161
626
                        return (p - s);
162
161k
                }
163
3.79k
                n = p - s;
164
3.79k
            }
165
285k
            while (n > MEMRCHR_CUT_OFF);
166
285k
        }
167
#endif
168
378k
    }
169
121k
#endif  /* HAVE_MEMRCHR */
170
121k
    p = s + n;
171
641k
    while (p > s) {
172
599k
        p--;
173
599k
        if (*p == ch)
174
79.0k
            return (p - s);
175
599k
    }
176
42.0k
    return -1;
177
121k
}
Unexecuted instantiation: bytesobject.c:stringlib_rfind_char
unicodeobject.c:ucs1lib_rfind_char
Line
Count
Source
117
72.9k
{
118
72.9k
    const STRINGLIB_CHAR *p;
119
72.9k
#ifdef HAVE_MEMRCHR
120
    /* memrchr() is a GNU extension, available since glibc 2.1.91.  it
121
       doesn't seem as optimized as memchr(), but is still quite
122
       faster than our hand-written loop below. There is no wmemrchr
123
       for 4-byte chars. */
124
125
72.9k
    if (n > MEMRCHR_CUT_OFF) {
126
68.7k
#if STRINGLIB_SIZEOF_CHAR == 1
127
68.7k
        p = memrchr(s, ch, n);
128
68.7k
        if (p != NULL)
129
67.7k
            return (p - s);
130
1.08k
        return -1;
131
#else
132
        /* use memrchr if we can choose a needle without too many likely
133
           false positives */
134
        const STRINGLIB_CHAR *s1;
135
        Py_ssize_t n1;
136
        unsigned char needle = ch & 0xff;
137
        /* If looking for a multiple of 256, we'd have too
138
           many false positives looking for the '\0' byte in UCS2
139
           and UCS4 representations. */
140
        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
68.7k
    }
169
4.13k
#endif  /* HAVE_MEMRCHR */
170
4.13k
    p = s + n;
171
14.8k
    while (p > s) {
172
13.6k
        p--;
173
13.6k
        if (*p == ch)
174
2.91k
            return (p - s);
175
13.6k
    }
176
1.21k
    return -1;
177
4.13k
}
unicodeobject.c:ucs2lib_rfind_char
Line
Count
Source
117
222k
{
118
222k
    const STRINGLIB_CHAR *p;
119
222k
#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
222k
    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
208k
        const STRINGLIB_CHAR *s1;
135
208k
        Py_ssize_t n1;
136
208k
        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
208k
        if (needle != 0) {
141
211k
            do {
142
211k
                void *candidate = memrchr(s, needle,
143
211k
                                          n * sizeof(STRINGLIB_CHAR));
144
211k
                if (candidate == NULL)
145
711
                    return -1;
146
210k
                n1 = n;
147
210k
                p = (const STRINGLIB_CHAR *)
148
210k
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
149
210k
                n = p - s;
150
210k
                if (*p == ch)
151
206k
                    return n;
152
                /* False positive */
153
3.76k
                if (n1 - n > MEMRCHR_CUT_OFF)
154
1.73k
                    continue;
155
2.02k
                if (n <= MEMRCHR_CUT_OFF)
156
506
                    break;
157
1.52k
                s1 = p - MEMRCHR_CUT_OFF;
158
55.7k
                while (p > s1) {
159
54.4k
                    p--;
160
54.4k
                    if (*p == ch)
161
263
                        return (p - s);
162
54.4k
                }
163
1.25k
                n = p - s;
164
1.25k
            }
165
208k
            while (n > MEMRCHR_CUT_OFF);
166
208k
        }
167
208k
#endif
168
208k
    }
169
14.7k
#endif  /* HAVE_MEMRCHR */
170
14.7k
    p = s + n;
171
67.1k
    while (p > s) {
172
65.0k
        p--;
173
65.0k
        if (*p == ch)
174
12.6k
            return (p - s);
175
65.0k
    }
176
2.12k
    return -1;
177
14.7k
}
unicodeobject.c:ucs4lib_rfind_char
Line
Count
Source
117
124k
{
118
124k
    const STRINGLIB_CHAR *p;
119
124k
#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
124k
    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
77.0k
        const STRINGLIB_CHAR *s1;
135
77.0k
        Py_ssize_t n1;
136
77.0k
        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
77.0k
        if (needle != 0) {
141
83.3k
            do {
142
83.3k
                void *candidate = memrchr(s, needle,
143
83.3k
                                          n * sizeof(STRINGLIB_CHAR));
144
83.3k
                if (candidate == NULL)
145
507
                    return -1;
146
82.8k
                n1 = n;
147
82.8k
                p = (const STRINGLIB_CHAR *)
148
82.8k
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
149
82.8k
                n = p - s;
150
82.8k
                if (*p == ch)
151
75.3k
                    return n;
152
                /* False positive */
153
7.43k
                if (n1 - n > MEMRCHR_CUT_OFF)
154
4.12k
                    continue;
155
3.30k
                if (n <= MEMRCHR_CUT_OFF)
156
405
                    break;
157
2.90k
                s1 = p - MEMRCHR_CUT_OFF;
158
109k
                while (p > s1) {
159
107k
                    p--;
160
107k
                    if (*p == ch)
161
363
                        return (p - s);
162
107k
                }
163
2.53k
                n = p - s;
164
2.53k
            }
165
77.0k
            while (n > MEMRCHR_CUT_OFF);
166
77.0k
        }
167
77.0k
#endif
168
77.0k
    }
169
48.0k
#endif  /* HAVE_MEMRCHR */
170
48.0k
    p = s + n;
171
261k
    while (p > s) {
172
259k
        p--;
173
259k
        if (*p == ch)
174
46.3k
            return (p - s);
175
259k
    }
176
1.72k
    return -1;
177
48.0k
}
unicodeobject.c:asciilib_rfind_char
Line
Count
Source
117
61.0k
{
118
61.0k
    const STRINGLIB_CHAR *p;
119
61.0k
#ifdef HAVE_MEMRCHR
120
    /* memrchr() is a GNU extension, available since glibc 2.1.91.  it
121
       doesn't seem as optimized as memchr(), but is still quite
122
       faster than our hand-written loop below. There is no wmemrchr
123
       for 4-byte chars. */
124
125
61.0k
    if (n > MEMRCHR_CUT_OFF) {
126
21.5k
#if STRINGLIB_SIZEOF_CHAR == 1
127
21.5k
        p = memrchr(s, ch, n);
128
21.5k
        if (p != NULL)
129
21.3k
            return (p - s);
130
264
        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
21.5k
    }
169
39.5k
#endif  /* HAVE_MEMRCHR */
170
39.5k
    p = s + n;
171
223k
    while (p > s) {
172
195k
        p--;
173
195k
        if (*p == ch)
174
12.3k
            return (p - s);
175
195k
    }
176
27.1k
    return -1;
177
39.5k
}
bytes_methods.c:stringlib_rfind_char
Line
Count
Source
117
17.2k
{
118
17.2k
    const STRINGLIB_CHAR *p;
119
17.2k
#ifdef HAVE_MEMRCHR
120
    /* memrchr() is a GNU extension, available since glibc 2.1.91.  it
121
       doesn't seem as optimized as memchr(), but is still quite
122
       faster than our hand-written loop below. There is no wmemrchr
123
       for 4-byte chars. */
124
125
17.2k
    if (n > MEMRCHR_CUT_OFF) {
126
2.65k
#if STRINGLIB_SIZEOF_CHAR == 1
127
2.65k
        p = memrchr(s, ch, n);
128
2.65k
        if (p != NULL)
129
717
            return (p - s);
130
1.93k
        return -1;
131
#else
132
        /* use memrchr if we can choose a needle without too many likely
133
           false positives */
134
        const STRINGLIB_CHAR *s1;
135
        Py_ssize_t n1;
136
        unsigned char needle = ch & 0xff;
137
        /* If looking for a multiple of 256, we'd have too
138
           many false positives looking for the '\0' byte in UCS2
139
           and UCS4 representations. */
140
        if (needle != 0) {
141
            do {
142
                void *candidate = memrchr(s, needle,
143
                                          n * sizeof(STRINGLIB_CHAR));
144
                if (candidate == NULL)
145
                    return -1;
146
                n1 = n;
147
                p = (const STRINGLIB_CHAR *)
148
                        _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
149
                n = p - s;
150
                if (*p == ch)
151
                    return n;
152
                /* False positive */
153
                if (n1 - n > MEMRCHR_CUT_OFF)
154
                    continue;
155
                if (n <= MEMRCHR_CUT_OFF)
156
                    break;
157
                s1 = p - MEMRCHR_CUT_OFF;
158
                while (p > s1) {
159
                    p--;
160
                    if (*p == ch)
161
                        return (p - s);
162
                }
163
                n = p - s;
164
            }
165
            while (n > MEMRCHR_CUT_OFF);
166
        }
167
#endif
168
2.65k
    }
169
14.6k
#endif  /* HAVE_MEMRCHR */
170
14.6k
    p = s + n;
171
74.7k
    while (p > s) {
172
64.8k
        p--;
173
64.8k
        if (*p == ch)
174
4.75k
            return (p - s);
175
64.8k
    }
176
9.85k
    return -1;
177
14.6k
}
Unexecuted instantiation: bytearrayobject.c:stringlib_rfind_char
178
179
#undef MEMRCHR_CUT_OFF
180
181
/* Change to a 1 to see logging comments walk through the algorithm. */
182
#if 0 && STRINGLIB_SIZEOF_CHAR == 1
183
# define LOG(...) printf(__VA_ARGS__)
184
# define LOG_STRING(s, n) printf("\"%.*s\"", (int)(n), s)
185
# define LOG_LINEUP() do {                                         \
186
    LOG("> "); LOG_STRING(haystack, len_haystack); LOG("\n> ");    \
187
    LOG("%*s",(int)(window_last - haystack + 1 - len_needle), ""); \
188
    LOG_STRING(needle, len_needle); LOG("\n");                     \
189
} while(0)
190
#else
191
# define LOG(...)
192
# define LOG_STRING(s, n)
193
# define LOG_LINEUP()
194
#endif
195
196
Py_LOCAL_INLINE(Py_ssize_t)
197
STRINGLIB(_lex_search)(const STRINGLIB_CHAR *needle, Py_ssize_t len_needle,
198
                       Py_ssize_t *return_period, int invert_alphabet)
199
46
{
200
    /* Do a lexicographic search. Essentially this:
201
           >>> max(needle[i:] for i in range(len(needle)+1))
202
       Also find the period of the right half.   */
203
46
    Py_ssize_t max_suffix = 0;
204
46
    Py_ssize_t candidate = 1;
205
46
    Py_ssize_t k = 0;
206
    // The period of the right half.
207
46
    Py_ssize_t period = 1;
208
209
460
    while (candidate + k < len_needle) {
210
        // each loop increases candidate + k + max_suffix
211
414
        STRINGLIB_CHAR a = needle[candidate + k];
212
414
        STRINGLIB_CHAR b = needle[max_suffix + k];
213
        // check if the suffix at candidate is better than max_suffix
214
414
        if (invert_alphabet ? (b < a) : (a < b)) {
215
            // Fell short of max_suffix.
216
            // The next k + 1 characters are non-increasing
217
            // from candidate, so they won't start a maximal suffix.
218
299
            candidate += k + 1;
219
299
            k = 0;
220
            // We've ruled out any period smaller than what's
221
            // been scanned since max_suffix.
222
299
            period = candidate - max_suffix;
223
299
        }
224
115
        else if (a == b) {
225
23
            if (k + 1 != period) {
226
                // Keep scanning the equal strings
227
0
                k++;
228
0
            }
229
23
            else {
230
                // Matched a whole period.
231
                // Start matching the next period.
232
23
                candidate += period;
233
23
                k = 0;
234
23
            }
235
23
        }
236
92
        else {
237
            // Did better than max_suffix, so replace it.
238
92
            max_suffix = candidate;
239
92
            candidate++;
240
92
            k = 0;
241
92
            period = 1;
242
92
        }
243
414
    }
244
46
    *return_period = period;
245
46
    return max_suffix;
246
46
}
Unexecuted instantiation: bytesobject.c:stringlib__lex_search
Unexecuted instantiation: unicodeobject.c:asciilib__lex_search
unicodeobject.c:ucs1lib__lex_search
Line
Count
Source
199
46
{
200
    /* Do a lexicographic search. Essentially this:
201
           >>> max(needle[i:] for i in range(len(needle)+1))
202
       Also find the period of the right half.   */
203
46
    Py_ssize_t max_suffix = 0;
204
46
    Py_ssize_t candidate = 1;
205
46
    Py_ssize_t k = 0;
206
    // The period of the right half.
207
46
    Py_ssize_t period = 1;
208
209
460
    while (candidate + k < len_needle) {
210
        // each loop increases candidate + k + max_suffix
211
414
        STRINGLIB_CHAR a = needle[candidate + k];
212
414
        STRINGLIB_CHAR b = needle[max_suffix + k];
213
        // check if the suffix at candidate is better than max_suffix
214
414
        if (invert_alphabet ? (b < a) : (a < b)) {
215
            // Fell short of max_suffix.
216
            // The next k + 1 characters are non-increasing
217
            // from candidate, so they won't start a maximal suffix.
218
299
            candidate += k + 1;
219
299
            k = 0;
220
            // We've ruled out any period smaller than what's
221
            // been scanned since max_suffix.
222
299
            period = candidate - max_suffix;
223
299
        }
224
115
        else if (a == b) {
225
23
            if (k + 1 != period) {
226
                // Keep scanning the equal strings
227
0
                k++;
228
0
            }
229
23
            else {
230
                // Matched a whole period.
231
                // Start matching the next period.
232
23
                candidate += period;
233
23
                k = 0;
234
23
            }
235
23
        }
236
92
        else {
237
            // Did better than max_suffix, so replace it.
238
92
            max_suffix = candidate;
239
92
            candidate++;
240
92
            k = 0;
241
92
            period = 1;
242
92
        }
243
414
    }
244
46
    *return_period = period;
245
46
    return max_suffix;
246
46
}
Unexecuted instantiation: unicodeobject.c:ucs2lib__lex_search
Unexecuted instantiation: unicodeobject.c:ucs4lib__lex_search
Unexecuted instantiation: bytes_methods.c:stringlib__lex_search
Unexecuted instantiation: bytearrayobject.c:stringlib__lex_search
247
248
Py_LOCAL_INLINE(Py_ssize_t)
249
STRINGLIB(_factorize)(const STRINGLIB_CHAR *needle,
250
                      Py_ssize_t len_needle,
251
                      Py_ssize_t *return_period)
252
23
{
253
    /* Do a "critical factorization", making it so that:
254
       >>> needle = (left := needle[:cut]) + (right := needle[cut:])
255
       where the "local period" of the cut is maximal.
256
257
       The local period of the cut is the minimal length of a string w
258
       such that (left endswith w or w endswith left)
259
       and (right startswith w or w startswith right).
260
261
       The Critical Factorization Theorem says that this maximal local
262
       period is the global period of the string.
263
264
       Crochemore and Perrin (1991) show that this cut can be computed
265
       as the later of two cuts: one that gives a lexicographically
266
       maximal right half, and one that gives the same with the
267
       with respect to a reversed alphabet-ordering.
268
269
       This is what we want to happen:
270
           >>> x = "GCAGAGAG"
271
           >>> cut, period = factorize(x)
272
           >>> x[:cut], (right := x[cut:])
273
           ('GC', 'AGAGAG')
274
           >>> period  # right half period
275
           2
276
           >>> right[period:] == right[:-period]
277
           True
278
279
       This is how the local period lines up in the above example:
280
                GC | AGAGAG
281
           AGAGAGC = AGAGAGC
282
       The length of this minimal repetition is 7, which is indeed the
283
       period of the original string. */
284
285
23
    Py_ssize_t cut1, period1, cut2, period2, cut, period;
286
23
    cut1 = STRINGLIB(_lex_search)(needle, len_needle, &period1, 0);
287
23
    cut2 = STRINGLIB(_lex_search)(needle, len_needle, &period2, 1);
288
289
    // Take the later cut.
290
23
    if (cut1 > cut2) {
291
23
        period = period1;
292
23
        cut = cut1;
293
23
    }
294
0
    else {
295
0
        period = period2;
296
0
        cut = cut2;
297
0
    }
298
299
23
    LOG("split: "); LOG_STRING(needle, cut);
300
23
    LOG(" + "); LOG_STRING(needle + cut, len_needle - cut);
301
23
    LOG("\n");
302
303
23
    *return_period = period;
304
23
    return cut;
305
23
}
Unexecuted instantiation: bytesobject.c:stringlib__factorize
Unexecuted instantiation: unicodeobject.c:asciilib__factorize
unicodeobject.c:ucs1lib__factorize
Line
Count
Source
252
23
{
253
    /* Do a "critical factorization", making it so that:
254
       >>> needle = (left := needle[:cut]) + (right := needle[cut:])
255
       where the "local period" of the cut is maximal.
256
257
       The local period of the cut is the minimal length of a string w
258
       such that (left endswith w or w endswith left)
259
       and (right startswith w or w startswith right).
260
261
       The Critical Factorization Theorem says that this maximal local
262
       period is the global period of the string.
263
264
       Crochemore and Perrin (1991) show that this cut can be computed
265
       as the later of two cuts: one that gives a lexicographically
266
       maximal right half, and one that gives the same with the
267
       with respect to a reversed alphabet-ordering.
268
269
       This is what we want to happen:
270
           >>> x = "GCAGAGAG"
271
           >>> cut, period = factorize(x)
272
           >>> x[:cut], (right := x[cut:])
273
           ('GC', 'AGAGAG')
274
           >>> period  # right half period
275
           2
276
           >>> right[period:] == right[:-period]
277
           True
278
279
       This is how the local period lines up in the above example:
280
                GC | AGAGAG
281
           AGAGAGC = AGAGAGC
282
       The length of this minimal repetition is 7, which is indeed the
283
       period of the original string. */
284
285
23
    Py_ssize_t cut1, period1, cut2, period2, cut, period;
286
23
    cut1 = STRINGLIB(_lex_search)(needle, len_needle, &period1, 0);
287
23
    cut2 = STRINGLIB(_lex_search)(needle, len_needle, &period2, 1);
288
289
    // Take the later cut.
290
23
    if (cut1 > cut2) {
291
23
        period = period1;
292
23
        cut = cut1;
293
23
    }
294
0
    else {
295
0
        period = period2;
296
0
        cut = cut2;
297
0
    }
298
299
23
    LOG("split: "); LOG_STRING(needle, cut);
300
23
    LOG(" + "); LOG_STRING(needle + cut, len_needle - cut);
301
23
    LOG("\n");
302
303
23
    *return_period = period;
304
23
    return cut;
305
23
}
Unexecuted instantiation: unicodeobject.c:ucs2lib__factorize
Unexecuted instantiation: unicodeobject.c:ucs4lib__factorize
Unexecuted instantiation: bytes_methods.c:stringlib__factorize
Unexecuted instantiation: bytearrayobject.c:stringlib__factorize
306
307
308
253
#define SHIFT_TYPE uint8_t
309
#define MAX_SHIFT UINT8_MAX
310
311
83.7k
#define TABLE_SIZE_BITS 6u
312
83.7k
#define TABLE_SIZE (1U << TABLE_SIZE_BITS)
313
82.2k
#define TABLE_MASK (TABLE_SIZE - 1U)
314
315
typedef struct STRINGLIB(_pre) {
316
    const STRINGLIB_CHAR *needle;
317
    Py_ssize_t len_needle;
318
    Py_ssize_t cut;
319
    Py_ssize_t period;
320
    Py_ssize_t gap;
321
    int is_periodic;
322
    SHIFT_TYPE table[TABLE_SIZE];
323
} STRINGLIB(prework);
324
325
326
static void
327
STRINGLIB(_preprocess)(const STRINGLIB_CHAR *needle, Py_ssize_t len_needle,
328
                       STRINGLIB(prework) *p)
329
23
{
330
23
    p->needle = needle;
331
23
    p->len_needle = len_needle;
332
23
    p->cut = STRINGLIB(_factorize)(needle, len_needle, &(p->period));
333
23
    assert(p->period + p->cut <= len_needle);
334
23
    p->is_periodic = (0 == memcmp(needle,
335
23
                                  needle + p->period,
336
23
                                  p->cut * STRINGLIB_SIZEOF_CHAR));
337
23
    if (p->is_periodic) {
338
0
        assert(p->cut <= len_needle/2);
339
0
        assert(p->cut < p->period);
340
0
    }
341
23
    else {
342
        // A lower bound on the period
343
23
        p->period = Py_MAX(p->cut, len_needle - p->cut) + 1;
344
23
    }
345
    // The gap between the last character and the previous
346
    // occurrence of an equivalent character (modulo TABLE_SIZE)
347
23
    p->gap = len_needle;
348
23
    STRINGLIB_CHAR last = needle[len_needle - 1] & TABLE_MASK;
349
161
    for (Py_ssize_t i = len_needle - 2; i >= 0; i--) {
350
161
        STRINGLIB_CHAR x = needle[i] & TABLE_MASK;
351
161
        if (x == last) {
352
23
            p->gap = len_needle - 1 - i;
353
23
            break;
354
23
        }
355
161
    }
356
    // Fill up a compressed Boyer-Moore "Bad Character" table
357
23
    Py_ssize_t not_found_shift = Py_MIN(len_needle, MAX_SHIFT);
358
1.49k
    for (Py_ssize_t i = 0; i < (Py_ssize_t)TABLE_SIZE; i++) {
359
1.47k
        p->table[i] = Py_SAFE_DOWNCAST(not_found_shift,
360
1.47k
                                       Py_ssize_t, SHIFT_TYPE);
361
1.47k
    }
362
253
    for (Py_ssize_t i = len_needle - not_found_shift; i < len_needle; i++) {
363
230
        SHIFT_TYPE shift = Py_SAFE_DOWNCAST(len_needle - 1 - i,
364
230
                                            Py_ssize_t, SHIFT_TYPE);
365
230
        p->table[needle[i] & TABLE_MASK] = shift;
366
230
    }
367
23
}
Unexecuted instantiation: bytesobject.c:stringlib__preprocess
Unexecuted instantiation: unicodeobject.c:asciilib__preprocess
unicodeobject.c:ucs1lib__preprocess
Line
Count
Source
329
23
{
330
23
    p->needle = needle;
331
23
    p->len_needle = len_needle;
332
23
    p->cut = STRINGLIB(_factorize)(needle, len_needle, &(p->period));
333
23
    assert(p->period + p->cut <= len_needle);
334
23
    p->is_periodic = (0 == memcmp(needle,
335
23
                                  needle + p->period,
336
23
                                  p->cut * STRINGLIB_SIZEOF_CHAR));
337
23
    if (p->is_periodic) {
338
0
        assert(p->cut <= len_needle/2);
339
0
        assert(p->cut < p->period);
340
0
    }
341
23
    else {
342
        // A lower bound on the period
343
23
        p->period = Py_MAX(p->cut, len_needle - p->cut) + 1;
344
23
    }
345
    // The gap between the last character and the previous
346
    // occurrence of an equivalent character (modulo TABLE_SIZE)
347
23
    p->gap = len_needle;
348
23
    STRINGLIB_CHAR last = needle[len_needle - 1] & TABLE_MASK;
349
161
    for (Py_ssize_t i = len_needle - 2; i >= 0; i--) {
350
161
        STRINGLIB_CHAR x = needle[i] & TABLE_MASK;
351
161
        if (x == last) {
352
23
            p->gap = len_needle - 1 - i;
353
23
            break;
354
23
        }
355
161
    }
356
    // Fill up a compressed Boyer-Moore "Bad Character" table
357
23
    Py_ssize_t not_found_shift = Py_MIN(len_needle, MAX_SHIFT);
358
1.49k
    for (Py_ssize_t i = 0; i < (Py_ssize_t)TABLE_SIZE; i++) {
359
1.47k
        p->table[i] = Py_SAFE_DOWNCAST(not_found_shift,
360
1.47k
                                       Py_ssize_t, SHIFT_TYPE);
361
1.47k
    }
362
253
    for (Py_ssize_t i = len_needle - not_found_shift; i < len_needle; i++) {
363
230
        SHIFT_TYPE shift = Py_SAFE_DOWNCAST(len_needle - 1 - i,
364
230
                                            Py_ssize_t, SHIFT_TYPE);
365
230
        p->table[needle[i] & TABLE_MASK] = shift;
366
230
    }
367
23
}
Unexecuted instantiation: unicodeobject.c:ucs2lib__preprocess
Unexecuted instantiation: unicodeobject.c:ucs4lib__preprocess
Unexecuted instantiation: bytes_methods.c:stringlib__preprocess
Unexecuted instantiation: bytearrayobject.c:stringlib__preprocess
368
369
static Py_ssize_t
370
STRINGLIB(_two_way)(const STRINGLIB_CHAR *haystack, Py_ssize_t len_haystack,
371
                    STRINGLIB(prework) *p)
372
23
{
373
    // Crochemore and Perrin's (1991) Two-Way algorithm.
374
    // See http://www-igm.univ-mlv.fr/~lecroq/string/node26.html#SECTION00260
375
23
    const Py_ssize_t len_needle = p->len_needle;
376
23
    const Py_ssize_t cut = p->cut;
377
23
    Py_ssize_t period = p->period;
378
23
    const STRINGLIB_CHAR *const needle = p->needle;
379
23
    const STRINGLIB_CHAR *window_last = haystack + len_needle - 1;
380
23
    const STRINGLIB_CHAR *const haystack_end = haystack + len_haystack;
381
23
    SHIFT_TYPE *table = p->table;
382
23
    const STRINGLIB_CHAR *window;
383
23
    LOG("===== Two-way: \"%s\" in \"%s\". =====\n", needle, haystack);
384
385
23
    Py_ssize_t gap = p->gap;
386
23
    Py_ssize_t gap_jump_end = Py_MIN(len_needle, cut + gap);
387
23
    if (p->is_periodic) {
388
0
        LOG("Needle is periodic.\n");
389
0
        Py_ssize_t memory = 0;
390
0
      periodicwindowloop:
391
0
        while (window_last < haystack_end) {
392
0
            assert(memory == 0);
393
0
            for (;;) {
394
0
                LOG_LINEUP();
395
0
                Py_ssize_t shift = table[(*window_last) & TABLE_MASK];
396
0
                window_last += shift;
397
0
                if (shift == 0) {
398
0
                    break;
399
0
                }
400
0
                if (window_last >= haystack_end) {
401
0
                    return -1;
402
0
                }
403
0
                LOG("Horspool skip\n");
404
0
            }
405
0
          no_shift:
406
0
            window = window_last - len_needle + 1;
407
0
            assert((window[len_needle - 1] & TABLE_MASK) ==
408
0
                   (needle[len_needle - 1] & TABLE_MASK));
409
0
            Py_ssize_t i = Py_MAX(cut, memory);
410
0
            for (; i < len_needle; i++) {
411
0
                if (needle[i] != window[i]) {
412
0
                    if (i < gap_jump_end) {
413
0
                        LOG("Early right half mismatch: jump by gap.\n");
414
0
                        assert(gap >= i - cut + 1);
415
0
                        window_last += gap;
416
0
                    }
417
0
                    else {
418
0
                        LOG("Late right half mismatch: jump by n (>gap)\n");
419
0
                        assert(i - cut + 1 > gap);
420
0
                        window_last += i - cut + 1;
421
0
                    }
422
0
                    memory = 0;
423
0
                    goto periodicwindowloop;
424
0
                }
425
0
            }
426
0
            for (i = memory; i < cut; i++) {
427
0
                if (needle[i] != window[i]) {
428
0
                    LOG("Left half does not match.\n");
429
0
                    window_last += period;
430
0
                    memory = len_needle - period;
431
0
                    if (window_last >= haystack_end) {
432
0
                        return -1;
433
0
                    }
434
0
                    Py_ssize_t shift = table[(*window_last) & TABLE_MASK];
435
0
                    if (shift) {
436
                        // A mismatch has been identified to the right
437
                        // of where i will next start, so we can jump
438
                        // at least as far as if the mismatch occurred
439
                        // on the first comparison.
440
0
                        Py_ssize_t mem_jump = Py_MAX(cut, memory) - cut + 1;
441
0
                        LOG("Skip with Memory.\n");
442
0
                        memory = 0;
443
0
                        window_last += Py_MAX(shift, mem_jump);
444
0
                        goto periodicwindowloop;
445
0
                    }
446
0
                    goto no_shift;
447
0
                }
448
0
            }
449
0
            LOG("Found a match!\n");
450
0
            return window - haystack;
451
0
        }
452
0
    }
453
23
    else {
454
23
        period = Py_MAX(gap, period);
455
23
        LOG("Needle is not periodic.\n");
456
15.7k
      windowloop:
457
15.7k
        while (window_last < haystack_end) {
458
81.8k
            for (;;) {
459
81.8k
                LOG_LINEUP();
460
81.8k
                Py_ssize_t shift = table[(*window_last) & TABLE_MASK];
461
81.8k
                window_last += shift;
462
81.8k
                if (shift == 0) {
463
15.7k
                    break;
464
15.7k
                }
465
66.1k
                if (window_last >= haystack_end) {
466
19
                    return -1;
467
19
                }
468
66.1k
                LOG("Horspool skip\n");
469
66.1k
            }
470
15.7k
            window = window_last - len_needle + 1;
471
15.7k
            assert((window[len_needle - 1] & TABLE_MASK) ==
472
15.7k
                   (needle[len_needle - 1] & TABLE_MASK));
473
15.7k
            Py_ssize_t i = cut;
474
15.9k
            for (; i < len_needle; i++) {
475
15.8k
                if (needle[i] != window[i]) {
476
15.6k
                    if (i < gap_jump_end) {
477
15.6k
                        LOG("Early right half mismatch: jump by gap.\n");
478
15.6k
                        assert(gap >= i - cut + 1);
479
15.6k
                        window_last += gap;
480
15.6k
                    }
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
15.6k
                    goto windowloop;
487
15.6k
                }
488
15.8k
            }
489
201
            for (Py_ssize_t i = 0; i < cut; i++) {
490
199
                if (needle[i] != window[i]) {
491
103
                    LOG("Left half does not match.\n");
492
103
                    window_last += period;
493
103
                    goto windowloop;
494
103
                }
495
199
            }
496
2
            LOG("Found a match!\n");
497
2
            return window - haystack;
498
105
        }
499
15.7k
    }
500
2
    LOG("Not found. Returning -1.\n");
501
2
    return -1;
502
23
}
Unexecuted instantiation: bytesobject.c:stringlib__two_way
Unexecuted instantiation: unicodeobject.c:asciilib__two_way
unicodeobject.c:ucs1lib__two_way
Line
Count
Source
372
23
{
373
    // Crochemore and Perrin's (1991) Two-Way algorithm.
374
    // See http://www-igm.univ-mlv.fr/~lecroq/string/node26.html#SECTION00260
375
23
    const Py_ssize_t len_needle = p->len_needle;
376
23
    const Py_ssize_t cut = p->cut;
377
23
    Py_ssize_t period = p->period;
378
23
    const STRINGLIB_CHAR *const needle = p->needle;
379
23
    const STRINGLIB_CHAR *window_last = haystack + len_needle - 1;
380
23
    const STRINGLIB_CHAR *const haystack_end = haystack + len_haystack;
381
23
    SHIFT_TYPE *table = p->table;
382
23
    const STRINGLIB_CHAR *window;
383
23
    LOG("===== Two-way: \"%s\" in \"%s\". =====\n", needle, haystack);
384
385
23
    Py_ssize_t gap = p->gap;
386
23
    Py_ssize_t gap_jump_end = Py_MIN(len_needle, cut + gap);
387
23
    if (p->is_periodic) {
388
0
        LOG("Needle is periodic.\n");
389
0
        Py_ssize_t memory = 0;
390
0
      periodicwindowloop:
391
0
        while (window_last < haystack_end) {
392
0
            assert(memory == 0);
393
0
            for (;;) {
394
0
                LOG_LINEUP();
395
0
                Py_ssize_t shift = table[(*window_last) & TABLE_MASK];
396
0
                window_last += shift;
397
0
                if (shift == 0) {
398
0
                    break;
399
0
                }
400
0
                if (window_last >= haystack_end) {
401
0
                    return -1;
402
0
                }
403
0
                LOG("Horspool skip\n");
404
0
            }
405
0
          no_shift:
406
0
            window = window_last - len_needle + 1;
407
0
            assert((window[len_needle - 1] & TABLE_MASK) ==
408
0
                   (needle[len_needle - 1] & TABLE_MASK));
409
0
            Py_ssize_t i = Py_MAX(cut, memory);
410
0
            for (; i < len_needle; i++) {
411
0
                if (needle[i] != window[i]) {
412
0
                    if (i < gap_jump_end) {
413
0
                        LOG("Early right half mismatch: jump by gap.\n");
414
0
                        assert(gap >= i - cut + 1);
415
0
                        window_last += gap;
416
0
                    }
417
0
                    else {
418
0
                        LOG("Late right half mismatch: jump by n (>gap)\n");
419
0
                        assert(i - cut + 1 > gap);
420
0
                        window_last += i - cut + 1;
421
0
                    }
422
0
                    memory = 0;
423
0
                    goto periodicwindowloop;
424
0
                }
425
0
            }
426
0
            for (i = memory; i < cut; i++) {
427
0
                if (needle[i] != window[i]) {
428
0
                    LOG("Left half does not match.\n");
429
0
                    window_last += period;
430
0
                    memory = len_needle - period;
431
0
                    if (window_last >= haystack_end) {
432
0
                        return -1;
433
0
                    }
434
0
                    Py_ssize_t shift = table[(*window_last) & TABLE_MASK];
435
0
                    if (shift) {
436
                        // A mismatch has been identified to the right
437
                        // of where i will next start, so we can jump
438
                        // at least as far as if the mismatch occurred
439
                        // on the first comparison.
440
0
                        Py_ssize_t mem_jump = Py_MAX(cut, memory) - cut + 1;
441
0
                        LOG("Skip with Memory.\n");
442
0
                        memory = 0;
443
0
                        window_last += Py_MAX(shift, mem_jump);
444
0
                        goto periodicwindowloop;
445
0
                    }
446
0
                    goto no_shift;
447
0
                }
448
0
            }
449
0
            LOG("Found a match!\n");
450
0
            return window - haystack;
451
0
        }
452
0
    }
453
23
    else {
454
23
        period = Py_MAX(gap, period);
455
23
        LOG("Needle is not periodic.\n");
456
15.7k
      windowloop:
457
15.7k
        while (window_last < haystack_end) {
458
81.8k
            for (;;) {
459
81.8k
                LOG_LINEUP();
460
81.8k
                Py_ssize_t shift = table[(*window_last) & TABLE_MASK];
461
81.8k
                window_last += shift;
462
81.8k
                if (shift == 0) {
463
15.7k
                    break;
464
15.7k
                }
465
66.1k
                if (window_last >= haystack_end) {
466
19
                    return -1;
467
19
                }
468
66.1k
                LOG("Horspool skip\n");
469
66.1k
            }
470
15.7k
            window = window_last - len_needle + 1;
471
15.7k
            assert((window[len_needle - 1] & TABLE_MASK) ==
472
15.7k
                   (needle[len_needle - 1] & TABLE_MASK));
473
15.7k
            Py_ssize_t i = cut;
474
15.9k
            for (; i < len_needle; i++) {
475
15.8k
                if (needle[i] != window[i]) {
476
15.6k
                    if (i < gap_jump_end) {
477
15.6k
                        LOG("Early right half mismatch: jump by gap.\n");
478
15.6k
                        assert(gap >= i - cut + 1);
479
15.6k
                        window_last += gap;
480
15.6k
                    }
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
15.6k
                    goto windowloop;
487
15.6k
                }
488
15.8k
            }
489
201
            for (Py_ssize_t i = 0; i < cut; i++) {
490
199
                if (needle[i] != window[i]) {
491
103
                    LOG("Left half does not match.\n");
492
103
                    window_last += period;
493
103
                    goto windowloop;
494
103
                }
495
199
            }
496
2
            LOG("Found a match!\n");
497
2
            return window - haystack;
498
105
        }
499
15.7k
    }
500
2
    LOG("Not found. Returning -1.\n");
501
2
    return -1;
502
23
}
Unexecuted instantiation: unicodeobject.c:ucs2lib__two_way
Unexecuted instantiation: unicodeobject.c:ucs4lib__two_way
Unexecuted instantiation: bytes_methods.c:stringlib__two_way
Unexecuted instantiation: bytearrayobject.c:stringlib__two_way
503
504
505
static Py_ssize_t
506
STRINGLIB(_two_way_find)(const STRINGLIB_CHAR *haystack,
507
                         Py_ssize_t len_haystack,
508
                         const STRINGLIB_CHAR *needle,
509
                         Py_ssize_t len_needle)
510
23
{
511
23
    LOG("###### Finding \"%s\" in \"%s\".\n", needle, haystack);
512
23
    STRINGLIB(prework) p;
513
23
    STRINGLIB(_preprocess)(needle, len_needle, &p);
514
23
    return STRINGLIB(_two_way)(haystack, len_haystack, &p);
515
23
}
Unexecuted instantiation: bytesobject.c:stringlib__two_way_find
Unexecuted instantiation: unicodeobject.c:asciilib__two_way_find
unicodeobject.c:ucs1lib__two_way_find
Line
Count
Source
510
23
{
511
23
    LOG("###### Finding \"%s\" in \"%s\".\n", needle, haystack);
512
23
    STRINGLIB(prework) p;
513
23
    STRINGLIB(_preprocess)(needle, len_needle, &p);
514
23
    return STRINGLIB(_two_way)(haystack, len_haystack, &p);
515
23
}
Unexecuted instantiation: unicodeobject.c:ucs2lib__two_way_find
Unexecuted instantiation: unicodeobject.c:ucs4lib__two_way_find
Unexecuted instantiation: bytes_methods.c:stringlib__two_way_find
Unexecuted instantiation: bytearrayobject.c:stringlib__two_way_find
516
517
518
static Py_ssize_t
519
STRINGLIB(_two_way_count)(const STRINGLIB_CHAR *haystack,
520
                          Py_ssize_t len_haystack,
521
                          const STRINGLIB_CHAR *needle,
522
                          Py_ssize_t len_needle,
523
                          Py_ssize_t maxcount)
524
0
{
525
0
    LOG("###### Counting \"%s\" in \"%s\".\n", needle, haystack);
526
0
    STRINGLIB(prework) p;
527
0
    STRINGLIB(_preprocess)(needle, len_needle, &p);
528
0
    Py_ssize_t index = 0, count = 0;
529
0
    while (1) {
530
0
        Py_ssize_t result;
531
0
        result = STRINGLIB(_two_way)(haystack + index,
532
0
                                     len_haystack - index, &p);
533
0
        if (result == -1) {
534
0
            return count;
535
0
        }
536
0
        count++;
537
0
        if (count == maxcount) {
538
0
            return maxcount;
539
0
        }
540
0
        index += result + len_needle;
541
0
    }
542
0
    return count;
543
0
}
Unexecuted instantiation: bytesobject.c:stringlib__two_way_count
Unexecuted instantiation: unicodeobject.c:asciilib__two_way_count
Unexecuted instantiation: unicodeobject.c:ucs1lib__two_way_count
Unexecuted instantiation: unicodeobject.c:ucs2lib__two_way_count
Unexecuted instantiation: unicodeobject.c:ucs4lib__two_way_count
Unexecuted instantiation: bytes_methods.c:stringlib__two_way_count
Unexecuted instantiation: bytearrayobject.c:stringlib__two_way_count
544
545
#undef SHIFT_TYPE
546
#undef NOT_FOUND
547
#undef SHIFT_OVERFLOW
548
#undef TABLE_SIZE_BITS
549
#undef TABLE_SIZE
550
#undef TABLE_MASK
551
552
#undef LOG
553
#undef LOG_STRING
554
#undef LOG_LINEUP
555
556
static inline Py_ssize_t
557
STRINGLIB(default_find)(const STRINGLIB_CHAR* s, Py_ssize_t n,
558
                        const STRINGLIB_CHAR* p, Py_ssize_t m,
559
                        Py_ssize_t maxcount, int mode)
560
34.4M
{
561
34.4M
    const Py_ssize_t w = n - m;
562
34.4M
    Py_ssize_t mlast = m - 1, count = 0;
563
34.4M
    Py_ssize_t gap = mlast;
564
34.4M
    const STRINGLIB_CHAR last = p[mlast];
565
34.4M
    const STRINGLIB_CHAR *const ss = &s[mlast];
566
567
34.4M
    unsigned long mask = 0;
568
156M
    for (Py_ssize_t i = 0; i < mlast; i++) {
569
121M
        STRINGLIB_BLOOM_ADD(mask, p[i]);
570
121M
        if (p[i] == last) {
571
1.18M
            gap = mlast - i - 1;
572
1.18M
        }
573
121M
    }
574
34.4M
    STRINGLIB_BLOOM_ADD(mask, last);
575
576
3.43G
    for (Py_ssize_t i = 0; i <= w; i++) {
577
3.41G
        if (ss[i] == last) {
578
            /* candidate match */
579
53.9M
            Py_ssize_t j;
580
85.5M
            for (j = 0; j < mlast; j++) {
581
54.8M
                if (s[i+j] != p[j]) {
582
23.2M
                    break;
583
23.2M
                }
584
54.8M
            }
585
53.9M
            if (j == mlast) {
586
                /* got a match! */
587
30.6M
                if (mode != FAST_COUNT) {
588
15.7M
                    return i;
589
15.7M
                }
590
14.9M
                count++;
591
14.9M
                if (count == maxcount) {
592
0
                    return maxcount;
593
0
                }
594
14.9M
                i = i + mlast;
595
14.9M
                continue;
596
14.9M
            }
597
            /* miss: check if next character is part of pattern */
598
23.2M
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
599
5.44M
                i = i + m;
600
5.44M
            }
601
17.8M
            else {
602
17.8M
                i = i + gap;
603
17.8M
            }
604
23.2M
        }
605
3.36G
        else {
606
            /* skip: check if next character is part of pattern */
607
3.36G
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
608
3.26G
                i = i + m;
609
3.26G
            }
610
3.36G
        }
611
3.41G
    }
612
18.7M
    return mode == FAST_COUNT ? count : -1;
613
34.4M
}
Unexecuted instantiation: bytesobject.c:stringlib_default_find
unicodeobject.c:asciilib_default_find
Line
Count
Source
560
2.41M
{
561
2.41M
    const Py_ssize_t w = n - m;
562
2.41M
    Py_ssize_t mlast = m - 1, count = 0;
563
2.41M
    Py_ssize_t gap = mlast;
564
2.41M
    const STRINGLIB_CHAR last = p[mlast];
565
2.41M
    const STRINGLIB_CHAR *const ss = &s[mlast];
566
567
2.41M
    unsigned long mask = 0;
568
5.11M
    for (Py_ssize_t i = 0; i < mlast; i++) {
569
2.70M
        STRINGLIB_BLOOM_ADD(mask, p[i]);
570
2.70M
        if (p[i] == last) {
571
21.0k
            gap = mlast - i - 1;
572
21.0k
        }
573
2.70M
    }
574
2.41M
    STRINGLIB_BLOOM_ADD(mask, last);
575
576
125M
    for (Py_ssize_t i = 0; i <= w; i++) {
577
125M
        if (ss[i] == last) {
578
            /* candidate match */
579
4.90M
            Py_ssize_t j;
580
7.51M
            for (j = 0; j < mlast; j++) {
581
5.14M
                if (s[i+j] != p[j]) {
582
2.53M
                    break;
583
2.53M
                }
584
5.14M
            }
585
4.90M
            if (j == mlast) {
586
                /* got a match! */
587
2.37M
                if (mode != FAST_COUNT) {
588
2.37M
                    return i;
589
2.37M
                }
590
0
                count++;
591
0
                if (count == maxcount) {
592
0
                    return maxcount;
593
0
                }
594
0
                i = i + mlast;
595
0
                continue;
596
0
            }
597
            /* miss: check if next character is part of pattern */
598
2.53M
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
599
145k
                i = i + m;
600
145k
            }
601
2.38M
            else {
602
2.38M
                i = i + gap;
603
2.38M
            }
604
2.53M
        }
605
120M
        else {
606
            /* skip: check if next character is part of pattern */
607
120M
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
608
115M
                i = i + m;
609
115M
            }
610
120M
        }
611
125M
    }
612
45.5k
    return mode == FAST_COUNT ? count : -1;
613
2.41M
}
unicodeobject.c:ucs1lib_default_find
Line
Count
Source
560
20.4M
{
561
20.4M
    const Py_ssize_t w = n - m;
562
20.4M
    Py_ssize_t mlast = m - 1, count = 0;
563
20.4M
    Py_ssize_t gap = mlast;
564
20.4M
    const STRINGLIB_CHAR last = p[mlast];
565
20.4M
    const STRINGLIB_CHAR *const ss = &s[mlast];
566
567
20.4M
    unsigned long mask = 0;
568
127M
    for (Py_ssize_t i = 0; i < mlast; i++) {
569
107M
        STRINGLIB_BLOOM_ADD(mask, p[i]);
570
107M
        if (p[i] == last) {
571
1.10M
            gap = mlast - i - 1;
572
1.10M
        }
573
107M
    }
574
20.4M
    STRINGLIB_BLOOM_ADD(mask, last);
575
576
585M
    for (Py_ssize_t i = 0; i <= w; i++) {
577
567M
        if (ss[i] == last) {
578
            /* candidate match */
579
14.5M
            Py_ssize_t j;
580
20.6M
            for (j = 0; j < mlast; j++) {
581
14.9M
                if (s[i+j] != p[j]) {
582
8.82M
                    break;
583
8.82M
                }
584
14.9M
            }
585
14.5M
            if (j == mlast) {
586
                /* got a match! */
587
5.74M
                if (mode != FAST_COUNT) {
588
1.87M
                    return i;
589
1.87M
                }
590
3.86M
                count++;
591
3.86M
                if (count == maxcount) {
592
0
                    return maxcount;
593
0
                }
594
3.86M
                i = i + mlast;
595
3.86M
                continue;
596
3.86M
            }
597
            /* miss: check if next character is part of pattern */
598
8.82M
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
599
2.03M
                i = i + m;
600
2.03M
            }
601
6.79M
            else {
602
6.79M
                i = i + gap;
603
6.79M
            }
604
8.82M
        }
605
552M
        else {
606
            /* skip: check if next character is part of pattern */
607
552M
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
608
480M
                i = i + m;
609
480M
            }
610
552M
        }
611
567M
    }
612
18.5M
    return mode == FAST_COUNT ? count : -1;
613
20.4M
}
unicodeobject.c:ucs2lib_default_find
Line
Count
Source
560
4.71M
{
561
4.71M
    const Py_ssize_t w = n - m;
562
4.71M
    Py_ssize_t mlast = m - 1, count = 0;
563
4.71M
    Py_ssize_t gap = mlast;
564
4.71M
    const STRINGLIB_CHAR last = p[mlast];
565
4.71M
    const STRINGLIB_CHAR *const ss = &s[mlast];
566
567
4.71M
    unsigned long mask = 0;
568
9.59M
    for (Py_ssize_t i = 0; i < mlast; i++) {
569
4.88M
        STRINGLIB_BLOOM_ADD(mask, p[i]);
570
4.88M
        if (p[i] == last) {
571
39.2k
            gap = mlast - i - 1;
572
39.2k
        }
573
4.88M
    }
574
4.71M
    STRINGLIB_BLOOM_ADD(mask, last);
575
576
1.04G
    for (Py_ssize_t i = 0; i <= w; i++) {
577
1.04G
        if (ss[i] == last) {
578
            /* candidate match */
579
14.4M
            Py_ssize_t j;
580
23.6M
            for (j = 0; j < mlast; j++) {
581
14.6M
                if (s[i+j] != p[j]) {
582
5.45M
                    break;
583
5.45M
                }
584
14.6M
            }
585
14.4M
            if (j == mlast) {
586
                /* got a match! */
587
9.03M
                if (mode != FAST_COUNT) {
588
4.62M
                    return i;
589
4.62M
                }
590
4.41M
                count++;
591
4.41M
                if (count == maxcount) {
592
0
                    return maxcount;
593
0
                }
594
4.41M
                i = i + mlast;
595
4.41M
                continue;
596
4.41M
            }
597
            /* miss: check if next character is part of pattern */
598
5.45M
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
599
1.03M
                i = i + m;
600
1.03M
            }
601
4.41M
            else {
602
4.41M
                i = i + gap;
603
4.41M
            }
604
5.45M
        }
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.04G
    }
612
93.1k
    return mode == FAST_COUNT ? count : -1;
613
4.71M
}
unicodeobject.c:ucs4lib_default_find
Line
Count
Source
560
6.87M
{
561
6.87M
    const Py_ssize_t w = n - m;
562
6.87M
    Py_ssize_t mlast = m - 1, count = 0;
563
6.87M
    Py_ssize_t gap = mlast;
564
6.87M
    const STRINGLIB_CHAR last = p[mlast];
565
6.87M
    const STRINGLIB_CHAR *const ss = &s[mlast];
566
567
6.87M
    unsigned long mask = 0;
568
13.9M
    for (Py_ssize_t i = 0; i < mlast; i++) {
569
7.04M
        STRINGLIB_BLOOM_ADD(mask, p[i]);
570
7.04M
        if (p[i] == last) {
571
25.3k
            gap = mlast - i - 1;
572
25.3k
        }
573
7.04M
    }
574
6.87M
    STRINGLIB_BLOOM_ADD(mask, last);
575
576
1.68G
    for (Py_ssize_t i = 0; i <= w; i++) {
577
1.68G
        if (ss[i] == last) {
578
            /* candidate match */
579
19.9M
            Py_ssize_t j;
580
33.6M
            for (j = 0; j < mlast; j++) {
581
20.1M
                if (s[i+j] != p[j]) {
582
6.45M
                    break;
583
6.45M
                }
584
20.1M
            }
585
19.9M
            if (j == mlast) {
586
                /* got a match! */
587
13.5M
                if (mode != FAST_COUNT) {
588
6.85M
                    return i;
589
6.85M
                }
590
6.67M
                count++;
591
6.67M
                if (count == maxcount) {
592
0
                    return maxcount;
593
0
                }
594
6.67M
                i = i + mlast;
595
6.67M
                continue;
596
6.67M
            }
597
            /* miss: check if next character is part of pattern */
598
6.45M
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
599
2.22M
                i = i + m;
600
2.22M
            }
601
4.22M
            else {
602
4.22M
                i = i + gap;
603
4.22M
            }
604
6.45M
        }
605
1.66G
        else {
606
            /* skip: check if next character is part of pattern */
607
1.66G
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
608
1.64G
                i = i + m;
609
1.64G
            }
610
1.66G
        }
611
1.68G
    }
612
27.8k
    return mode == FAST_COUNT ? count : -1;
613
6.87M
}
bytes_methods.c:stringlib_default_find
Line
Count
Source
560
2.57k
{
561
2.57k
    const Py_ssize_t w = n - m;
562
2.57k
    Py_ssize_t mlast = m - 1, count = 0;
563
2.57k
    Py_ssize_t gap = mlast;
564
2.57k
    const STRINGLIB_CHAR last = p[mlast];
565
2.57k
    const STRINGLIB_CHAR *const ss = &s[mlast];
566
567
2.57k
    unsigned long mask = 0;
568
10.3k
    for (Py_ssize_t i = 0; i < mlast; i++) {
569
7.73k
        STRINGLIB_BLOOM_ADD(mask, p[i]);
570
7.73k
        if (p[i] == last) {
571
2.57k
            gap = mlast - i - 1;
572
2.57k
        }
573
7.73k
    }
574
2.57k
    STRINGLIB_BLOOM_ADD(mask, last);
575
576
977k
    for (Py_ssize_t i = 0; i <= w; i++) {
577
976k
        if (ss[i] == last) {
578
            /* candidate match */
579
6.88k
            Py_ssize_t j;
580
13.9k
            for (j = 0; j < mlast; j++) {
581
11.6k
                if (s[i+j] != p[j]) {
582
4.59k
                    break;
583
4.59k
                }
584
11.6k
            }
585
6.88k
            if (j == mlast) {
586
                /* got a match! */
587
2.29k
                if (mode != FAST_COUNT) {
588
2.29k
                    return i;
589
2.29k
                }
590
0
                count++;
591
0
                if (count == maxcount) {
592
0
                    return maxcount;
593
0
                }
594
0
                i = i + mlast;
595
0
                continue;
596
0
            }
597
            /* miss: check if next character is part of pattern */
598
4.59k
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
599
808
                i = i + m;
600
808
            }
601
3.78k
            else {
602
3.78k
                i = i + gap;
603
3.78k
            }
604
4.59k
        }
605
969k
        else {
606
            /* skip: check if next character is part of pattern */
607
969k
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
608
53.5k
                i = i + m;
609
53.5k
            }
610
969k
        }
611
976k
    }
612
280
    return mode == FAST_COUNT ? count : -1;
613
2.57k
}
Unexecuted instantiation: bytearrayobject.c:stringlib_default_find
614
615
616
static Py_ssize_t
617
STRINGLIB(adaptive_find)(const STRINGLIB_CHAR* s, Py_ssize_t n,
618
                         const STRINGLIB_CHAR* p, Py_ssize_t m,
619
                         Py_ssize_t maxcount, int mode)
620
0
{
621
0
    const Py_ssize_t w = n - m;
622
0
    Py_ssize_t mlast = m - 1, count = 0;
623
0
    Py_ssize_t gap = mlast;
624
0
    Py_ssize_t hits = 0, res;
625
0
    const STRINGLIB_CHAR last = p[mlast];
626
0
    const STRINGLIB_CHAR *const ss = &s[mlast];
627
628
0
    unsigned long mask = 0;
629
0
    for (Py_ssize_t i = 0; i < mlast; i++) {
630
0
        STRINGLIB_BLOOM_ADD(mask, p[i]);
631
0
        if (p[i] == last) {
632
0
            gap = mlast - i - 1;
633
0
        }
634
0
    }
635
0
    STRINGLIB_BLOOM_ADD(mask, last);
636
637
0
    for (Py_ssize_t i = 0; i <= w; i++) {
638
0
        if (ss[i] == last) {
639
            /* candidate match */
640
0
            Py_ssize_t j;
641
0
            for (j = 0; j < mlast; j++) {
642
0
                if (s[i+j] != p[j]) {
643
0
                    break;
644
0
                }
645
0
            }
646
0
            if (j == mlast) {
647
                /* got a match! */
648
0
                if (mode != FAST_COUNT) {
649
0
                    return i;
650
0
                }
651
0
                count++;
652
0
                if (count == maxcount) {
653
0
                    return maxcount;
654
0
                }
655
0
                i = i + mlast;
656
0
                continue;
657
0
            }
658
0
            hits += j + 1;
659
0
            if (hits > m / 4 && w - i > 2000) {
660
0
                if (mode == FAST_SEARCH) {
661
0
                    res = STRINGLIB(_two_way_find)(s + i, n - i, p, m);
662
0
                    return res == -1 ? -1 : res + i;
663
0
                }
664
0
                else {
665
0
                    res = STRINGLIB(_two_way_count)(s + i, n - i, p, m,
666
0
                                                    maxcount - count);
667
0
                    return res + count;
668
0
                }
669
0
            }
670
            /* miss: check if next character is part of pattern */
671
0
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
672
0
                i = i + m;
673
0
            }
674
0
            else {
675
0
                i = i + gap;
676
0
            }
677
0
        }
678
0
        else {
679
            /* skip: check if next character is part of pattern */
680
0
            if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
681
0
                i = i + m;
682
0
            }
683
0
        }
684
0
    }
685
0
    return mode == FAST_COUNT ? count : -1;
686
0
}
Unexecuted instantiation: bytesobject.c:stringlib_adaptive_find
Unexecuted instantiation: unicodeobject.c:asciilib_adaptive_find
Unexecuted instantiation: unicodeobject.c:ucs1lib_adaptive_find
Unexecuted instantiation: unicodeobject.c:ucs2lib_adaptive_find
Unexecuted instantiation: unicodeobject.c:ucs4lib_adaptive_find
Unexecuted instantiation: bytes_methods.c:stringlib_adaptive_find
Unexecuted instantiation: bytearrayobject.c:stringlib_adaptive_find
687
688
689
static Py_ssize_t
690
STRINGLIB(default_rfind)(const STRINGLIB_CHAR* s, Py_ssize_t n,
691
                         const STRINGLIB_CHAR* p, Py_ssize_t m,
692
                         Py_ssize_t maxcount, int mode)
693
7.02k
{
694
    /* create compressed boyer-moore delta 1 table */
695
7.02k
    unsigned long mask = 0;
696
7.02k
    Py_ssize_t i, j, mlast = m - 1, skip = m - 1, w = n - m;
697
698
    /* process pattern[0] outside the loop */
699
7.02k
    STRINGLIB_BLOOM_ADD(mask, p[0]);
700
    /* process pattern[:0:-1] */
701
28.1k
    for (i = mlast; i > 0; i--) {
702
21.0k
        STRINGLIB_BLOOM_ADD(mask, p[i]);
703
21.0k
        if (p[i] == p[0]) {
704
0
            skip = i - 1;
705
0
        }
706
21.0k
    }
707
708
1.29M
    for (i = w; i >= 0; i--) {
709
1.29M
        if (s[i] == p[0]) {
710
            /* candidate match */
711
76.9k
            for (j = mlast; j > 0; j--) {
712
70.0k
                if (s[i+j] != p[j]) {
713
48.6k
                    break;
714
48.6k
                }
715
70.0k
            }
716
55.5k
            if (j == 0) {
717
                /* got a match! */
718
6.92k
                return i;
719
6.92k
            }
720
            /* miss: check if previous character is part of pattern */
721
48.6k
            if (i > 0 && !STRINGLIB_BLOOM(mask, s[i-1])) {
722
47.3k
                i = i - m;
723
47.3k
            }
724
1.30k
            else {
725
1.30k
                i = i - skip;
726
1.30k
            }
727
48.6k
        }
728
1.23M
        else {
729
            /* skip: check if previous character is part of pattern */
730
1.23M
            if (i > 0 && !STRINGLIB_BLOOM(mask, s[i-1])) {
731
1.12M
                i = i - m;
732
1.12M
            }
733
1.23M
        }
734
1.29M
    }
735
96
    return -1;
736
7.02k
}
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
7.02k
{
694
    /* create compressed boyer-moore delta 1 table */
695
7.02k
    unsigned long mask = 0;
696
7.02k
    Py_ssize_t i, j, mlast = m - 1, skip = m - 1, w = n - m;
697
698
    /* process pattern[0] outside the loop */
699
7.02k
    STRINGLIB_BLOOM_ADD(mask, p[0]);
700
    /* process pattern[:0:-1] */
701
28.1k
    for (i = mlast; i > 0; i--) {
702
21.0k
        STRINGLIB_BLOOM_ADD(mask, p[i]);
703
21.0k
        if (p[i] == p[0]) {
704
0
            skip = i - 1;
705
0
        }
706
21.0k
    }
707
708
1.29M
    for (i = w; i >= 0; i--) {
709
1.29M
        if (s[i] == p[0]) {
710
            /* candidate match */
711
76.9k
            for (j = mlast; j > 0; j--) {
712
70.0k
                if (s[i+j] != p[j]) {
713
48.6k
                    break;
714
48.6k
                }
715
70.0k
            }
716
55.5k
            if (j == 0) {
717
                /* got a match! */
718
6.92k
                return i;
719
6.92k
            }
720
            /* miss: check if previous character is part of pattern */
721
48.6k
            if (i > 0 && !STRINGLIB_BLOOM(mask, s[i-1])) {
722
47.3k
                i = i - m;
723
47.3k
            }
724
1.30k
            else {
725
1.30k
                i = i - skip;
726
1.30k
            }
727
48.6k
        }
728
1.23M
        else {
729
            /* skip: check if previous character is part of pattern */
730
1.23M
            if (i > 0 && !STRINGLIB_BLOOM(mask, s[i-1])) {
731
1.12M
                i = i - m;
732
1.12M
            }
733
1.23M
        }
734
1.29M
    }
735
96
    return -1;
736
7.02k
}
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
62.7M
{
762
62.7M
    Py_ssize_t count = 0;
763
5.89G
    for (Py_ssize_t i = 0; i < n; i++) {
764
5.83G
        if (s[i] == p0) {
765
177M
            count++;
766
177M
        }
767
5.83G
    }
768
62.7M
    return count;
769
62.7M
}
Unexecuted instantiation: bytesobject.c:stringlib_count_char_no_maxcount
Unexecuted instantiation: unicodeobject.c:asciilib_count_char_no_maxcount
unicodeobject.c:ucs1lib_count_char_no_maxcount
Line
Count
Source
761
51.6M
{
762
51.6M
    Py_ssize_t count = 0;
763
1.43G
    for (Py_ssize_t i = 0; i < n; i++) {
764
1.38G
        if (s[i] == p0) {
765
55.2M
            count++;
766
55.2M
        }
767
1.38G
    }
768
51.6M
    return count;
769
51.6M
}
unicodeobject.c:ucs2lib_count_char_no_maxcount
Line
Count
Source
761
9.26M
{
762
9.26M
    Py_ssize_t count = 0;
763
2.06G
    for (Py_ssize_t i = 0; i < n; i++) {
764
2.05G
        if (s[i] == p0) {
765
74.0M
            count++;
766
74.0M
        }
767
2.05G
    }
768
9.26M
    return count;
769
9.26M
}
unicodeobject.c:ucs4lib_count_char_no_maxcount
Line
Count
Source
761
1.80M
{
762
1.80M
    Py_ssize_t count = 0;
763
2.38G
    for (Py_ssize_t i = 0; i < n; i++) {
764
2.38G
        if (s[i] == p0) {
765
47.9M
            count++;
766
47.9M
        }
767
2.38G
    }
768
1.80M
    return count;
769
1.80M
}
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
229M
{
777
229M
    if (n < m || (mode == FAST_COUNT && maxcount == 0)) {
778
14.7k
        return -1;
779
14.7k
    }
780
781
    /* look for special cases */
782
229M
    if (m <= 1) {
783
195M
        if (m <= 0) {
784
0
            return -1;
785
0
        }
786
        /* use special case for 1-character strings */
787
195M
        if (mode == FAST_SEARCH)
788
132M
            return STRINGLIB(find_char)(s, n, p[0]);
789
62.7M
        else if (mode == FAST_RSEARCH)
790
61.0k
            return STRINGLIB(rfind_char)(s, n, p[0]);
791
62.7M
        else {
792
62.7M
            if (maxcount == PY_SSIZE_T_MAX) {
793
62.7M
                return STRINGLIB(count_char_no_maxcount)(s, n, p[0]);
794
62.7M
            }
795
0
            return STRINGLIB(count_char)(s, n, p[0], maxcount);
796
62.7M
        }
797
195M
    }
798
799
34.4M
    if (mode != FAST_RSEARCH) {
800
34.4M
        if (n < 2500 || (m < 100 && n < 30000) || m < 6) {
801
34.4M
            return STRINGLIB(default_find)(s, n, p, m, maxcount, mode);
802
34.4M
        }
803
23
        else if ((m >> 2) * 3 < (n >> 2)) {
804
            /* 33% threshold, but don't overflow. */
805
            /* For larger problems where the needle isn't a huge
806
               percentage of the size of the haystack, the relatively
807
               expensive O(m) startup cost of the two-way algorithm
808
               will surely pay off. */
809
23
            if (mode == FAST_SEARCH) {
810
23
                return STRINGLIB(_two_way_find)(s, n, p, m);
811
23
            }
812
0
            else {
813
0
                return STRINGLIB(_two_way_count)(s, n, p, m, maxcount);
814
0
            }
815
23
        }
816
0
        else {
817
            /* To ensure that we have good worst-case behavior,
818
               here's an adaptive version of the algorithm, where if
819
               we match O(m) characters without any matches of the
820
               entire needle, then we predict that the startup cost of
821
               the two-way algorithm will probably be worth it. */
822
0
            return STRINGLIB(adaptive_find)(s, n, p, m, maxcount, mode);
823
0
        }
824
34.4M
    }
825
7.02k
    else {
826
        /* FAST_RSEARCH */
827
7.02k
        return STRINGLIB(default_rfind)(s, n, p, m, maxcount, mode);
828
7.02k
    }
829
34.4M
}
Unexecuted instantiation: bytesobject.c:fastsearch
unicodeobject.c:asciilib_fastsearch
Line
Count
Source
776
27.6M
{
777
27.6M
    if (n < m || (mode == FAST_COUNT && maxcount == 0)) {
778
0
        return -1;
779
0
    }
780
781
    /* look for special cases */
782
27.6M
    if (m <= 1) {
783
25.2M
        if (m <= 0) {
784
0
            return -1;
785
0
        }
786
        /* use special case for 1-character strings */
787
25.2M
        if (mode == FAST_SEARCH)
788
25.2M
            return STRINGLIB(find_char)(s, n, p[0]);
789
61.0k
        else if (mode == FAST_RSEARCH)
790
61.0k
            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
25.2M
    }
798
799
2.41M
    if (mode != FAST_RSEARCH) {
800
2.41M
        if (n < 2500 || (m < 100 && n < 30000) || m < 6) {
801
2.41M
            return STRINGLIB(default_find)(s, n, p, m, maxcount, mode);
802
2.41M
        }
803
0
        else if ((m >> 2) * 3 < (n >> 2)) {
804
            /* 33% threshold, but don't overflow. */
805
            /* For larger problems where the needle isn't a huge
806
               percentage of the size of the haystack, the relatively
807
               expensive O(m) startup cost of the two-way algorithm
808
               will surely pay off. */
809
0
            if (mode == FAST_SEARCH) {
810
0
                return STRINGLIB(_two_way_find)(s, n, p, m);
811
0
            }
812
0
            else {
813
0
                return STRINGLIB(_two_way_count)(s, n, p, m, maxcount);
814
0
            }
815
0
        }
816
0
        else {
817
            /* To ensure that we have good worst-case behavior,
818
               here's an adaptive version of the algorithm, where if
819
               we match O(m) characters without any matches of the
820
               entire needle, then we predict that the startup cost of
821
               the two-way algorithm will probably be worth it. */
822
0
            return STRINGLIB(adaptive_find)(s, n, p, m, maxcount, mode);
823
0
        }
824
2.41M
    }
825
0
    else {
826
        /* FAST_RSEARCH */
827
0
        return STRINGLIB(default_rfind)(s, n, p, m, maxcount, mode);
828
0
    }
829
2.41M
}
unicodeobject.c:ucs1lib_fastsearch
Line
Count
Source
776
82.1M
{
777
82.1M
    if (n < m || (mode == FAST_COUNT && maxcount == 0)) {
778
0
        return -1;
779
0
    }
780
781
    /* look for special cases */
782
82.1M
    if (m <= 1) {
783
61.7M
        if (m <= 0) {
784
0
            return -1;
785
0
        }
786
        /* use special case for 1-character strings */
787
61.7M
        if (mode == FAST_SEARCH)
788
10.0M
            return STRINGLIB(find_char)(s, n, p[0]);
789
51.6M
        else if (mode == FAST_RSEARCH)
790
0
            return STRINGLIB(rfind_char)(s, n, p[0]);
791
51.6M
        else {
792
51.6M
            if (maxcount == PY_SSIZE_T_MAX) {
793
51.6M
                return STRINGLIB(count_char_no_maxcount)(s, n, p[0]);
794
51.6M
            }
795
0
            return STRINGLIB(count_char)(s, n, p[0], maxcount);
796
51.6M
        }
797
61.7M
    }
798
799
20.4M
    if (mode != FAST_RSEARCH) {
800
20.4M
        if (n < 2500 || (m < 100 && n < 30000) || m < 6) {
801
20.4M
            return STRINGLIB(default_find)(s, n, p, m, maxcount, mode);
802
20.4M
        }
803
23
        else if ((m >> 2) * 3 < (n >> 2)) {
804
            /* 33% threshold, but don't overflow. */
805
            /* For larger problems where the needle isn't a huge
806
               percentage of the size of the haystack, the relatively
807
               expensive O(m) startup cost of the two-way algorithm
808
               will surely pay off. */
809
23
            if (mode == FAST_SEARCH) {
810
23
                return STRINGLIB(_two_way_find)(s, n, p, m);
811
23
            }
812
0
            else {
813
0
                return STRINGLIB(_two_way_count)(s, n, p, m, maxcount);
814
0
            }
815
23
        }
816
0
        else {
817
            /* To ensure that we have good worst-case behavior,
818
               here's an adaptive version of the algorithm, where if
819
               we match O(m) characters without any matches of the
820
               entire needle, then we predict that the startup cost of
821
               the two-way algorithm will probably be worth it. */
822
0
            return STRINGLIB(adaptive_find)(s, n, p, m, maxcount, mode);
823
0
        }
824
20.4M
    }
825
0
    else {
826
        /* FAST_RSEARCH */
827
0
        return STRINGLIB(default_rfind)(s, n, p, m, maxcount, mode);
828
0
    }
829
20.4M
}
unicodeobject.c:ucs2lib_fastsearch
Line
Count
Source
776
71.9M
{
777
71.9M
    if (n < m || (mode == FAST_COUNT && maxcount == 0)) {
778
14.7k
        return -1;
779
14.7k
    }
780
781
    /* look for special cases */
782
71.9M
    if (m <= 1) {
783
67.2M
        if (m <= 0) {
784
0
            return -1;
785
0
        }
786
        /* use special case for 1-character strings */
787
67.2M
        if (mode == FAST_SEARCH)
788
57.9M
            return STRINGLIB(find_char)(s, n, p[0]);
789
9.26M
        else if (mode == FAST_RSEARCH)
790
0
            return STRINGLIB(rfind_char)(s, n, p[0]);
791
9.26M
        else {
792
9.26M
            if (maxcount == PY_SSIZE_T_MAX) {
793
9.26M
                return STRINGLIB(count_char_no_maxcount)(s, n, p[0]);
794
9.26M
            }
795
0
            return STRINGLIB(count_char)(s, n, p[0], maxcount);
796
9.26M
        }
797
67.2M
    }
798
799
4.71M
    if (mode != FAST_RSEARCH) {
800
4.71M
        if (n < 2500 || (m < 100 && n < 30000) || m < 6) {
801
4.71M
            return STRINGLIB(default_find)(s, n, p, m, maxcount, mode);
802
4.71M
        }
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.71M
    }
825
0
    else {
826
        /* FAST_RSEARCH */
827
0
        return STRINGLIB(default_rfind)(s, n, p, m, maxcount, mode);
828
0
    }
829
4.71M
}
unicodeobject.c:ucs4lib_fastsearch
Line
Count
Source
776
47.6M
{
777
47.6M
    if (n < m || (mode == FAST_COUNT && maxcount == 0)) {
778
0
        return -1;
779
0
    }
780
781
    /* look for special cases */
782
47.6M
    if (m <= 1) {
783
40.7M
        if (m <= 0) {
784
0
            return -1;
785
0
        }
786
        /* use special case for 1-character strings */
787
40.7M
        if (mode == FAST_SEARCH)
788
38.9M
            return STRINGLIB(find_char)(s, n, p[0]);
789
1.80M
        else if (mode == FAST_RSEARCH)
790
0
            return STRINGLIB(rfind_char)(s, n, p[0]);
791
1.80M
        else {
792
1.80M
            if (maxcount == PY_SSIZE_T_MAX) {
793
1.80M
                return STRINGLIB(count_char_no_maxcount)(s, n, p[0]);
794
1.80M
            }
795
0
            return STRINGLIB(count_char)(s, n, p[0], maxcount);
796
1.80M
        }
797
40.7M
    }
798
799
6.87M
    if (mode != FAST_RSEARCH) {
800
6.87M
        if (n < 2500 || (m < 100 && n < 30000) || m < 6) {
801
6.87M
            return STRINGLIB(default_find)(s, n, p, m, maxcount, mode);
802
6.87M
        }
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
6.87M
    }
825
0
    else {
826
        /* FAST_RSEARCH */
827
0
        return STRINGLIB(default_rfind)(s, n, p, m, maxcount, mode);
828
0
    }
829
6.87M
}
bytes_methods.c:fastsearch
Line
Count
Source
776
9.61k
{
777
9.61k
    if (n < m || (mode == FAST_COUNT && maxcount == 0)) {
778
8
        return -1;
779
8
    }
780
781
    /* look for special cases */
782
9.60k
    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
9.60k
    if (mode != FAST_RSEARCH) {
800
2.57k
        if (n < 2500 || (m < 100 && n < 30000) || m < 6) {
801
2.57k
            return STRINGLIB(default_find)(s, n, p, m, maxcount, mode);
802
2.57k
        }
803
0
        else if ((m >> 2) * 3 < (n >> 2)) {
804
            /* 33% threshold, but don't overflow. */
805
            /* For larger problems where the needle isn't a huge
806
               percentage of the size of the haystack, the relatively
807
               expensive O(m) startup cost of the two-way algorithm
808
               will surely pay off. */
809
0
            if (mode == FAST_SEARCH) {
810
0
                return STRINGLIB(_two_way_find)(s, n, p, m);
811
0
            }
812
0
            else {
813
0
                return STRINGLIB(_two_way_count)(s, n, p, m, maxcount);
814
0
            }
815
0
        }
816
0
        else {
817
            /* To ensure that we have good worst-case behavior,
818
               here's an adaptive version of the algorithm, where if
819
               we match O(m) characters without any matches of the
820
               entire needle, then we predict that the startup cost of
821
               the two-way algorithm will probably be worth it. */
822
0
            return STRINGLIB(adaptive_find)(s, n, p, m, maxcount, mode);
823
0
        }
824
2.57k
    }
825
7.02k
    else {
826
        /* FAST_RSEARCH */
827
7.02k
        return STRINGLIB(default_rfind)(s, n, p, m, maxcount, mode);
828
7.02k
    }
829
9.60k
}
Unexecuted instantiation: bytearrayobject.c:fastsearch
830