Skip to content
Merged
8 changes: 6 additions & 2 deletions Include/internal/pycore_bytes_methods.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,12 @@ extern PyObject *_Py_bytes_rfind(const char *str, Py_ssize_t len, PyObject *args
extern PyObject *_Py_bytes_rindex(const char *str, Py_ssize_t len, PyObject *args);
extern PyObject *_Py_bytes_count(const char *str, Py_ssize_t len, PyObject *args);
extern int _Py_bytes_contains(const char *str, Py_ssize_t len, PyObject *arg);
extern PyObject *_Py_bytes_startswith(const char *str, Py_ssize_t len, PyObject *args);
extern PyObject *_Py_bytes_endswith(const char *str, Py_ssize_t len, PyObject *args);
extern PyObject *_Py_bytes_startswith(const char *str, Py_ssize_t len,
PyObject *subobj, Py_ssize_t start,
Py_ssize_t end);
extern PyObject *_Py_bytes_endswith(const char *str, Py_ssize_t len,
PyObject *subobj, Py_ssize_t start,
Py_ssize_t end);

/* The maketrans() static method. */
extern PyObject* _Py_bytes_maketrans(Py_buffer *frm, Py_buffer *to);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Improve the performance of the following :class:`bytes` and
:class:`bytearray` methods by adapting them to the :c:macro:`METH_FASTCALL`
calling convention:

* :meth:`!endswith`
* :meth:`!startswith`
50 changes: 42 additions & 8 deletions Objects/bytearrayobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1186,16 +1186,52 @@ bytearray_contains(PyObject *self, PyObject *arg)
return _Py_bytes_contains(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), arg);
}

/*[clinic input]
@text_signature "($self, prefix[, start[, end]], /)"
bytearray.startswith

prefix as subobj: object
A bytes or a tuple of bytes to try.
start: slice_index(accept={int, NoneType}, c_default='0') = None
Optional start position. Default: start of the bytearray.
end: slice_index(accept={int, NoneType}, c_default='PY_SSIZE_T_MAX') = None
Optional stop position. Default: end of the bytearray.
/

Return True if the bytearray starts with the specified prefix, False otherwise.
[clinic start generated code]*/

static PyObject *
bytearray_startswith(PyByteArrayObject *self, PyObject *args)
bytearray_startswith_impl(PyByteArrayObject *self, PyObject *subobj,
Py_ssize_t start, Py_ssize_t end)
/*[clinic end generated code: output=a3d9b6d44d3662a6 input=76385e0b376b45c1]*/
{
return _Py_bytes_startswith(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), args);
return _Py_bytes_startswith(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self),
subobj, start, end);
}

/*[clinic input]
@text_signature "($self, suffix[, start[, end]], /)"
bytearray.endswith

suffix as subobj: object
A bytes or a tuple of bytes to try.
start: slice_index(accept={int, NoneType}, c_default='0') = None
Optional start position. Default: start of the bytearray.
end: slice_index(accept={int, NoneType}, c_default='PY_SSIZE_T_MAX') = None
Optional stop position. Default: end of the bytearray.
/

Return True if the bytearray ends with the specified suffix, False otherwise.
[clinic start generated code]*/

static PyObject *
bytearray_endswith(PyByteArrayObject *self, PyObject *args)
bytearray_endswith_impl(PyByteArrayObject *self, PyObject *subobj,
Py_ssize_t start, Py_ssize_t end)
/*[clinic end generated code: output=e75ea8c227954caa input=9b8baa879aa3d74b]*/
{
return _Py_bytes_endswith(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), args);
return _Py_bytes_endswith(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self),
subobj, start, end);
}

