Skip to content

[mypyc] optimise startswith and endswith #9557

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions mypyc/lib-rt/str_ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,18 @@ PyObject *CPyStr_Split(PyObject *str, PyObject *sep, CPyTagged max_split)
return PyUnicode_Split(str, sep, temp_max_split);
}

bool CPyStr_Startswith(PyObject *self, PyObject *subobj) {
Py_ssize_t start = 0;
Py_ssize_t end = PyUnicode_GET_LENGTH(self);
return PyUnicode_Tailmatch(self, subobj, start, end, -1);
}

bool CPyStr_Endswith(PyObject *self, PyObject *subobj) {
Py_ssize_t start = 0;
Py_ssize_t end = PyUnicode_GET_LENGTH(self);
return PyUnicode_Tailmatch(self, subobj, start, end, 1);
}

/* This does a dodgy attempt to append in place */
PyObject *CPyStr_Append(PyObject *o1, PyObject *o2) {
PyUnicode_Append(&o1, o2);
Expand Down
20 changes: 19 additions & 1 deletion mypyc/primitives/str_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from mypyc.ir.ops import ERR_MAGIC, ERR_NEVER
from mypyc.ir.rtypes import (
RType, object_rprimitive, str_rprimitive, int_rprimitive, list_rprimitive,
c_int_rprimitive, pointer_rprimitive
c_int_rprimitive, pointer_rprimitive, bool_rprimitive
)
from mypyc.primitives.registry import (
c_method_op, c_binary_op, c_function_op,
Expand Down Expand Up @@ -43,6 +43,24 @@
error_kind=ERR_MAGIC
)

# str.startswith(str)
c_method_op(
name='startswith',
arg_types=[str_rprimitive, str_rprimitive],
return_type=bool_rprimitive,
c_function_name='CPyStr_Startswith',
error_kind=ERR_NEVER
)

# str.endswith(str)
c_method_op(
name='endswith',
arg_types=[str_rprimitive, str_rprimitive],
return_type=bool_rprimitive,
c_function_name='CPyStr_Endswith',
error_kind=ERR_NEVER
)

# str[index] (for an int index)
c_method_op(
name='__getitem__',
Expand Down
2 changes: 2 additions & 0 deletions mypyc/test-data/fixtures/ir.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ def strip (self, item: str) -> str: pass
def join(self, x: Iterable[str]) -> str: pass
def format(self, *args: Any, **kwargs: Any) -> str: ...
def upper(self) -> str: pass
def startswith(self, x: str, start: int=..., end: int=...) -> bool: pass
def endswith(self, x: str, start: int=..., end: int=...) -> bool: pass

class float:
def __init__(self, x: object) -> None: pass
Expand Down
10 changes: 9 additions & 1 deletion mypyc/test-data/run-strings.test
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Test cases for strings (compile and run)

[case testStr]
from typing import Tuple
def f() -> str:
return 'some string'
def g() -> str:
Expand All @@ -17,9 +18,11 @@ def eq(x: str) -> int:
elif x != 'bar':
return 1
return 2
def match(x: str, y: str) -> Tuple[bool, bool]:
return (x.startswith(y), x.endswith(y))

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

assert int(tostr(0)) == 0
assert int(tostr(20)) == 20
assert match('', '') == (True, True)
assert match('abc', '') == (True, True)
assert match('abc', 'a') == (True, False)
assert match('abc', 'c') == (False, True)
assert match('', 'abc') == (False, False)

[case testStringOps]
from typing import List, Optional
Expand Down