diff options
| author | thegeorg <[email protected]> | 2024-02-19 02:38:52 +0300 | 
|---|---|---|
| committer | thegeorg <[email protected]> | 2024-02-19 02:50:43 +0300 | 
| commit | d96fa07134c06472bfee6718b5cfd1679196fc99 (patch) | |
| tree | 31ec344fa9d3ff8dc038692516b6438dfbdb8a2d /contrib/tools/python3/Objects/bytes_methods.c | |
| parent | 452cf9e068aef7110e35e654c5d47eb80111ef89 (diff) | |
Sync contrib/tools/python3 layout with upstream
* Move src/ subdir contents to the top of the layout
* Rename self-written lib -> lib2 to avoid CaseFolding warning from the VCS
* Regenerate contrib/libs/python proxy-headers accordingly
4ccc62ac1511abcf0fed14ccade38e984e088f1e
Diffstat (limited to 'contrib/tools/python3/Objects/bytes_methods.c')
| -rw-r--r-- | contrib/tools/python3/Objects/bytes_methods.c | 837 | 
1 files changed, 837 insertions, 0 deletions
| diff --git a/contrib/tools/python3/Objects/bytes_methods.c b/contrib/tools/python3/Objects/bytes_methods.c new file mode 100644 index 00000000000..33aa9c3db6e --- /dev/null +++ b/contrib/tools/python3/Objects/bytes_methods.c @@ -0,0 +1,837 @@ +#define PY_SSIZE_T_CLEAN +#include "Python.h" +#include "pycore_abstract.h"   // _PyIndex_Check() +#include "pycore_bytes_methods.h" + +PyDoc_STRVAR_shared(_Py_isspace__doc__, +"B.isspace() -> bool\n\ +\n\ +Return True if all characters in B are whitespace\n\ +and there is at least one character in B, False otherwise."); + +PyObject* +_Py_bytes_isspace(const char *cptr, Py_ssize_t len) +{ +    const unsigned char *p +        = (const unsigned char *) cptr; +    const unsigned char *e; + +    /* Shortcut for single character strings */ +    if (len == 1 && Py_ISSPACE(*p)) +        Py_RETURN_TRUE; + +    /* Special case for empty strings */ +    if (len == 0) +        Py_RETURN_FALSE; + +    e = p + len; +    for (; p < e; p++) { +        if (!Py_ISSPACE(*p)) +            Py_RETURN_FALSE; +    } +    Py_RETURN_TRUE; +} + + +PyDoc_STRVAR_shared(_Py_isalpha__doc__, +"B.isalpha() -> bool\n\ +\n\ +Return True if all characters in B are alphabetic\n\ +and there is at least one character in B, False otherwise."); + +PyObject* +_Py_bytes_isalpha(const char *cptr, Py_ssize_t len) +{ +    const unsigned char *p +        = (const unsigned char *) cptr; +    const unsigned char *e; + +    /* Shortcut for single character strings */ +    if (len == 1 && Py_ISALPHA(*p)) +        Py_RETURN_TRUE; + +    /* Special case for empty strings */ +    if (len == 0) +        Py_RETURN_FALSE; + +    e = p + len; +    for (; p < e; p++) { +        if (!Py_ISALPHA(*p)) +            Py_RETURN_FALSE; +    } +    Py_RETURN_TRUE; +} + + +PyDoc_STRVAR_shared(_Py_isalnum__doc__, +"B.isalnum() -> bool\n\ +\n\ +Return True if all characters in B are alphanumeric\n\ +and there is at least one character in B, False otherwise."); + +PyObject* +_Py_bytes_isalnum(const char *cptr, Py_ssize_t len) +{ +    const unsigned char *p +        = (const unsigned char *) cptr; +    const unsigned char *e; + +    /* Shortcut for single character strings */ +    if (len == 1 && Py_ISALNUM(*p)) +        Py_RETURN_TRUE; + +    /* Special case for empty strings */ +    if (len == 0) +        Py_RETURN_FALSE; + +    e = p + len; +    for (; p < e; p++) { +        if (!Py_ISALNUM(*p)) +            Py_RETURN_FALSE; +    } +    Py_RETURN_TRUE; +} + + +PyDoc_STRVAR_shared(_Py_isascii__doc__, +"B.isascii() -> bool\n\ +\n\ +Return True if B is empty or all characters in B are ASCII,\n\ +False otherwise."); + +// Optimization is copied from ascii_decode in unicodeobject.c +/* Mask to quickly check whether a C 'size_t' contains a +   non-ASCII, UTF8-encoded char. */ +#if (SIZEOF_SIZE_T == 8) +# define ASCII_CHAR_MASK 0x8080808080808080ULL +#elif (SIZEOF_SIZE_T == 4) +# define ASCII_CHAR_MASK 0x80808080U +#else +# error C 'size_t' size should be either 4 or 8! +#endif + +PyObject* +_Py_bytes_isascii(const char *cptr, Py_ssize_t len) +{ +    const char *p = cptr; +    const char *end = p + len; + +    while (p < end) { +        /* Fast path, see in STRINGLIB(utf8_decode) in stringlib/codecs.h +           for an explanation. */ +        if (_Py_IS_ALIGNED(p, ALIGNOF_SIZE_T)) { +            /* Help allocation */ +            const char *_p = p; +            while (_p + SIZEOF_SIZE_T <= end) { +                size_t value = *(const size_t *) _p; +                if (value & ASCII_CHAR_MASK) { +                    Py_RETURN_FALSE; +                } +                _p += SIZEOF_SIZE_T; +            } +            p = _p; +            if (_p == end) +                break; +        } +        if ((unsigned char)*p & 0x80) { +            Py_RETURN_FALSE; +        } +        p++; +    } +    Py_RETURN_TRUE; +} + +#undef ASCII_CHAR_MASK + + +PyDoc_STRVAR_shared(_Py_isdigit__doc__, +"B.isdigit() -> bool\n\ +\n\ +Return True if all characters in B are digits\n\ +and there is at least one character in B, False otherwise."); + +PyObject* +_Py_bytes_isdigit(const char *cptr, Py_ssize_t len) +{ +    const unsigned char *p +        = (const unsigned char *) cptr; +    const unsigned char *e; + +    /* Shortcut for single character strings */ +    if (len == 1 && Py_ISDIGIT(*p)) +        Py_RETURN_TRUE; + +    /* Special case for empty strings */ +    if (len == 0) +        Py_RETURN_FALSE; + +    e = p + len; +    for (; p < e; p++) { +        if (!Py_ISDIGIT(*p)) +            Py_RETURN_FALSE; +    } +    Py_RETURN_TRUE; +} + + +PyDoc_STRVAR_shared(_Py_islower__doc__, +"B.islower() -> bool\n\ +\n\ +Return True if all cased characters in B are lowercase and there is\n\ +at least one cased character in B, False otherwise."); + +PyObject* +_Py_bytes_islower(const char *cptr, Py_ssize_t len) +{ +    const unsigned char *p +        = (const unsigned char *) cptr; +    const unsigned char *e; +    int cased; + +    /* Shortcut for single character strings */ +    if (len == 1) +        return PyBool_FromLong(Py_ISLOWER(*p)); + +    /* Special case for empty strings */ +    if (len == 0) +        Py_RETURN_FALSE; + +    e = p + len; +    cased = 0; +    for (; p < e; p++) { +        if (Py_ISUPPER(*p)) +            Py_RETURN_FALSE; +        else if (!cased && Py_ISLOWER(*p)) +            cased = 1; +    } +    return PyBool_FromLong(cased); +} + + +PyDoc_STRVAR_shared(_Py_isupper__doc__, +"B.isupper() -> bool\n\ +\n\ +Return True if all cased characters in B are uppercase and there is\n\ +at least one cased character in B, False otherwise."); + +PyObject* +_Py_bytes_isupper(const char *cptr, Py_ssize_t len) +{ +    const unsigned char *p +        = (const unsigned char *) cptr; +    const unsigned char *e; +    int cased; + +    /* Shortcut for single character strings */ +    if (len == 1) +        return PyBool_FromLong(Py_ISUPPER(*p)); + +    /* Special case for empty strings */ +    if (len == 0) +        Py_RETURN_FALSE; + +    e = p + len; +    cased = 0; +    for (; p < e; p++) { +        if (Py_ISLOWER(*p)) +            Py_RETURN_FALSE; +        else if (!cased && Py_ISUPPER(*p)) +            cased = 1; +    } +    return PyBool_FromLong(cased); +} + + +PyDoc_STRVAR_shared(_Py_istitle__doc__, +"B.istitle() -> bool\n\ +\n\ +Return True if B is a titlecased string and there is at least one\n\ +character in B, i.e. uppercase characters may only follow uncased\n\ +characters and lowercase characters only cased ones. Return False\n\ +otherwise."); + +PyObject* +_Py_bytes_istitle(const char *cptr, Py_ssize_t len) +{ +    const unsigned char *p +        = (const unsigned char *) cptr; +    const unsigned char *e; +    int cased, previous_is_cased; + +    if (len == 1) { +        if (Py_ISUPPER(*p)) { +            Py_RETURN_TRUE; +        } +        Py_RETURN_FALSE; +    } + +    /* Special case for empty strings */ +    if (len == 0) +        Py_RETURN_FALSE; + +    e = p + len; +    cased = 0; +    previous_is_cased = 0; +    for (; p < e; p++) { +        const unsigned char ch = *p; + +        if (Py_ISUPPER(ch)) { +            if (previous_is_cased) +                Py_RETURN_FALSE; +            previous_is_cased = 1; +            cased = 1; +        } +        else if (Py_ISLOWER(ch)) { +            if (!previous_is_cased) +                Py_RETURN_FALSE; +            previous_is_cased = 1; +            cased = 1; +        } +        else +            previous_is_cased = 0; +    } +    return PyBool_FromLong(cased); +} + + +PyDoc_STRVAR_shared(_Py_lower__doc__, +"B.lower() -> copy of B\n\ +\n\ +Return a copy of B with all ASCII characters converted to lowercase."); + +void +_Py_bytes_lower(char *result, const char *cptr, Py_ssize_t len) +{ +    Py_ssize_t i; + +    for (i = 0; i < len; i++) { +        result[i] = Py_TOLOWER((unsigned char) cptr[i]); +    } +} + + +PyDoc_STRVAR_shared(_Py_upper__doc__, +"B.upper() -> copy of B\n\ +\n\ +Return a copy of B with all ASCII characters converted to uppercase."); + +void +_Py_bytes_upper(char *result, const char *cptr, Py_ssize_t len) +{ +    Py_ssize_t i; + +    for (i = 0; i < len; i++) { +        result[i] = Py_TOUPPER((unsigned char) cptr[i]); +    } +} + + +PyDoc_STRVAR_shared(_Py_title__doc__, +"B.title() -> copy of B\n\ +\n\ +Return a titlecased version of B, i.e. ASCII words start with uppercase\n\ +characters, all remaining cased characters have lowercase."); + +void +_Py_bytes_title(char *result, const char *s, Py_ssize_t len) +{ +    Py_ssize_t i; +    int previous_is_cased = 0; + +    for (i = 0; i < len; i++) { +        int c = Py_CHARMASK(*s++); +        if (Py_ISLOWER(c)) { +            if (!previous_is_cased) +                c = Py_TOUPPER(c); +            previous_is_cased = 1; +        } else if (Py_ISUPPER(c)) { +            if (previous_is_cased) +                c = Py_TOLOWER(c); +            previous_is_cased = 1; +        } else +            previous_is_cased = 0; +        *result++ = c; +    } +} + + +PyDoc_STRVAR_shared(_Py_capitalize__doc__, +"B.capitalize() -> copy of B\n\ +\n\ +Return a copy of B with only its first character capitalized (ASCII)\n\ +and the rest lower-cased."); + +void +_Py_bytes_capitalize(char *result, const char *s, Py_ssize_t len) +{ +    if (len > 0) { +        *result = Py_TOUPPER(*s); +        _Py_bytes_lower(result + 1, s + 1, len - 1); +    } +} + + +PyDoc_STRVAR_shared(_Py_swapcase__doc__, +"B.swapcase() -> copy of B\n\ +\n\ +Return a copy of B with uppercase ASCII characters converted\n\ +to lowercase ASCII and vice versa."); + +void +_Py_bytes_swapcase(char *result, const char *s, Py_ssize_t len) +{ +    Py_ssize_t i; + +    for (i = 0; i < len; i++) { +        int c = Py_CHARMASK(*s++); +        if (Py_ISLOWER(c)) { +            *result = Py_TOUPPER(c); +        } +        else if (Py_ISUPPER(c)) { +            *result = Py_TOLOWER(c); +        } +        else +            *result = c; +        result++; +    } +} + + +PyDoc_STRVAR_shared(_Py_maketrans__doc__, +"B.maketrans(frm, to) -> translation table\n\ +\n\ +Return a translation table (a bytes object of length 256) suitable\n\ +for use in the bytes or bytearray translate method where each byte\n\ +in frm is mapped to the byte at the same position in to.\n\ +The bytes objects frm and to must be of the same length."); + +PyObject * +_Py_bytes_maketrans(Py_buffer *frm, Py_buffer *to) +{ +    PyObject *res = NULL; +    Py_ssize_t i; +    char *p; + +    if (frm->len != to->len) { +        PyErr_Format(PyExc_ValueError, +                     "maketrans arguments must have same length"); +        return NULL; +    } +    res = PyBytes_FromStringAndSize(NULL, 256); +    if (!res) +        return NULL; +    p = PyBytes_AS_STRING(res); +    for (i = 0; i < 256; i++) +        p[i] = (char) i; +    for (i = 0; i < frm->len; i++) { +        p[((unsigned char *)frm->buf)[i]] = ((char *)to->buf)[i]; +    } + +    return res; +} + +#define FASTSEARCH fastsearch +#define STRINGLIB(F) stringlib_##F +#define STRINGLIB_CHAR char +#define STRINGLIB_SIZEOF_CHAR 1 +#define STRINGLIB_FAST_MEMCHR memchr + +#include "stringlib/fastsearch.h" +#include "stringlib/count.h" +#include "stringlib/find.h" + +/* +Wraps stringlib_parse_args_finds() and additionally checks the first +argument type. + +In case the first argument is a bytes-like object, sets it to subobj, +and doesn't touch the byte parameter. +In case it is an integer in range(0, 256), writes the integer value +to byte, and sets subobj to NULL. + +The other parameters are similar to those of +stringlib_parse_args_finds(). +*/ + +Py_LOCAL_INLINE(int) +parse_args_finds_byte(const char *function_name, PyObject *args, +                      PyObject **subobj, char *byte, +                      Py_ssize_t *start, Py_ssize_t *end) +{ +    PyObject *tmp_subobj; +    Py_ssize_t ival; + +    if(!stringlib_parse_args_finds(function_name, args, &tmp_subobj, +                                   start, end)) +        return 0; + +    if (PyObject_CheckBuffer(tmp_subobj)) { +        *subobj = tmp_subobj; +        return 1; +    } + +    if (!_PyIndex_Check(tmp_subobj)) { +        PyErr_Format(PyExc_TypeError, +                     "argument should be integer or bytes-like object, " +                     "not '%.200s'", +                     Py_TYPE(tmp_subobj)->tp_name); +        return 0; +    } + +    ival = PyNumber_AsSsize_t(tmp_subobj, NULL); +    if (ival == -1 && PyErr_Occurred()) { +        return 0; +    } +    if (ival < 0 || ival > 255) { +        PyErr_SetString(PyExc_ValueError, "byte must be in range(0, 256)"); +        return 0; +    } + +    *subobj = NULL; +    *byte = (char)ival; +    return 1; +} + +/* helper macro to fixup start/end slice values */ +#define ADJUST_INDICES(start, end, len)         \ +    if (end > len)                          \ +        end = len;                          \ +    else if (end < 0) {                     \ +        end += len;                         \ +        if (end < 0)                        \ +        end = 0;                        \ +    }                                       \ +    if (start < 0) {                        \ +        start += len;                       \ +        if (start < 0)                      \ +        start = 0;                      \ +    } + +Py_LOCAL_INLINE(Py_ssize_t) +find_internal(const char *str, Py_ssize_t len, +              const char *function_name, PyObject *args, int dir) +{ +    PyObject *subobj; +    char byte; +    Py_buffer subbuf; +    const char *sub; +    Py_ssize_t sub_len; +    Py_ssize_t start = 0, end = PY_SSIZE_T_MAX; +    Py_ssize_t res; + +    if (!parse_args_finds_byte(function_name, args, +                               &subobj, &byte, &start, &end)) +        return -2; + +    if (subobj) { +        if (PyObject_GetBuffer(subobj, &subbuf, PyBUF_SIMPLE) != 0) +            return -2; + +        sub = subbuf.buf; +        sub_len = subbuf.len; +    } +    else { +        sub = &byte; +        sub_len = 1; +    } + +    ADJUST_INDICES(start, end, len); +    if (end - start < sub_len) +        res = -1; +    else if (sub_len == 1) { +        if (dir > 0) +            res = stringlib_find_char( +                str + start, end - start, +                *sub); +        else +            res = stringlib_rfind_char( +                str + start, end - start, +                *sub); +        if (res >= 0) +            res += start; +    } +    else { +        if (dir > 0) +            res = stringlib_find_slice( +                str, len, +                sub, sub_len, start, end); +        else +            res = stringlib_rfind_slice( +                str, len, +                sub, sub_len, start, end); +    } + +    if (subobj) +        PyBuffer_Release(&subbuf); + +    return res; +} + +PyDoc_STRVAR_shared(_Py_find__doc__, +"B.find(sub[, start[, end]]) -> int\n\ +\n\ +Return the lowest index in B where subsection sub is found,\n\ +such that sub is contained within B[start,end].  Optional\n\ +arguments start and end are interpreted as in slice notation.\n\ +\n\ +Return -1 on failure."); + +PyObject * +_Py_bytes_find(const char *str, Py_ssize_t len, PyObject *args) +{ +    Py_ssize_t result = find_internal(str, len, "find", args, +1); +    if (result == -2) +        return NULL; +    return PyLong_FromSsize_t(result); +} + +PyDoc_STRVAR_shared(_Py_index__doc__, +"B.index(sub[, start[, end]]) -> int\n\ +\n\ +Return the lowest index in B where subsection sub is found,\n\ +such that sub is contained within B[start,end].  Optional\n\ +arguments start and end are interpreted as in slice notation.\n\ +\n\ +Raises ValueError when the subsection is not found."); + +PyObject * +_Py_bytes_index(const char *str, Py_ssize_t len, PyObject *args) +{ +    Py_ssize_t result = find_internal(str, len, "index", args, +1); +    if (result == -2) +        return NULL; +    if (result == -1) { +        PyErr_SetString(PyExc_ValueError, +                        "subsection not found"); +        return NULL; +    } +    return PyLong_FromSsize_t(result); +} + +PyDoc_STRVAR_shared(_Py_rfind__doc__, +"B.rfind(sub[, start[, end]]) -> int\n\ +\n\ +Return the highest index in B where subsection sub is found,\n\ +such that sub is contained within B[start,end].  Optional\n\ +arguments start and end are interpreted as in slice notation.\n\ +\n\ +Return -1 on failure."); + +PyObject * +_Py_bytes_rfind(const char *str, Py_ssize_t len, PyObject *args) +{ +    Py_ssize_t result = find_internal(str, len, "rfind", args, -1); +    if (result == -2) +        return NULL; +    return PyLong_FromSsize_t(result); +} + +PyDoc_STRVAR_shared(_Py_rindex__doc__, +"B.rindex(sub[, start[, end]]) -> int\n\ +\n\ +Return the highest index in B where subsection sub is found,\n\ +such that sub is contained within B[start,end].  Optional\n\ +arguments start and end are interpreted as in slice notation.\n\ +\n\ +Raise ValueError when the subsection is not found."); + +PyObject * +_Py_bytes_rindex(const char *str, Py_ssize_t len, PyObject *args) +{ +    Py_ssize_t result = find_internal(str, len, "rindex", args, -1); +    if (result == -2) +        return NULL; +    if (result == -1) { +        PyErr_SetString(PyExc_ValueError, +                        "subsection not found"); +        return NULL; +    } +    return PyLong_FromSsize_t(result); +} + +PyDoc_STRVAR_shared(_Py_count__doc__, +"B.count(sub[, start[, end]]) -> int\n\ +\n\ +Return the number of non-overlapping occurrences of subsection sub in\n\ +bytes B[start:end].  Optional arguments start and end are interpreted\n\ +as in slice notation."); + +PyObject * +_Py_bytes_count(const char *str, Py_ssize_t len, PyObject *args) +{ +    PyObject *sub_obj; +    const char *sub; +    Py_ssize_t sub_len; +    char byte; +    Py_ssize_t start = 0, end = PY_SSIZE_T_MAX; + +    Py_buffer vsub; +    PyObject *count_obj; + +    if (!parse_args_finds_byte("count", args, +                               &sub_obj, &byte, &start, &end)) +        return NULL; + +    if (sub_obj) { +        if (PyObject_GetBuffer(sub_obj, &vsub, PyBUF_SIMPLE) != 0) +            return NULL; + +        sub = vsub.buf; +        sub_len = vsub.len; +    } +    else { +        sub = &byte; +        sub_len = 1; +    } + +    ADJUST_INDICES(start, end, len); + +    count_obj = PyLong_FromSsize_t( +        stringlib_count(str + start, end - start, sub, sub_len, PY_SSIZE_T_MAX) +        ); + +    if (sub_obj) +        PyBuffer_Release(&vsub); + +    return count_obj; +} + +int +_Py_bytes_contains(const char *str, Py_ssize_t len, PyObject *arg) +{ +    Py_ssize_t ival = PyNumber_AsSsize_t(arg, NULL); +    if (ival == -1 && PyErr_Occurred()) { +        Py_buffer varg; +        Py_ssize_t pos; +        PyErr_Clear(); +        if (PyObject_GetBuffer(arg, &varg, PyBUF_SIMPLE) != 0) +            return -1; +        pos = stringlib_find(str, len, +                             varg.buf, varg.len, 0); +        PyBuffer_Release(&varg); +        return pos >= 0; +    } +    if (ival < 0 || ival >= 256) { +        PyErr_SetString(PyExc_ValueError, "byte must be in range(0, 256)"); +        return -1; +    } + +    return memchr(str, (int) ival, len) != NULL; +} + + +/* Matches the end (direction >= 0) or start (direction < 0) of the buffer + * against substr, using the start and end arguments. Returns + * -1 on error, 0 if not found and 1 if found. + */ +static int +tailmatch(const char *str, Py_ssize_t len, PyObject *substr, +          Py_ssize_t start, Py_ssize_t end, int direction) +{ +    Py_buffer sub_view = {NULL, NULL}; +    const char *sub; +    Py_ssize_t slen; + +    if (PyBytes_Check(substr)) { +        sub = PyBytes_AS_STRING(substr); +        slen = PyBytes_GET_SIZE(substr); +    } +    else { +        if (PyObject_GetBuffer(substr, &sub_view, PyBUF_SIMPLE) != 0) +            return -1; +        sub = sub_view.buf; +        slen = sub_view.len; +    } + +    ADJUST_INDICES(start, end, len); + +    if (direction < 0) { +        /* startswith */ +        if (start > len - slen) +            goto notfound; +    } else { +        /* endswith */ +        if (end - start < slen || start > len) +            goto notfound; + +        if (end - slen > start) +            start = end - slen; +    } +    if (end - start < slen) +        goto notfound; +    if (memcmp(str + start, sub, slen) != 0) +        goto notfound; + +    PyBuffer_Release(&sub_view); +    return 1; + +notfound: +    PyBuffer_Release(&sub_view); +    return 0; +} + +static PyObject * +_Py_bytes_tailmatch(const char *str, Py_ssize_t len, +                    const char *function_name, PyObject *args, +                    int direction) +{ +    Py_ssize_t start = 0; +    Py_ssize_t end = PY_SSIZE_T_MAX; +    PyObject *subobj = NULL; +    int result; + +    if (!stringlib_parse_args_finds(function_name, args, &subobj, &start, &end)) +        return NULL; +    if (PyTuple_Check(subobj)) { +        Py_ssize_t i; +        for (i = 0; i < PyTuple_GET_SIZE(subobj); i++) { +            result = tailmatch(str, len, PyTuple_GET_ITEM(subobj, i), +                               start, end, direction); +            if (result == -1) +                return NULL; +            else if (result) { +                Py_RETURN_TRUE; +            } +        } +        Py_RETURN_FALSE; +    } +    result = tailmatch(str, len, subobj, start, end, direction); +    if (result == -1) { +        if (PyErr_ExceptionMatches(PyExc_TypeError)) +            PyErr_Format(PyExc_TypeError, +                         "%s first arg must be bytes or a tuple of bytes, " +                         "not %s", +                         function_name, Py_TYPE(subobj)->tp_name); +        return NULL; +    } +    else +        return PyBool_FromLong(result); +} + +PyDoc_STRVAR_shared(_Py_startswith__doc__, +"B.startswith(prefix[, start[, end]]) -> bool\n\ +\n\ +Return True if B starts with the specified prefix, False otherwise.\n\ +With optional start, test B beginning at that position.\n\ +With optional end, stop comparing B at that position.\n\ +prefix can also be a tuple of bytes to try."); + +PyObject * +_Py_bytes_startswith(const char *str, Py_ssize_t len, PyObject *args) +{ +    return _Py_bytes_tailmatch(str, len, "startswith", args, -1); +} + +PyDoc_STRVAR_shared(_Py_endswith__doc__, +"B.endswith(suffix[, start[, end]]) -> bool\n\ +\n\ +Return True if B ends with the specified suffix, False otherwise.\n\ +With optional start, test B beginning at that position.\n\ +With optional end, stop comparing B at that position.\n\ +suffix can also be a tuple of bytes to try."); + +PyObject * +_Py_bytes_endswith(const char *str, Py_ssize_t len, PyObject *args) +{ +    return _Py_bytes_tailmatch(str, len, "endswith", args, +1); +} | 
