Home | History | Annotate | Download | only in stringlib
      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)
     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)
     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)
     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)
     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)
     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)
    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)
    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