1 /* stringlib: fastsearch implementation */
2
3 #define STRINGLIB_FASTSEARCH_H
4
5 /* fast search/count implementation, based on a mix between boyer-
6 moore and horspool, with a few more bells and whistles on the top.
7 for some more background, see: http://effbot.org/zone/stringlib.htm */
8
9 /* note: fastsearch may access s[n], which isn't a problem when using
10 Python's ordinary string types, but may cause problems if you're
11 using this code in other contexts. also, the count mode returns -1
12 if there cannot possible be a match in the target string, and 0 if
13 it has actually checked for matches, but didn't find any. callers
14 beware! */
15
16 #define FAST_COUNT 0
17 #define FAST_SEARCH 1
18 #define FAST_RSEARCH 2
19
20 #if LONG_BIT >= 128
21 #define STRINGLIB_BLOOM_WIDTH 128
22 #elif LONG_BIT >= 64
23 #define STRINGLIB_BLOOM_WIDTH 64
24 #elif LONG_BIT >= 32
25 #define STRINGLIB_BLOOM_WIDTH 32
26 #else
27 #error "LONG_BIT is smaller than 32"
28 #endif
29
30 #define STRINGLIB_BLOOM_ADD(mask, ch) \
31 ((mask |= (1UL << ((ch) & (STRINGLIB_BLOOM_WIDTH -1)))))
32 #define STRINGLIB_BLOOM(mask, ch) \
33 ((mask & (1UL << ((ch) & (STRINGLIB_BLOOM_WIDTH -1)))))
34
35 #if STRINGLIB_SIZEOF_CHAR == 1
36 # define MEMCHR_CUT_OFF 15
37 #else
38 # define MEMCHR_CUT_OFF 40
39 #endif
40
41 Py_LOCAL_INLINE(Py_ssize_t)
STRINGLIB(find_char)42 STRINGLIB(find_char)(const STRINGLIB_CHAR* s, Py_ssize_t n, STRINGLIB_CHAR ch)
43 {
44 const STRINGLIB_CHAR *p, *e;
45
46 p = s;
47 e = s + n;
48 if (n > MEMCHR_CUT_OFF) {
49 #if STRINGLIB_SIZEOF_CHAR == 1
50 p = memchr(s, ch, n);
51 if (p != NULL)
52 return (p - s);
53 return -1;
54 #else
55 /* use memchr if we can choose a needle without too many likely
56 false positives */
57 const STRINGLIB_CHAR *s1, *e1;
58 unsigned char needle = ch & 0xff;
59 /* If looking for a multiple of 256, we'd have too
60 many false positives looking for the '\0' byte in UCS2
61 and UCS4 representations. */
62 if (needle != 0) {
63 do {
64 void *candidate = memchr(p, needle,
65 (e - p) * sizeof(STRINGLIB_CHAR));
66 if (candidate == NULL)
67 return -1;
68 s1 = p;
69 p = (const STRINGLIB_CHAR *)
70 _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
71 if (*p == ch)
72 return (p - s);
73 /* False positive */
74 p++;
75 if (p - s1 > MEMCHR_CUT_OFF)
76 continue;
77 if (e - p <= MEMCHR_CUT_OFF)
78 break;
79 e1 = p + MEMCHR_CUT_OFF;
80 while (p != e1) {
81 if (*p == ch)
82 return (p - s);
83 p++;
84 }
85 }
86 while (e - p > MEMCHR_CUT_OFF);
87 }
88 #endif
89 }
90 while (p < e) {
91 if (*p == ch)
92 return (p - s);
93 p++;
94 }
95 return -1;
96 }
97
98 Py_LOCAL_INLINE(Py_ssize_t)
STRINGLIB(rfind_char)99 STRINGLIB(rfind_char)(const STRINGLIB_CHAR* s, Py_ssize_t n, STRINGLIB_CHAR ch)
100 {
101 const STRINGLIB_CHAR *p;
102 #ifdef HAVE_MEMRCHR
103 /* memrchr() is a GNU extension, available since glibc 2.1.91.
104 it doesn't seem as optimized as memchr(), but is still quite
105 faster than our hand-written loop below */
106
107 if (n > MEMCHR_CUT_OFF) {
108 #if STRINGLIB_SIZEOF_CHAR == 1
109 p = memrchr(s, ch, n);
110 if (p != NULL)
111 return (p - s);
112 return -1;
113 #else
114 /* use memrchr if we can choose a needle without too many likely
115 false positives */
116 const STRINGLIB_CHAR *s1;
117 Py_ssize_t n1;
118 unsigned char needle = ch & 0xff;
119 /* If looking for a multiple of 256, we'd have too
120 many false positives looking for the '\0' byte in UCS2
121 and UCS4 representations. */
122 if (needle != 0) {
123 do {
124 void *candidate = memrchr(s, needle,
125 n * sizeof(STRINGLIB_CHAR));
126 if (candidate == NULL)
127 return -1;
128 n1 = n;
129 p = (const STRINGLIB_CHAR *)
130 _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
131 n = p - s;
132 if (*p == ch)
133 return n;
134 /* False positive */
135 if (n1 - n > MEMCHR_CUT_OFF)
136 continue;
137 if (n <= MEMCHR_CUT_OFF)
138 break;
139 s1 = p - MEMCHR_CUT_OFF;
140 while (p > s1) {
141 p--;
142 if (*p == ch)
143 return (p - s);
144 }
145 n = p - s;
146 }
147 while (n > MEMCHR_CUT_OFF);
148 }
149 #endif
150 }
151 #endif /* HAVE_MEMRCHR */
152 p = s + n;
153 while (p > s) {
154 p--;
155 if (*p == ch)
156 return (p - s);
157 }
158 return -1;
159 }
160
161 #undef MEMCHR_CUT_OFF
162
163 Py_LOCAL_INLINE(Py_ssize_t)
FASTSEARCH(const STRINGLIB_CHAR * s,Py_ssize_t n,const STRINGLIB_CHAR * p,Py_ssize_t m,Py_ssize_t maxcount,int mode)164 FASTSEARCH(const STRINGLIB_CHAR* s, Py_ssize_t n,
165 const STRINGLIB_CHAR* p, Py_ssize_t m,
166 Py_ssize_t maxcount, int mode)
167 {
168 unsigned long mask;
169 Py_ssize_t skip, count = 0;
170 Py_ssize_t i, j, mlast, w;
171
172 w = n - m;
173
174 if (w < 0 || (mode == FAST_COUNT && maxcount == 0))
175 return -1;
176
177 /* look for special cases */
178 if (m <= 1) {
179 if (m <= 0)
180 return -1;
181 /* use special case for 1-character strings */
182 if (mode == FAST_SEARCH)
183 return STRINGLIB(find_char)(s, n, p[0]);
184 else if (mode == FAST_RSEARCH)
185 return STRINGLIB(rfind_char)(s, n, p[0]);
186 else { /* FAST_COUNT */
187 for (i = 0; i < n; i++)
188 if (s[i] == p[0]) {
189 count++;
190 if (count == maxcount)
191 return maxcount;
192 }
193 return count;
194 }
195 }
196
197 mlast = m - 1;
198 skip = mlast - 1;
199 mask = 0;
200
201 if (mode != FAST_RSEARCH) {
202 const STRINGLIB_CHAR *ss = s + m - 1;
203 const STRINGLIB_CHAR *pp = p + m - 1;
204
205 /* create compressed boyer-moore delta 1 table */
206
207 /* process pattern[:-1] */
208 for (i = 0; i < mlast; i++) {
209 STRINGLIB_BLOOM_ADD(mask, p[i]);
210 if (p[i] == p[mlast])
211 skip = mlast - i - 1;
212 }
213 /* process pattern[-1] outside the loop */
214 STRINGLIB_BLOOM_ADD(mask, p[mlast]);
215
216 for (i = 0; i <= w; i++) {
217 /* note: using mlast in the skip path slows things down on x86 */
218 if (ss[i] == pp[0]) {
219 /* candidate match */
220 for (j = 0; j < mlast; j++)
221 if (s[i+j] != p[j])
222 break;
223 if (j == mlast) {
224 /* got a match! */
225 if (mode != FAST_COUNT)
226 return i;
227 count++;
228 if (count == maxcount)
229 return maxcount;
230 i = i + mlast;
231 continue;
232 }
233 /* miss: check if next character is part of pattern */
234 if (!STRINGLIB_BLOOM(mask, ss[i+1]))
235 i = i + m;
236 else
237 i = i + skip;
238 } else {
239 /* skip: check if next character is part of pattern */
240 if (!STRINGLIB_BLOOM(mask, ss[i+1]))
241 i = i + m;
242 }
243 }
244 } else { /* FAST_RSEARCH */
245
246 /* create compressed boyer-moore delta 1 table */
247
248 /* process pattern[0] outside the loop */
249 STRINGLIB_BLOOM_ADD(mask, p[0]);
250 /* process pattern[:0:-1] */
251 for (i = mlast; i > 0; i--) {
252 STRINGLIB_BLOOM_ADD(mask, p[i]);
253 if (p[i] == p[0])
254 skip = i - 1;
255 }
256
257 for (i = w; i >= 0; i--) {
258 if (s[i] == p[0]) {
259 /* candidate match */
260 for (j = mlast; j > 0; j--)
261 if (s[i+j] != p[j])
262 break;
263 if (j == 0)
264 /* got a match! */
265 return i;
266 /* miss: check if previous character is part of pattern */
267 if (i > 0 && !STRINGLIB_BLOOM(mask, s[i-1]))
268 i = i - m;
269 else
270 i = i - skip;
271 } else {
272 /* skip: check if previous character is part of pattern */
273 if (i > 0 && !STRINGLIB_BLOOM(mask, s[i-1]))
274 i = i - m;
275 }
276 }
277 }
278
279 if (mode != FAST_COUNT)
280 return -1;
281 return count;
282 }
283
284