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 
10 Py_LOCAL_INLINE(Py_ssize_t)
stringlib_find(const STRINGLIB_CHAR * str,Py_ssize_t str_len,const STRINGLIB_CHAR * sub,Py_ssize_t sub_len,Py_ssize_t offset)11 stringlib_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 
30 Py_LOCAL_INLINE(Py_ssize_t)
stringlib_rfind(const STRINGLIB_CHAR * str,Py_ssize_t str_len,const STRINGLIB_CHAR * sub,Py_ssize_t sub_len,Py_ssize_t offset)31 stringlib_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 
65 Py_LOCAL_INLINE(Py_ssize_t)
stringlib_find_slice(const STRINGLIB_CHAR * str,Py_ssize_t str_len,const STRINGLIB_CHAR * sub,Py_ssize_t sub_len,Py_ssize_t start,Py_ssize_t end)66 stringlib_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 
74 Py_LOCAL_INLINE(Py_ssize_t)
stringlib_rfind_slice(const STRINGLIB_CHAR * str,Py_ssize_t str_len,const STRINGLIB_CHAR * sub,Py_ssize_t sub_len,Py_ssize_t start,Py_ssize_t end)75 stringlib_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 
85 Py_LOCAL_INLINE(int)
stringlib_contains_obj(PyObject * str,PyObject * sub)86 stringlib_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 /*
97 This function is a helper for the "find" family (find, rfind, index,
98 rindex) and for count, startswith and endswith, because they all have
99 the same behaviour for the arguments.
100 
101 It does not touch the variables received until it knows everything
102 is ok.
103 */
104 
105 #define FORMAT_BUFFER_SIZE 50
106 
107 Py_LOCAL_INLINE(int)
stringlib_parse_args_finds(const char * function_name,PyObject * args,PyObject ** subobj,Py_ssize_t * start,Py_ssize_t * end)108 stringlib_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 /*
146 Wraps stringlib_parse_args_finds() and additionally ensures that the
147 first argument is a unicode object.
148 
149 Note that we receive a pointer to the pointer of the substring object,
150 so when we create that object in this function we don't DECREF it,
151 because it continues living in the caller functions (those functions,
152 after finishing using the substring, must DECREF it).
153 */
154 
155 Py_LOCAL_INLINE(int)
stringlib_parse_args_finds_unicode(const char * function_name,PyObject * args,PyUnicodeObject ** substring,Py_ssize_t * start,Py_ssize_t * end)156 stringlib_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