/*[clinic input]
Expand Down Expand Up @@ -2203,8 +2239,7 @@ bytearray_methods[] = {
{"count", (PyCFunction)bytearray_count, METH_VARARGS,
_Py_count__doc__},
BYTEARRAY_DECODE_METHODDEF
{"endswith", (PyCFunction)bytearray_endswith, METH_VARARGS,
_Py_endswith__doc__},
BYTEARRAY_ENDSWITH_METHODDEF
STRINGLIB_EXPANDTABS_METHODDEF
BYTEARRAY_EXTEND_METHODDEF
{"find", (PyCFunction)bytearray_find, METH_VARARGS,
Expand Down Expand Up @@ -2249,8 +2284,7 @@ bytearray_methods[] = {
BYTEARRAY_RSTRIP_METHODDEF
BYTEARRAY_SPLIT_METHODDEF
BYTEARRAY_SPLITLINES_METHODDEF
{"startswith", (PyCFunction)bytearray_startswith, METH_VARARGS ,
_Py_startswith__doc__},
BYTEARRAY_STARTSWITH_METHODDEF
BYTEARRAY_STRIP_METHODDEF
{"swapcase", stringlib_swapcase, METH_NOARGS,
_Py_swapcase__doc__},
Expand Down
51 changes: 16 additions & 35 deletions Objects/bytes_methods.c
Original file line number Diff line number Diff line change
Expand Up @@ -771,66 +771,47 @@ tailmatch(const char *str, Py_ssize_t len, PyObject *substr,

static PyObject *
_Py_bytes_tailmatch(const char *str, Py_ssize_t len,
const char *function_name, PyObject *args,
const char *function_name, PyObject *subobj,
Py_ssize_t start, Py_ssize_t end,
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)
PyObject *item = PyTuple_GET_ITEM(subobj, i);
int result = tailmatch(str, len, item, start, end, direction);
if (result < 0) {
return NULL;
}
else if (result) {
Py_RETURN_TRUE;
}
}
Py_RETURN_FALSE;
}
result = tailmatch(str, len, subobj, start, end, direction);
int result = tailmatch(str, len, subobj, start, end, direction);
if (result == -1) {
if (PyErr_ExceptionMatches(PyExc_TypeError))
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);
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)
_Py_bytes_startswith(const char *str, Py_ssize_t len, PyObject *subobj,
Py_ssize_t start, Py_ssize_t end)
{
return _Py_bytes_tailmatch(str, len, "startswith", args, -1);
return _Py_bytes_tailmatch(str, len, "startswith", subobj, start, end, -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)
_Py_bytes_endswith(const char *str, Py_ssize_t len, PyObject *subobj,
Py_ssize_t start, Py_ssize_t end)
{
return _Py_bytes_tailmatch(str, len, "endswith", args, +1);
return _Py_bytes_tailmatch(str, len, "endswith", subobj, start, end, +1);
}
50 changes: 42 additions & 8 deletions Objects/bytesobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -2285,16 +2285,52 @@ bytes_removesuffix_impl(PyBytesObject *self, Py_buffer *suffix)
return PyBytes_FromStringAndSize(self_start, self_len);
}

/*[clinic input]
@text_signature "($self, prefix[, start[, end]], /)"
bytes.startswith

prefix as subobj: object
A bytes or a tuple of bytes to try.
start: slice_index(accept={int, NoneType}, c_default='0') = None
Optional start position. Default: start of the bytes.
end: slice_index(accept={int, NoneType}, c_default='PY_SSIZE_T_MAX') = None
Optional stop position. Default: end of the bytes.
/

Return True if the bytes starts with the specified prefix, False otherwise.
[clinic start generated code]*/

static PyObject *
bytes_startswith(PyBytesObject *self, PyObject *args)
bytes_startswith_impl(PyBytesObject *self, PyObject *subobj,
Py_ssize_t start, Py_ssize_t end)
/*[clinic end generated code: output=b1e8da1cbd528e8c input=8a4165df8adfa6c9]*/
{
return _Py_bytes_startswith(PyBytes_AS_STRING(self), PyBytes_GET_SIZE(self), args);
return _Py_bytes_startswith(PyBytes_AS_STRING(self), PyBytes_GET_SIZE(self),
subobj, start, end);
}

/*[clinic input]
@text_signature "($self, suffix[, start[, end]], /)"
bytes.endswith

suffix as subobj: object
A bytes or a tuple of bytes to try.
start: slice_index(accept={int, NoneType}, c_default='0') = None
Optional start position. Default: start of the bytes.
end: slice_index(accept={int, NoneType}, c_default='PY_SSIZE_T_MAX') = None
Optional stop position. Default: end of the bytes.
/

Return True if the bytes ends with the specified suffix, False otherwise.
[clinic start generated code]*/

static PyObject *
bytes_endswith(PyBytesObject *self, PyObject *args)
bytes_endswith_impl(PyBytesObject *self, PyObject *subobj, Py_ssize_t start,
Py_ssize_t end)
/*[clinic end generated code: output=038b633111f3629d input=b5c3407a2a5c9aac]*/
{
return _Py_bytes_endswith(PyBytes_AS_STRING(self), PyBytes_GET_SIZE(self), args);
return _Py_bytes_endswith(PyBytes_AS_STRING(self), PyBytes_GET_SIZE(self),
subobj, start, end);
}


Expand Down Expand Up @@ -2491,8 +2527,7 @@ bytes_methods[] = {
{"count", (PyCFunction)bytes_count, METH_VARARGS,
_Py_count__doc__},
BYTES_DECODE_METHODDEF
{"endswith", (PyCFunction)bytes_endswith, METH_VARARGS,
_Py_endswith__doc__},
BYTES_ENDSWITH_METHODDEF
STRINGLIB_EXPANDTABS_METHODDEF
{"find", (PyCFunction)bytes_find, METH_VARARGS,
_Py_find__doc__},
Expand Down Expand Up @@ -2532,8 +2567,7 @@ bytes_methods[] = {
BYTES_RSTRIP_METHODDEF
BYTES_SPLIT_METHODDEF
BYTES_SPLITLINES_METHODDEF
{"startswith", (PyCFunction)bytes_startswith, METH_VARARGS,
_Py_startswith__doc__},
BYTES_STARTSWITH_METHODDEF
BYTES_STRIP_METHODDEF
{"swapcase", stringlib_swapcase, METH_NOARGS,
_Py_swapcase__doc__},
Expand Down
104 changes: 103 additions & 1 deletion Objects/clinic/bytearrayobject.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading