Coverage Report

Created: 2026-10-17 06:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cpython/Objects/bytes_methods.c
Line
Count
Source
1
#include "Python.h"
2
#include "pycore_abstract.h"   // _PyIndex_Check()
3
#include "pycore_bytes_methods.h"
4
5
PyDoc_STRVAR_shared(_Py_isspace__doc__,
6
"B.isspace() -> bool\n\
7
\n\
8
Return True if all characters in B are whitespace\n\
9
and there is at least one character in B, False otherwise.");
10
11
PyObject*
12
_Py_bytes_isspace(const char *cptr, Py_ssize_t len)
13
0
{
14
0
    const unsigned char *p
15
0
        = (const unsigned char *) cptr;
16
0
    const unsigned char *e;
17
18
    /* Shortcut for single character strings */
19
0
    if (len == 1 && Py_ISSPACE(*p))
20
0
        Py_RETURN_TRUE;
21
22
    /* Special case for empty strings */
23
0
    if (len == 0)
24
0
        Py_RETURN_FALSE;
25
26
0
    e = p + len;
27
0
    for (; p < e; p++) {
28
0
        if (!Py_ISSPACE(*p))
29
0
            Py_RETURN_FALSE;
30
0
    }
31
0
    Py_RETURN_TRUE;
32
0
}
33
34
35
PyDoc_STRVAR_shared(_Py_isalpha__doc__,
36
"B.isalpha() -> bool\n\
37
\n\
38
Return True if all characters in B are alphabetic\n\
39
and there is at least one character in B, False otherwise.");
40
41
PyObject*
42
_Py_bytes_isalpha(const char *cptr, Py_ssize_t len)
43
0
{
44
0
    const unsigned char *p
45
0
        = (const unsigned char *) cptr;
46
0
    const unsigned char *e;
47
48
    /* Shortcut for single character strings */
49
0
    if (len == 1 && Py_ISALPHA(*p))
50
0
        Py_RETURN_TRUE;
51
52
    /* Special case for empty strings */
53
0
    if (len == 0)
54
0
        Py_RETURN_FALSE;
55
56
0
    e = p + len;
57
0
    for (; p < e; p++) {
58
0
        if (!Py_ISALPHA(*p))
59
0
            Py_RETURN_FALSE;
60
0
    }
61
0
    Py_RETURN_TRUE;
62
0
}
63
64
65
PyDoc_STRVAR_shared(_Py_isalnum__doc__,
66
"B.isalnum() -> bool\n\
67
\n\
68
Return True if all characters in B are alphanumeric\n\
69
and there is at least one character in B, False otherwise.");
70
71
PyObject*
72
_Py_bytes_isalnum(const char *cptr, Py_ssize_t len)
73
0
{
74
0
    const unsigned char *p
75
0
        = (const unsigned char *) cptr;
76
0
    const unsigned char *e;
77
78
    /* Shortcut for single character strings */
79
0
    if (len == 1 && Py_ISALNUM(*p))
80
0
        Py_RETURN_TRUE;
81
82
    /* Special case for empty strings */
83
0
    if (len == 0)
84
0
        Py_RETURN_FALSE;
85
86
0
    e = p + len;
87
0
    for (; p < e; p++) {
88
0
        if (!Py_ISALNUM(*p))
89
0
            Py_RETURN_FALSE;
90
0
    }
91
0
    Py_RETURN_TRUE;
92
0
}
93
94
95
PyDoc_STRVAR_shared(_Py_isdigit__doc__,
96
"B.isdigit() -> bool\n\
97
\n\
98
Return True if all characters in B are digits\n\
99
and there is at least one character in B, False otherwise.");
100
101
PyObject*
102
_Py_bytes_isdigit(const char *cptr, Py_ssize_t len)
103
0
{
104
0
    const unsigned char *p
105
0
        = (const unsigned char *) cptr;
106
0
    const unsigned char *e;
107
108
    /* Shortcut for single character strings */
109
0
    if (len == 1 && Py_ISDIGIT(*p))
110
0
        Py_RETURN_TRUE;
111
112
    /* Special case for empty strings */
113
0
    if (len == 0)
114
0
        Py_RETURN_FALSE;
115
116
0
    e = p + len;
117
0
    for (; p < e; p++) {
118
0
        if (!Py_ISDIGIT(*p))
119
0
            Py_RETURN_FALSE;
120
0
    }
121
0
    Py_RETURN_TRUE;
122
0
}
123
124
125
PyDoc_STRVAR_shared(_Py_islower__doc__,
126
"B.islower() -> bool\n\
127
\n\
128
Return True if all cased characters in B are lowercase and there is\n\
129
at least one cased character in B, False otherwise.");
130
131
PyObject*
132
_Py_bytes_islower(const char *cptr, Py_ssize_t len)
133
0
{
134
0
    const unsigned char *p
135
0
        = (const unsigned char *) cptr;
136
0
    const unsigned char *e;
137
0
    int cased;
138
139
    /* Shortcut for single character strings */
140
0
    if (len == 1)
141
0
        return PyBool_FromLong(Py_ISLOWER(*p));
142
143
    /* Special case for empty strings */
144
0
    if (len == 0)
145
0
        Py_RETURN_FALSE;
146
147
0
    e = p + len;
148
0
    cased = 0;
149
0
    for (; p < e; p++) {
150
0
        if (Py_ISUPPER(*p))
151
0
            Py_RETURN_FALSE;
152
0
        else if (!cased && Py_ISLOWER(*p))
153
0
            cased = 1;
154
0
    }
155
0
    return PyBool_FromLong(cased);
156
0
}
157
158
159
PyDoc_STRVAR_shared(_Py_isupper__doc__,
160
"B.isupper() -> bool\n\
161
\n\
162
Return True if all cased characters in B are uppercase and there is\n\
163
at least one cased character in B, False otherwise.");
164
165
PyObject*
166
_Py_bytes_isupper(const char *cptr, Py_ssize_t len)
167
0
{
168
0
    const unsigned char *p
169
0
        = (const unsigned char *) cptr;
170
0
    const unsigned char *e;
171
0
    int cased;
172
173
    /* Shortcut for single character strings */
174
0
    if (len == 1)
175
0
        return PyBool_FromLong(Py_ISUPPER(*p));
176
177
    /* Special case for empty strings */
178
0
    if (len == 0)
179
0
        Py_RETURN_FALSE;
180
181
0
    e = p + len;
182
0
    cased = 0;
183
0
    for (; p < e; p++) {
184
0
        if (Py_ISLOWER(*p))
185
0
            Py_RETURN_FALSE;
186
0
        else if (!cased && Py_ISUPPER(*p))
187
0
            cased = 1;
188
0
    }
189
0
    return PyBool_FromLong(cased);
190
0
}
191
192
193
PyDoc_STRVAR_shared(_Py_istitle__doc__,
194
"B.istitle() -> bool\n\
195
\n\
196
Return True if B is a titlecased string and there is at least one\n\
197
character in B, i.e. uppercase characters may only follow uncased\n\
198
characters and lowercase characters only cased ones. Return False\n\
199
otherwise.");
200
201
PyObject*
202
_Py_bytes_istitle(const char *cptr, Py_ssize_t len)
203
0
{
204
0
    const unsigned char *p
205
0
        = (const unsigned char *) cptr;
206
0
    const unsigned char *e;
207
0
    int cased, previous_is_cased;
208
209
0
    if (len == 1) {
210
0
        if (Py_ISUPPER(*p)) {
211
0
            Py_RETURN_TRUE;
212
0
        }
213
0
        Py_RETURN_FALSE;
214
0
    }
215
216
    /* Special case for empty strings */
217
0
    if (len == 0)
218
0
        Py_RETURN_FALSE;
219
220
0
    e = p + len;
221
0
    cased = 0;
222
0
    previous_is_cased = 0;
223
0
    for (; p < e; p++) {
224
0
        const unsigned char ch = *p;
225
226
0
        if (Py_ISUPPER(ch)) {
227
0
            if (previous_is_cased)
228
0
                Py_RETURN_FALSE;
229
0
            previous_is_cased = 1;
230
0
            cased = 1;
231
0
        }
232
0
        else if (Py_ISLOWER(ch)) {
233
0
            if (!previous_is_cased)
234
0
                Py_RETURN_FALSE;
235
0
            previous_is_cased = 1;
236
0
            cased = 1;
237
0
        }
238
0
        else
239
0
            previous_is_cased = 0;
240
0
    }
241
0
    return PyBool_FromLong(cased);
242
0
}
243
244
245
PyDoc_STRVAR_shared(_Py_lower__doc__,
246
"B.lower() -> copy of B\n\
247
\n\
248
Return a copy of B with all ASCII characters converted to lowercase.");
249
250
void
251
_Py_bytes_lower(char *result, const char *cptr, Py_ssize_t len)
252
75.3M
{
253
75.3M
    Py_ssize_t i;
254
255
991M
    for (i = 0; i < len; i++) {
256
916M
        result[i] = Py_TOLOWER((unsigned char) cptr[i]);
257
916M
    }
258
75.3M
}
259
260
261
PyDoc_STRVAR_shared(_Py_upper__doc__,
262
"B.upper() -> copy of B\n\
263
\n\
264
Return a copy of B with all ASCII characters converted to uppercase.");
265
266
void
267
_Py_bytes_upper(char *result, const char *cptr, Py_ssize_t len)
268
1.19M
{
269
1.19M
    Py_ssize_t i;
270
271
10.1M
    for (i = 0; i < len; i++) {
272
8.92M
        result[i] = Py_TOUPPER((unsigned char) cptr[i]);
273
8.92M
    }
274
1.19M
}
275
276
277
PyDoc_STRVAR_shared(_Py_title__doc__,
278
"B.title() -> copy of B\n\
279
\n\
280
Return a titlecased version of B, i.e. ASCII words start with\n\
281
uppercase characters, all remaining cased characters have lowercase.");
282
283
void
284
_Py_bytes_title(char *result, const char *s, Py_ssize_t len)
285
0
{
286
0
    Py_ssize_t i;
287
0
    int previous_is_cased = 0;
288
289
0
    for (i = 0; i < len; i++) {
290
0
        int c = Py_CHARMASK(*s++);
291
0
        if (Py_ISLOWER(c)) {
292
0
            if (!previous_is_cased)
293
0
                c = Py_TOUPPER(c);
294
0
            previous_is_cased = 1;
295
0
        } else if (Py_ISUPPER(c)) {
296
0
            if (previous_is_cased)
297
0
                c = Py_TOLOWER(c);
298
0
            previous_is_cased = 1;
299
0
        } else
300
0
            previous_is_cased = 0;
301
0
        *result++ = c;
302
0
    }
303
0
}
304
305
306
PyDoc_STRVAR_shared(_Py_capitalize__doc__,
307
"B.capitalize() -> copy of B\n\
308
\n\
309
Return a copy of B with only its first character capitalized (ASCII)\n\
310
and the rest lower-cased.");
311
312
void
313
_Py_bytes_capitalize(char *result, const char *s, Py_ssize_t len)
314
0
{
315
0
    if (len > 0) {
316
0
        *result = Py_TOUPPER(*s);
317
0
        _Py_bytes_lower(result + 1, s + 1, len - 1);
318
0
    }
319
0
}
320
321
322
PyDoc_STRVAR_shared(_Py_swapcase__doc__,
323
"B.swapcase() -> copy of B\n\
324
\n\
325
Return a copy of B with uppercase ASCII characters converted\n\
326
to lowercase ASCII and vice versa.");
327
328
void
329
_Py_bytes_swapcase(char *result, const char *s, Py_ssize_t len)
330
0
{
331
0
    Py_ssize_t i;
332
333
0
    for (i = 0; i < len; i++) {
334
0
        int c = Py_CHARMASK(*s++);
335
0
        if (Py_ISLOWER(c)) {
336
0
            *result = Py_TOUPPER(c);
337
0
        }
338
0
        else if (Py_ISUPPER(c)) {
339
0
            *result = Py_TOLOWER(c);
340
0
        }
341
0
        else
342
0
            *result = c;
343
0
        result++;
344
0
    }
345
0
}
346
347
348
PyDoc_STRVAR_shared(_Py_maketrans__doc__,
349
"B.maketrans(frm, to) -> translation table\n\
350
\n\
351
Return a translation table (a bytes object of length 256) suitable\n\
352
for use in the bytes or bytearray translate method where each byte\n\
353
in frm is mapped to the byte at the same position in to.\n\
354
The bytes objects frm and to must be of the same length.");
355
356
PyObject *
357
_Py_bytes_maketrans(Py_buffer *frm, Py_buffer *to)
358
9
{
359
9
    if (frm->len != to->len) {
360
0
        PyErr_Format(PyExc_ValueError,
361
0
                     "maketrans arguments must have same length");
362
0
        return NULL;
363
0
    }
364
9
    PyBytesWriter *writer = PyBytesWriter_Create(256);
365
9
    if (!writer) {
366
0
        return NULL;
367
0
    }
368
9
    char *p = PyBytesWriter_GetData(writer);
369
9
    Py_ssize_t i;
370
2.31k
    for (i = 0; i < 256; i++)
371
2.30k
        p[i] = (char) i;
372
27
    for (i = 0; i < frm->len; i++) {
373
18
        p[((unsigned char *)frm->buf)[i]] = ((char *)to->buf)[i];
374
18
    }
375
376
9
    return PyBytesWriter_Finish(writer);
377
9
}
378
379
5.55M
#define FASTSEARCH fastsearch
380
5.55M
#define STRINGLIB(F) stringlib_##F
381
0
#define STRINGLIB_CHAR char
382
0
#define STRINGLIB_SIZEOF_CHAR 1
383
3.27M
#define STRINGLIB_FAST_MEMCHR memchr
384
385
#include "stringlib/fastsearch.h"
386
#include "stringlib/count.h"
387
#include "stringlib/find.h"
388
#include "stringlib/find_max_char.h"
389
390
/*
391
Wraps stringlib_parse_args_finds() and additionally checks the first
392
argument type.
393
394
In case the first argument is a bytes-like object, sets it to subobj,
395
and doesn't touch the byte parameter.
396
In case it is an integer in range(0, 256), writes the integer value
397
to byte, and sets subobj to NULL.
398
399
The other parameters are similar to those of
400
stringlib_parse_args_finds().
401
*/
402
403
Py_LOCAL_INLINE(int)
404
parse_args_finds_byte(const char *function_name, PyObject **subobj, char *byte)
405
18.5M
{
406
18.5M
    if (PyObject_CheckBuffer(*subobj)) {
407
18.0M
        return 1;
408
18.0M
    }
409
410
489k
    if (!_PyIndex_Check(*subobj)) {
411
0
        PyErr_Format(PyExc_TypeError,
412
0
                     "argument should be integer or bytes-like object, "
413
0
                     "not '%.200s'",
414
0
                     Py_TYPE(*subobj)->tp_name);
415
0
        return 0;
416
0
    }
417
418
489k
    Py_ssize_t ival = PyNumber_AsSsize_t(*subobj, NULL);
419
489k
    if (ival == -1 && PyErr_Occurred()) {
420
0
        return 0;
421
0
    }
422
489k
    if (ival < 0 || ival > 255) {
423
0
        PyErr_SetString(PyExc_ValueError, "byte must be in range(0, 256)");
424
0
        return 0;
425
0
    }
426
427
489k
    *subobj = NULL;
428
489k
    *byte = (char)ival;
429
489k
    return 1;
430
489k
}
431
432
/* helper macro to fixup start/end slice values */
433
#define ADJUST_INDICES(start, end, len) \
434
26.0M
    do {                                \
435
26.0M
        if (end > len) {                \
436
26.0M
            end = len;                  \
437
26.0M
        }                               \
438
26.0M
        else if (end < 0) {             \
439
0
            end += len;                 \
440
0
            if (end < 0) {              \
441
0
                end = 0;                \
442
0
            }                           \
443
0
        }                               \
444
26.0M
        if (start < 0) {                \
445
26.5k
            start += len;               \
446
26.5k
            if (start < 0) {            \
447
19.1k
                start = 0;              \
448
19.1k
            }                           \
449
26.5k
        }                               \
450
26.0M
    } while (0)
451
452
Py_LOCAL_INLINE(Py_ssize_t)
453
find_internal(const char *str, Py_ssize_t len,
454
              const char *function_name, PyObject *subobj,
455
              Py_ssize_t start, Py_ssize_t end,
456
              int dir)
457
12.9M
{
458
12.9M
    char byte;
459
12.9M
    Py_buffer subbuf;
460
12.9M
    const char *sub;
461
12.9M
    Py_ssize_t sub_len;
462
12.9M
    Py_ssize_t res;
463
464
12.9M
    if (!parse_args_finds_byte(function_name, &subobj, &byte)) {
465
0
        return -2;
466
0
    }
467
468
12.9M
    if (subobj) {
469
12.4M
        if (PyObject_GetBuffer(subobj, &subbuf, PyBUF_SIMPLE) != 0)
470
0
            return -2;
471
472
12.4M
        sub = subbuf.buf;
473
12.4M
        sub_len = subbuf.len;
474
12.4M
    }
475
489k
    else {
476
489k
        sub = &byte;
477
489k
        sub_len = 1;
478
489k
    }
479
480
12.9M
    ADJUST_INDICES(start, end, len);
481
12.9M
    if (end - start < sub_len)
482
31.0k
        res = -1;
483
12.9M
    else if (sub_len == 1) {
484
12.9M
        if (dir > 0)
485
11.7M
            res = stringlib_find_char(
486
11.7M
                str + start, end - start,
487
11.7M
                *sub);
488
1.19M
        else
489
1.19M
            res = stringlib_rfind_char(
490
1.19M
                str + start, end - start,
491
1.19M
                *sub);
492
12.9M
        if (res >= 0)
493
8.01M
            res += start;
494
12.9M
    }
495
7.00k
    else {
496
7.00k
        if (dir > 0)
497
0
            res = stringlib_find_slice(
498
0
                str, len,
499
0
                sub, sub_len, start, end);
500
7.00k
        else
501
7.00k
            res = stringlib_rfind_slice(
502
7.00k
                str, len,
503
7.00k
                sub, sub_len, start, end);
504
7.00k
    }
505
506
12.9M
    if (subobj)
507
12.4M
        PyBuffer_Release(&subbuf);
508
509
12.9M
    return res;
510
12.9M
}
511
512
PyObject *
513
_Py_bytes_find(const char *str, Py_ssize_t len, PyObject *sub,
514
               Py_ssize_t start, Py_ssize_t end)
515
11.7M
{
516
11.7M
    Py_ssize_t result = find_internal(str, len, "find", sub, start, end, +1);
517
11.7M
    if (result == -2)
518
0
        return NULL;
519
11.7M
    return PyLong_FromSsize_t(result);
520
11.7M
}
521
522
PyObject *
523
_Py_bytes_index(const char *str, Py_ssize_t len, PyObject *sub,
524
                Py_ssize_t start, Py_ssize_t end)
525
0
{
526
0
    Py_ssize_t result = find_internal(str, len, "index", sub, start, end, +1);
527
0
    if (result == -2)
528
0
        return NULL;
529
0
    if (result == -1) {
530
0
        PyErr_SetString(PyExc_ValueError,
531
0
                        "subsection not found");
532
0
        return NULL;
533
0
    }
534
0
    return PyLong_FromSsize_t(result);
535
0
}
536
537
PyObject *
538
_Py_bytes_rfind(const char *str, Py_ssize_t len, PyObject *sub,
539
                Py_ssize_t start, Py_ssize_t end)
540
1.19M
{
541
1.19M
    Py_ssize_t result = find_internal(str, len, "rfind", sub, start, end, -1);
542
1.19M
    if (result == -2)
543
0
        return NULL;
544
1.19M
    return PyLong_FromSsize_t(result);
545
1.19M
}
546
547
PyObject *
548
_Py_bytes_rindex(const char *str, Py_ssize_t len, PyObject *sub,
549
                 Py_ssize_t start, Py_ssize_t end)
550
0
{
551
0
    Py_ssize_t result = find_internal(str, len, "rindex", sub, start, end, -1);
552
0
    if (result == -2)
553
0
        return NULL;
554
0
    if (result == -1) {
555
0
        PyErr_SetString(PyExc_ValueError,
556
0
                        "subsection not found");
557
0
        return NULL;
558
0
    }
559
0
    return PyLong_FromSsize_t(result);
560
0
}
561
562
PyObject *
563
_Py_bytes_count(const char *str, Py_ssize_t len, PyObject *sub_obj,
564
                Py_ssize_t start, Py_ssize_t end)
565
5.54M
{
566
5.54M
    const char *sub;
567
5.54M
    Py_ssize_t sub_len;
568
5.54M
    char byte;
569
570
5.54M
    Py_buffer vsub;
571
5.54M
    PyObject *count_obj;
572
573
5.54M
    if (!parse_args_finds_byte("count", &sub_obj, &byte)) {
574
0
        return NULL;
575
0
    }
576
577
5.54M
    if (sub_obj) {
578
5.54M
        if (PyObject_GetBuffer(sub_obj, &vsub, PyBUF_SIMPLE) != 0)
579
0
            return NULL;
580
581
5.54M
        sub = vsub.buf;
582
5.54M
        sub_len = vsub.len;
583
5.54M
    }
584
0
    else {
585
0
        sub = &byte;
586
0
        sub_len = 1;
587
0
    }
588
589
5.54M
    ADJUST_INDICES(start, end, len);
590
591
5.54M
    count_obj = PyLong_FromSsize_t(
592
5.54M
        stringlib_count(str + start, end - start, sub, sub_len, PY_SSIZE_T_MAX)
593
5.54M
        );
594
595
5.54M
    if (sub_obj)
596
5.54M
        PyBuffer_Release(&vsub);
597
598
5.54M
    return count_obj;
599
5.54M
}
600
601
int
602
_Py_bytes_contains(const char *str, Py_ssize_t len, PyObject *arg)
603
2.74k
{
604
2.74k
    Py_ssize_t ival = PyNumber_AsSsize_t(arg, NULL);
605
2.74k
    if (ival == -1 && PyErr_Occurred()) {
606
2.72k
        Py_buffer varg;
607
2.72k
        Py_ssize_t pos;
608
2.72k
        PyErr_Clear();
609
2.72k
        if (PyObject_GetBuffer(arg, &varg, PyBUF_SIMPLE) != 0)
610
0
            return -1;
611
2.72k
        pos = stringlib_find(str, len,
612
2.72k
                             varg.buf, varg.len, 0);
613
2.72k
        PyBuffer_Release(&varg);
614
2.72k
        return pos >= 0;
615
2.72k
    }
616
14
    if (ival < 0 || ival >= 256) {
617
0
        PyErr_SetString(PyExc_ValueError, "byte must be in range(0, 256)");
618
0
        return -1;
619
0
    }
620
621
14
    return memchr(str, (int) ival, len) != NULL;
622
14
}
623
624
625
/* Matches the end (direction >= 0) or start (direction < 0) of the buffer
626
 * against substr, using the start and end arguments. Returns
627
 * -1 on error, 0 if not found and 1 if found.
628
 */
629
static int
630
tailmatch(const char *str, Py_ssize_t len, PyObject *substr,
631
          Py_ssize_t start, Py_ssize_t end, int direction)
632
7.56M
{
633
7.56M
    Py_buffer sub_view = {NULL, NULL};
634
7.56M
    const char *sub;
635
7.56M
    Py_ssize_t slen;
636
637
7.56M
    if (PyBytes_Check(substr)) {
638
7.56M
        sub = PyBytes_AS_STRING(substr);
639
7.56M
        slen = PyBytes_GET_SIZE(substr);
640
7.56M
    }
641
0
    else {
642
0
        if (PyObject_GetBuffer(substr, &sub_view, PyBUF_SIMPLE) != 0)
643
0
            return -1;
644
0
        sub = sub_view.buf;
645
0
        slen = sub_view.len;
646
0
    }
647
648
7.56M
    ADJUST_INDICES(start, end, len);
649
650
7.56M
    if (direction < 0) {
651
        /* startswith */
652
7.56M
        if (start > len - slen)
653
4.57M
            goto notfound;
654
7.56M
    } else {
655
        /* endswith */
656
713
        if (end - start < slen || start > len)
657
4
            goto notfound;
658
659
709
        if (end - slen > start)
660
708
            start = end - slen;
661
709
    }
662
2.99M
    if (end - start < slen)
663
0
        goto notfound;
664
2.99M
    if (memcmp(str + start, sub, slen) != 0)
665
1.79M
        goto notfound;
666
667
1.20M
    PyBuffer_Release(&sub_view);
668
1.20M
    return 1;
669
670
6.36M
notfound:
671
6.36M
    PyBuffer_Release(&sub_view);
672
6.36M
    return 0;
673
2.99M
}
674
675
static PyObject *
676
_Py_bytes_tailmatch(const char *str, Py_ssize_t len,
677
                    const char *function_name, PyObject *subobj,
678
                    Py_ssize_t start, Py_ssize_t end,
679
                    int direction)
680
7.56M
{
681
7.56M
    if (PyTuple_Check(subobj)) {
682
12
        Py_ssize_t i;
683
36
        for (i = 0; i < PyTuple_GET_SIZE(subobj); i++) {
684
24
            PyObject *item = PyTuple_GET_ITEM(subobj, i);
685
24
            int result = tailmatch(str, len, item, start, end, direction);
686
24
            if (result < 0) {
687
0
                return NULL;
688
0
            }
689
24
            else if (result) {
690
0
                Py_RETURN_TRUE;
691
0
            }
692
24
        }
693
12
        Py_RETURN_FALSE;
694
12
    }
695
7.56M
    int result = tailmatch(str, len, subobj, start, end, direction);
696
7.56M
    if (result == -1) {
697
0
        if (PyErr_ExceptionMatches(PyExc_TypeError)) {
698
0
            PyErr_Format(PyExc_TypeError,
699
0
                         "%s first arg must be bytes or a tuple of bytes, "
700
0
                         "not %s",
701
0
                         function_name, Py_TYPE(subobj)->tp_name);
702
0
        }
703
0
        return NULL;
704
0
    }
705
7.56M
    return PyBool_FromLong(result);
706
7.56M
}
707
708
PyObject *
709
_Py_bytes_startswith(const char *str, Py_ssize_t len, PyObject *subobj,
710
                     Py_ssize_t start, Py_ssize_t end)
711
7.56M
{
712
7.56M
    return _Py_bytes_tailmatch(str, len, "startswith", subobj, start, end, -1);
713
7.56M
}
714
715
PyObject *
716
_Py_bytes_endswith(const char *str, Py_ssize_t len, PyObject *subobj,
717
                   Py_ssize_t start, Py_ssize_t end)
718
713
{
719
713
    return _Py_bytes_tailmatch(str, len, "endswith", subobj, start, end, +1);
720
713
}
721
722
PyDoc_STRVAR_shared(_Py_isascii__doc__,
723
"B.isascii() -> bool\n\
724
\n\
725
Return True if B is empty or all characters in B are ASCII,\n\
726
False otherwise.");
727
728
PyObject*
729
_Py_bytes_isascii(const char *cptr, Py_ssize_t len)
730
0
{
731
0
    const char *p = cptr;
732
0
    const char *end = p + len;
733
0
    Py_ssize_t max_char = stringlib_find_max_char(cptr, end);
734
0
    if (max_char > 127) {
735
0
        Py_RETURN_FALSE;
736
0
    }
737
0
    Py_RETURN_TRUE;
738
0
}