1/* stringlib: find/index implementation */
2
3#ifndef STRINGLIB_FIND_H
4#define STRINGLIB_FIND_H
5
6#ifndef STRINGLIB_FASTSEARCH_H
7#error must include "stringlib/fastsearch.h" before including this module
8#endif
9
10Py_LOCAL_INLINE(Py_ssize_t)
11stringlib_find(const STRINGLIB_CHAR* str, Py_ssize_t str_len,
12               const STRINGLIB_CHAR* sub, Py_ssize_t sub_len,
13               Py_ssize_t offset)
14{
15    Py_ssize_t pos;
16
17    if (str_len < 0)
18        return -1;
19    if (sub_len == 0)
20        return offset;
21
22    pos = fastsearch(str, str_len, sub, sub_len, -1, FAST_SEARCH);
23
24    if (pos >= 0)
25        pos += offset;
26
27    return pos;
28}
29
30Py_LOCAL_INLINE(Py_ssize_t)
31stringlib_rfind(const STRINGLIB_CHAR* str, Py_ssize_t str_len,
32                const STRINGLIB_CHAR* sub, Py_ssize_t sub_len,
33                Py_ssize_t offset)
34{
35    Py_ssize_t pos;
36
37    if (str_len < 0)
38        return -1;
39    if (sub_len == 0)
40        return str_len + offset;
41
42    pos = fastsearch(str, str_len, sub, sub_len, -1, FAST_RSEARCH);
43
44    if (pos >= 0)
45        pos += offset;
46
47    return pos;
48}
49
50/* helper macro to fixup start/end slice values */
51#define ADJUST_INDICES(start, end, len)         \
52    if (end > len)                              \
53        end = len;                              \
54    else if (end < 0) {                         \
55        end += len;                             \
56        if (end < 0)                            \
57            end = 0;                            \
58    }                                           \
59    if (start < 0) {                            \
60        start += len;                           \
61        if (start < 0)                          \
62            start = 0;                          \
63    }
64
65Py_LOCAL_INLINE(Py_ssize_t)
66stringlib_find_slice(const STRINGLIB_CHAR* str, Py_ssize_t str_len,
67                     const STRINGLIB_CHAR* sub, Py_ssize_t sub_len,
68                     Py_ssize_t start, Py_ssize_t end)
69{
70    ADJUST_INDICES(start, end, str_len);
71    return stringlib_find(str + start, end - start, sub, sub_len, start);
72}
73
74Py_LOCAL_INLINE(Py_ssize_t)
75stringlib_rfind_slice(const STRINGLIB_CHAR* str, Py_ssize_t str_len,
76                      const STRINGLIB_CHAR* sub, Py_ssize_t sub_len,
77                      Py_ssize_t start, Py_ssize_t end)
78{
79    ADJUST_INDICES(start, end, str_len);
80    return stringlib_rfind(str + start, end - start, sub, sub_len, start);
81}
82
83#ifdef STRINGLIB_WANT_CONTAINS_OBJ
84
85Py_LOCAL_INLINE(int)
86stringlib_contains_obj(PyObject* str, PyObject* sub)
87{
88    return stringlib_find(
89        STRINGLIB_STR(str), STRINGLIB_LEN(str),
90        STRINGLIB_STR(sub), STRINGLIB_LEN(sub), 0
91        ) != -1;
92}
93
94#endif /* STRINGLIB_WANT_CONTAINS_OBJ */
95
96/*
97This function is a helper for the "find" family (find, rfind, index,
98rindex) and for count, startswith and endswith, because they all have
99the same behaviour for the arguments.
100
101It does not touch the variables received until it knows everything
102is ok.
103*/
104
105#define FORMAT_BUFFER_SIZE 50
106
107Py_LOCAL_INLINE(int)
108stringlib_parse_args_finds(const char * function_name, PyObject *args,
109                           PyObject **subobj,
110                           Py_ssize_t *start, Py_ssize_t *end)
111{
112    PyObject *tmp_subobj;
113    Py_ssize_t tmp_start = 0;
114    Py_ssize_t tmp_end = PY_SSIZE_T_MAX;
115    PyObject *obj_start=Py_None, *obj_end=Py_None;
116    char format[FORMAT_BUFFER_SIZE] = "O|OO:";
117    size_t len = strlen(format);
118
119    strncpy(format + len, function_name, FORMAT_BUFFER_SIZE - len - 1);
120    format[FORMAT_BUFFER_SIZE - 1] = '\0';
121
122    if (!PyArg_ParseTuple(args, format, &tmp_subobj, &obj_start, &obj_end))
123        return 0;
124
125    /* To support None in "start" and "end" arguments, meaning
126       the same as if they were not passed.
127    */
128    if (obj_start != Py_None)
129        if (!_PyEval_SliceIndex(obj_start, &tmp_start))
130            return 0;
131    if (obj_end != Py_None)
132        if (!_PyEval_SliceIndex(obj_end, &tmp_end))
133            return 0;
134
135    *start = tmp_start;
136    *end = tmp_end;
137    *subobj = tmp_subobj;
138    return 1;
139}
140
141#undef FORMAT_BUFFER_SIZE
142
143#if STRINGLIB_IS_UNICODE
144
145/*
146Wraps stringlib_parse_args_finds() and additionally ensures that the
147first argument is a unicode object.
148
149Note that we receive a pointer to the pointer of the substring object,
150so when we create that object in this function we don't DECREF it,
151because it continues living in the caller functions (those functions,
152after finishing using the substring, must DECREF it).
153*/
154
155Py_LOCAL_INLINE(int)
156stringlib_parse_args_finds_unicode(const char * function_name, PyObject *args,
157                                   PyUnicodeObject **substring,
158                                   Py_ssize_t *start, Py_ssize_t *end)
159{
160    PyObject *tmp_substring;
161
162    if(stringlib_parse_args_finds(function_name, args, &tmp_substring,
163                                  start, end)) {
164        tmp_substring = PyUnicode_FromObject(tmp_substring);
165        if (!tmp_substring)
166            return 0;
167        *substring = (PyUnicodeObject *)tmp_substring;
168        return 1;
169    }
170    return 0;
171}
172
173#endif /* STRINGLIB_IS_UNICODE */
174
175#endif /* STRINGLIB_FIND_H */
176