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