8000 gh-117431: Adapt bytes and bytearray .startswith() and .endswith() to… · python/cpython@595bb49 · GitHub
[go: up one dir, main page]

Skip to content

Commit 595bb49

Browse files
gh-117431: Adapt bytes and bytearray .startswith() and .endswith() to Argument Clinic (#117495)
This change gives a significant speedup, as the METH_FASTCALL calling convention is now used.
1 parent 1dc1521 commit 595bb49

File tree

7 files changed

+318
-55
lines changed

7 files changed

+318
-55
lines changed

Include/internal/pycore_bytes_methods.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,12 @@ extern PyObject *_Py_bytes_rfind(const char *str, Py_ssize_t len, PyObject *args
3232
extern PyObject *_Py_bytes_rindex(const char *str, Py_ssize_t len, PyObject *args);
3333
extern PyObject *_Py_bytes_count(const char *str, Py_ssize_t len, PyObject *args);
3434
extern int _Py_bytes_contains(const char *str, Py_ssize_t len, PyObject *arg);
35-
extern PyObject *_Py_bytes_startswith(const char *str, Py_ssize_t len, PyObject *args);
36-
extern PyObject *_Py_bytes_endswith(const char *str, Py_ssize_t len, PyObject *args);
35+
extern PyObject *_Py_bytes_startswith(const char *str, Py_ssize_t len,
36+
PyObject *subobj, Py_ssize_t start,
37+
Py_ssize_t end);
38+
extern PyObject *_Py_bytes_endswith(const char *str, Py_ssize_t len,
39+
PyObject *subobj, Py_ssize_t start,
40+
Py_ssize_t end);
3741

3842
/* The maketrans() static method. */
3943
extern PyObject* _Py_bytes_maketrans(Py_buffer *frm, Py_buffer *to);
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Improve the performance of the following :class:`bytes` and
2+
:class:`bytearray` methods by adapting them to the :c:macro:`METH_FASTCALL`
3+
calling convention:
4+
5+
* :meth:`!endswith`
6+
* :meth:`!startswith`

Objects/bytearrayobject.c

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1186,16 +1186,52 @@ bytearray_contains(PyObject *self, PyObject *arg)
11861186
return _Py_bytes_contains(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), arg);
11871187
}
11881188

1189+
/*[clinic input]
1190+
@text_signature "($self, prefix[, start[, end]], /)"
1191+
bytearray.startswith
1192+
1193+
prefix as subobj: object
1194+
A bytes or a tuple of bytes to try.
1195+
start: slice_index(accept={int, NoneType}, c_default='0') = None
1196+
Optional start position. Default: start of the bytearray.
1197+
end: slice_index(accept={int, NoneType}, c_default='PY_SSIZE_T_MAX') = None
1198+
Optional stop position. Default: end of the bytearray.
1199+
/
1200+
1201+
Return True if the bytearray starts with the specified prefix, False otherwise.
1202+
[clinic start generated code]*/
1203+
11891204
static PyObject *
1190-
bytearray_startswith(PyByteArrayObject *self, PyObject *args)
1205+
bytearray_startswith_impl(PyByteArrayObject *self, PyObject *subobj,
1206+
Py_ssize_t start, Py_ssize_t end)
1207+
/*[clinic end generated code: output=a3d9b6d44d3662a6 input=76385e0b376b45c1]*/
11911208
{
1192-
return _Py_bytes_startswith(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), args);
1209+
return _Py_bytes_startswith(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self),
1210+
subobj, start, end);
11931211
}
11941212

1213+
/*[clinic input]
1214+
@text_signature "($self, suffix[, start[, end]], /)"
1215+
bytearray.endswith
1216+
1217+
suffix as subobj: object
1218+
A bytes or a tuple of bytes to try.
1219+
start: slice_index(accept={int, NoneType}, c_default='0') = None
1220+
Optional start position. Default: start of the bytearray.
1221+
end: slice_index(accept={int, NoneType}, c_default='PY_SSIZE_T_MAX') = None
1222+
Optional stop position. Default: end of the bytearray.
1223+
/
1224+
1225+
Return True if the bytearray ends with the specified suffix, False otherwise.
1226+
[clinic start generated code]*/
1227+
11951228
static PyObject *
1196-
bytearray_endswith(PyByteArrayObject *self, PyObject *args)
1229+
bytearray_endswith_impl(PyByteArrayObject *self, PyObject *subobj,
1230+
Py_ssize_t start, Py_ssize_t end)
1231+
/*[clinic end generated code: output=e75ea8c227954caa input=9b8baa879aa3d74b]*/
11971232
{
1198-
return _Py_bytes_endswith(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), args);
1233+
return _Py_bytes_endswith(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self),
1234+
subobj, start, end);
11991235
}
12001236

