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