Skip to content

Commit 08f207e

Browse files
authored
[mypyc] optimise startswith and endswith (#9557)
Relates to mypyc/mypyc#644.
1 parent ab1bd98 commit 08f207e

File tree

4 files changed

+42
-2
lines changed

4 files changed

+42
-2
lines changed

mypyc/lib-rt/str_ops.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,18 @@ PyObject *CPyStr_Split(PyObject *str, PyObject *sep, CPyTagged max_split)
5353
return PyUnicode_Split(str, sep, temp_max_split);
5454
}
5555

56+
bool CPyStr_Startswith(PyObject *self, PyObject *subobj) {
57+
Py_ssize_t start = 0;
58+
Py_ssize_t end = PyUnicode_GET_LENGTH(self);
59+
return PyUnicode_Tailmatch(self, subobj, start, end, -1);
60+
}
61+
62+
bool CPyStr_Endswith(PyObject *self, PyObject *subobj) {
63+
Py_ssize_t start = 0;
64+
Py_ssize_t end = PyUnicode_GET_LENGTH(self);
65+
return PyUnicode_Tailmatch(self, subobj, start, end, 1);
66+
}
67+
5668
/* This does a dodgy attempt to append in place */
5769
PyObject *CPyStr_Append(PyObject *o1, PyObject *o2) {
5870
PyUnicode_Append(&o1, o2);

mypyc/primitives/str_ops.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from mypyc.ir.ops import ERR_MAGIC, ERR_NEVER
66
from mypyc.ir.rtypes import (
77
RType, object_rprimitive, str_rprimitive, int_rprimitive, list_rprimitive,
8-
c_int_rprimitive, pointer_rprimitive
8+
c_int_rprimitive, pointer_rprimitive, bool_rprimitive
99
)
1010
from mypyc.primitives.registry import (
1111
c_method_op, c_binary_op, c_function_op,
@@ -43,6 +43,24 @@
4343
error_kind=ERR_MAGIC
4444
)
4545

46+
# str.startswith(str)
47+
c_method_op(
48+
name='startswith',
49+
arg_types=[str_rprimitive, str_rprimitive],
50+
return_type=bool_rprimitive,
51+
c_function_name='CPyStr_Startswith',
52+
error_kind=ERR_NEVER
53+
)
54+
55+
# str.endswith(str)
56+
c_method_op(
57+
name='endswith',
58+
arg_types=[str_rprimitive, str_rprimitive],
59+
return_type=bool_rprimitive,
60+
c_function_name='CPyStr_Endswith',
61+
error_kind=ERR_NEVER
62+
)
63+
4664
# str[index] (for an int index)
4765
c_method_op(
4866
name='__getitem__',

mypyc/test-data/fixtures/ir.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ def strip (self, item: str) -> str: pass
7373
def join(self, x: Iterable[str]) -> str: pass
7474
def format(self, *args: Any, **kwargs: Any) -> str: ...
7575
def upper(self) -> str: pass
76+
def startswith(self, x: str, start: int=..., end: int=...) -> bool: pass
77+
def endswith(self, x: str, start: int=..., end: int=...) -> bool: pass
7678

7779
class float:
7880
def __init__(self, x: object) -> None: pass

mypyc/test-data/run-strings.test

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Test cases for strings (compile and run)
22

33
[case testStr]
4+
from typing import Tuple
45
def f() -> str:
56
return 'some string'
67
def g() -> str:
@@ -17,9 +18,11 @@ def eq(x: str) -> int:
1718
elif x != 'bar':
1819
return 1
1920
return 2
21+
def match(x: str, y: str) -> Tuple[bool, bool]:
22+
return (x.startswith(y), x.endswith(y))
2023

2124
[file driver.py]
22-
from native import f, g, tostr, booltostr, concat, eq
25+
from native import f, g, tostr, booltostr, concat, eq, match
2326
assert f() == 'some string'
2427
assert g() == 'some\a \v \t \x7f " \n \0string 🐍'
2528
assert tostr(57) == '57'
@@ -32,6 +35,11 @@ assert eq('bar') == 2
3235

3336
assert int(tostr(0)) == 0
3437
assert int(tostr(20)) == 20
38+
assert match('', '') == (True, True)
39+
assert match('abc', '') == (True, True)
40+
assert match('abc', 'a') == (True, False)
41+
assert match('abc', 'c') == (False, True)
42+
assert match('', 'abc') == (False, False)
3543

3644
[case testStringOps]
3745
from typing import List, Optional

0 commit comments

Comments
 (0)