12011237
/*[clinic input]
@@ -2203,8 +2239,7 @@ bytearray_methods[] = {
22032239
{"count", (PyCFunction)bytearray_count, METH_VARARGS,
22042240
_Py_count__doc__},
22052241
BYTEARRAY_DECODE_METHODDEF
2206-
{"endswith", (PyCFunction)bytearray_endswith, METH_VARARGS,
2207-
_Py_endswith__doc__},
2242+
BYTEARRAY_ENDSWITH_METHODDEF
22082243
STRINGLIB_EXPANDTABS_METHODDEF
22092244
BYTEARRAY_EXTEND_METHODDEF
22102245
{"find", (PyCFunction)bytearray_find, METH_VARARGS,
@@ -2249,8 +2284,7 @@ bytearray_methods[] = {
22492284
BYTEARRAY_RSTRIP_METHODDEF
22502285
BYTEARRAY_SPLIT_METHODDEF
22512286
BYTEARRAY_SPLITLINES_METHODDEF
2252-
{"startswith", (PyCFunction)bytearray_startswith, METH_VARARGS ,
2253-
_Py_startswith__doc__},
2287+
BYTEARRAY_STARTSWITH_METHODDEF
22542288
BYTEARRAY_STRIP_METHODDEF
22552289
{"swapcase", stringlib_swapcase, METH_NOARGS,
22562290
_Py_swapcase__doc__},

Objects/bytes_methods.c

Lines changed: 16 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -771,66 +771,47 @@ tailmatch(const char *str, Py_ssize_t len, PyObject *substr,
771771

772772
static PyObject *
773773
_Py_bytes_tailmatch(const char *str, Py_ssize_t len,
774-
const char *function_name, PyObject *args,
774+
const char *function_name, PyObject *subobj,
775+
Py_ssize_t start, Py_ssize_t end,
775776
int direction)
776777
{
777-
Py_ssize_t start = 0;
778-
Py_ssize_t end = PY_SSIZE_T_MAX;
779-
PyObject *subobj = NULL;
780-
int result;
781-
782-
if (!stringlib_parse_args_finds(function_name, args, &subobj, &start, &end))
783-
return NULL;
784778
if (PyTuple_Check(subobj)) {
785779
Py_ssize_t i;
786780
for (i = 0; i < PyTuple_GET_SIZE(subobj); i++) {
787-
result = tailmatch(str, len, PyTuple_GET_ITEM(subobj, i),
788-
start, end, direction);
789-
if (result == -1)
781+
PyObject *item = PyTuple_GET_ITEM(subobj, i);
782+
int result = tailmatch(str, len, item, start, end, direction);
783+
if (result < 0) {
790784
return NULL;
785+
}
791786
else if (result) {
792787
Py_RETURN_TRUE;
793788
}
794789
}
795790
Py_RETURN_FALSE;
796791
}
797-
result = tailmatch(str, len, subobj, start, end, direction);
792+
int result = tailmatch(str, len, subobj, start, end, direction);
798793
if (result == -1) {
799-
if (PyErr_ExceptionMatches(PyExc_TypeError))
794+
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
800795
PyErr_Format(PyExc_TypeError,
801796
"%s first arg must be bytes or a tuple of bytes, "
802797
"not %s",
803798
function_name, Py_TYPE(subobj)->tp_name);
799+
}
804800
return NULL;
805801
}
806-
else
807-
return PyBool_FromLong(result);
802+
return PyBool_FromLong(result);
808803
}
809804

810-
PyDoc_STRVAR_shared(_Py_startswith__doc__,
811-
"B.startswith(prefix[, start[, end]]) -> bool\n\
812-
\n\
813-
Return True if B starts with the specified prefix, False otherwise.\n\
814-
With optional start, test B beginning at that position.\n\
815-
With optional end, stop comparing B at that position.\n\
816-
prefix can also be a tuple of bytes to try.");
817-
818805
PyObject *
819-
_Py_bytes_startswith(const char *str, Py_ssize_t len, PyObject *args)
806+
_Py_bytes_startswith(const char *str, Py_ssize_t len, PyObject *subobj,
807+
Py_ssize_t start, Py_ssize_t end)
820808
{
821-
return _Py_bytes_tailmatch(str, len, "startswith", args, -1);
809+
return _Py_bytes_tailmatch(str, len, "startswith", subobj, start, end, -1);
822810
}
823811

824-
PyDoc_STRVAR_shared(_Py_endswith__doc__,
825-
"B.endswith(suffix[, start[, end]]) -> bool\n\
826-
\n\
827-
Return True if B ends with the specified suffix, False otherwise.\n\
828-
With optional start, test B beginning at that position.\n\
829-
With optional end, stop comparing B at that position.\n\
830-
suffix can also be a tuple of bytes to try.");
831-
832812
PyObject *
833-
_Py_bytes_endswith(const char *str, Py_ssize_t len, PyObject *args)
813+
_Py_bytes_endswith(const char *str, Py_ssize_t len, PyObject *subobj,
814+
Py_ssize_t start, Py_ssize_t end)
834815
{
835-
return _Py_bytes_tailmatch(str, len, "endswith", args, +1);
816+
return _Py_bytes_tailmatch(str, len, "endswith", subobj, start, end, +1);
836817
}

Objects/bytesobject.c

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
< F438 div class="diff-text-inner color-fg-muted">@@ -2285,16 +2285,52 @@ bytes_removesuffix_impl(PyBytesObject *self, Py_buffer *suffix)
22852285
return PyBytes_FromStringAndSize(self_start, self_len);
22862286
}
22872287

2288+
/*[clinic input]
2289+
@text_signature "($self, prefix[, start[, end]], /)"
2290+
bytes.startswith
2291+
2292+
prefix as subobj: object
2293+
A bytes or a tuple of bytes to try.
2294+
start: slice_index(accept={int, NoneType}, c_default='0') = None
2295+
Optional start position. Default: start of the bytes.
2296+
end: slice_index(accept={int, NoneType}, c_default='PY_SSIZE_T_MAX') = None
2297+
Optional stop position. Default: end of the bytes.
2298+
/
2299+
2300+
Return True if the bytes starts with the specified prefix, False otherwise.
2301+
[clinic start generated code]*/
2302+
22882303
static PyObject *
2289-
bytes_startswith(PyBytesObject *self, PyObject *args)
2304+
bytes_startswith_impl(PyBytesObject *self, PyObject *subobj,
2305+
Py_ssize_t start, Py_ssize_t end)
2306+
/*[clinic end generated code: output=b1e8da1cbd528e8c input=8a4165df8adfa6c9]*/
22902307
{
2291-
return _Py_bytes_startswith(PyBytes_AS_STRING(self), PyBytes_GET_SIZE(self), args);
2308+
return _Py_bytes_startswith(PyBytes_AS_STRING(self), PyBytes_GET_SIZE(self),
2309+
subobj, start, end);
22922310
}
22932311

2312+
/*[clinic input]
2313+
@text_signature "($self, suffix[, start[, end]], /)"
2314+
bytes.endswith
2315+
2316+
suffix as subobj: object
2317+
A bytes or a tuple of bytes to try.
2318+
start: slice_index(accept={int, NoneType}, c_default='0') = None
2319+
Optional start position. Default: start of the bytes.
2320+
end: slice_index(accept={int, NoneType}, c_default='PY_SSIZE_T_MAX') = None
2321+
Optional stop position. Default: end of the bytes.
2322+
/
2323+
2324+
Return True if the bytes ends with the specified suffix, False otherwise.
2325+
[clinic start generated code]*/
2326+
22942327
static PyObject *
2295-
bytes_endswith(PyBytesObject *self, PyObject *args)
2328+
bytes_endswith_impl(PyBytesObject *self, PyObject *subobj, Py_ssize_t start,
2329+
Py_ssize_t end)
2330+
/*[clinic end generated code: output=038b633111f3629d input=b5c3407a2a5c9aac]*/
22962331
{
2297-
return _Py_bytes_endswith(PyBytes_AS_STRING(self), PyBytes_GET_SIZE(self), args);
2332+
return _Py_bytes_endswith(PyBytes_AS_STRING(self), PyBytes_GET_SIZE(self),
2333+
subobj, start, end);
22982334
}
22992335

23002336

@@ -2491,8 +2527,7 @@ bytes_methods[] = {
24912527
{"count", (PyCFunction)bytes_count, METH_VARARGS,
24922528
_Py_count__doc__},
24932529
BYTES_DECODE_METHODDEF
2494-
{"endswith", (PyCFunction)bytes_endswith, METH_VARARGS,
2495-
_Py_endswith__doc__},
2530+
BYTES_ENDSWITH_METHODDEF
24962531
STRINGLIB_EXPANDTABS_METHODDEF
24972532
{"find", (PyCFunction)bytes_find, METH_VARARGS,
24982533
_Py_find__doc__},
@@ -2532,8 +2567,7 @@ bytes_methods[] = {
25322567
BYTES_RSTRIP_METHODDEF
25332568
BYTES_SPLIT_METHODDEF
25342569
BYTES_SPLITLINES_METHODDEF
2535-
{"startswith", (PyCFunction)bytes_startswith, METH_VARARGS,
2536-
_Py_startswith__doc__},
2570+
BYTES_STARTSWITH_METHODDEF
25372571
BYTES_STRIP_METHODDEF
25382572
{"swapcase", stringlib_swapcase, METH_NOARGS,
25392573
_Py_swapcase__doc__},

Objects/clinic/bytearrayobject.c.h

Lines changed: 103 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)
0