blob: a8a51d577f3f7ce70b68f83a8fc5ab2089541955 [file] [log] [blame]
Thomas Wouters477c8d52006-05-27 19:21:47 +00001/* stringlib: fastsearch implementation */
2
Thomas Wouters477c8d52006-05-27 19:21:47 +00003#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.
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +00007 for some more background, see: http://effbot.org/zone/stringlib.htm */
Thomas Wouters477c8d52006-05-27 19:21:47 +00008
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
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +000018#define FAST_RSEARCH 2
Thomas Wouters477c8d52006-05-27 19:21:47 +000019
Antoine Pitrouf068f942010-01-13 14:19:12 +000020#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)))))
Antoine Pitrouf2c54842010-01-13 08:07:53 +000034
Serhiy Storchaka0a58f722017-03-30 09:11:10 +030035#if STRINGLIB_SIZEOF_CHAR == 1
36# define MEMCHR_CUT_OFF 15
37#else
38# define MEMCHR_CUT_OFF 40
39#endif
40
Serhiy Storchaka413fdce2015-11-14 15:42:17 +020041Py_LOCAL_INLINE(Py_ssize_t)
42STRINGLIB(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;
Serhiy Storchaka0a58f722017-03-30 09:11:10 +030048 if (n > MEMCHR_CUT_OFF) {
Serhiy Storchaka413fdce2015-11-14 15:42:17 +020049#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 two many likely
56 false positives */
Serhiy Storchaka0a58f722017-03-30 09:11:10 +030057 const STRINGLIB_CHAR *s1, *e1;
Serhiy Storchaka413fdce2015-11-14 15:42:17 +020058 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) {
Serhiy Storchaka0a58f722017-03-30 09:11:10 +030063 do {
Serhiy Storchaka413fdce2015-11-14 15:42:17 +020064 void *candidate = memchr(p, needle,
65 (e - p) * sizeof(STRINGLIB_CHAR));
66 if (candidate == NULL)
67 return -1;
Serhiy Storchaka0a58f722017-03-30 09:11:10 +030068 s1 = p;
Serhiy Storchaka413fdce2015-11-14 15:42:17 +020069 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++;
Serhiy Storchaka0a58f722017-03-30 09:11:10 +030075 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 }
Serhiy Storchaka413fdce2015-11-14 15:42:17 +020085 }
Serhiy Storchaka0a58f722017-03-30 09:11:10 +030086 while (e - p > MEMCHR_CUT_OFF);
Serhiy Storchaka413fdce2015-11-14 15:42:17 +020087 }
88#endif
89 }
90 while (p < e) {
91 if (*p == ch)
92 return (p - s);
93 p++;
94 }
95 return -1;
96}
Antoine Pitrou2c3b2302011-10-11 20:29:21 +020097
98Py_LOCAL_INLINE(Py_ssize_t)
Serhiy Storchaka413fdce2015-11-14 15:42:17 +020099STRINGLIB(rfind_char)(const STRINGLIB_CHAR* s, Py_ssize_t n, STRINGLIB_CHAR ch)
Antoine Pitrou2c3b2302011-10-11 20:29:21 +0200100{
Serhiy Storchaka413fdce2015-11-14 15:42:17 +0200101 const STRINGLIB_CHAR *p;
Antoine Pitrou2c3b2302011-10-11 20:29:21 +0200102#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
Serhiy Storchaka413fdce2015-11-14 15:42:17 +0200105 faster than our hand-written loop below */
Antoine Pitrou2c3b2302011-10-11 20:29:21 +0200106
Serhiy Storchaka0a58f722017-03-30 09:11:10 +0300107 if (n > MEMCHR_CUT_OFF) {
Serhiy Storchaka413fdce2015-11-14 15:42:17 +0200108#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 two many likely
115 false positives */
Serhiy Storchaka0a58f722017-03-30 09:11:10 +0300116 const STRINGLIB_CHAR *s1;
117 Py_ssize_t n1;
Serhiy Storchaka413fdce2015-11-14 15:42:17 +0200118 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) {
Serhiy Storchaka0a58f722017-03-30 09:11:10 +0300123 do {
Serhiy Storchaka413fdce2015-11-14 15:42:17 +0200124 void *candidate = memrchr(s, needle,
125 n * sizeof(STRINGLIB_CHAR));
126 if (candidate == NULL)
127 return -1;
Serhiy Storchaka0a58f722017-03-30 09:11:10 +0300128 n1 = n;
Serhiy Storchaka413fdce2015-11-14 15:42:17 +0200129 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 */
Serhiy Storchaka0a58f722017-03-30 09:11:10 +0300135 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;
Serhiy Storchaka413fdce2015-11-14 15:42:17 +0200146 }
Serhiy Storchaka0a58f722017-03-30 09:11:10 +0300147 while (n > MEMCHR_CUT_OFF);
Serhiy Storchaka413fdce2015-11-14 15:42:17 +0200148 }
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;
Antoine Pitrou2c3b2302011-10-11 20:29:21 +0200159}
160
Serhiy Storchaka0a58f722017-03-30 09:11:10 +0300161#undef MEMCHR_CUT_OFF
162
Thomas Wouters477c8d52006-05-27 19:21:47 +0000163Py_LOCAL_INLINE(Py_ssize_t)
Martin v. Löwisd63a3b82011-09-28 07:41:54 +0200164FASTSEARCH(const STRINGLIB_CHAR* s, Py_ssize_t n,
Thomas Wouters477c8d52006-05-27 19:21:47 +0000165 const STRINGLIB_CHAR* p, Py_ssize_t m,
Antoine Pitrouf2c54842010-01-13 08:07:53 +0000166 Py_ssize_t maxcount, int mode)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000167{
Antoine Pitrouf068f942010-01-13 14:19:12 +0000168 unsigned long mask;
Thomas Wouters477c8d52006-05-27 19:21:47 +0000169 Py_ssize_t skip, count = 0;
170 Py_ssize_t i, j, mlast, w;
171
172 w = n - m;
173
Antoine Pitrouf2c54842010-01-13 08:07:53 +0000174 if (w < 0 || (mode == FAST_COUNT && maxcount == 0))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000175 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 */
Serhiy Storchaka413fdce2015-11-14 15:42:17 +0200182 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 */
Thomas Wouters477c8d52006-05-27 19:21:47 +0000187 for (i = 0; i < n; i++)
Antoine Pitrouf2c54842010-01-13 08:07:53 +0000188 if (s[i] == p[0]) {
Thomas Wouters477c8d52006-05-27 19:21:47 +0000189 count++;
Antoine Pitrouf2c54842010-01-13 08:07:53 +0000190 if (count == maxcount)
191 return maxcount;
192 }
Thomas Wouters477c8d52006-05-27 19:21:47 +0000193 return count;
Thomas Wouters477c8d52006-05-27 19:21:47 +0000194 }
195 return -1;
196 }
197
198 mlast = m - 1;
Thomas Wouters477c8d52006-05-27 19:21:47 +0000199 skip = mlast - 1;
Antoine Pitrouf2c54842010-01-13 08:07:53 +0000200 mask = 0;
Thomas Wouters477c8d52006-05-27 19:21:47 +0000201
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000202 if (mode != FAST_RSEARCH) {
Victor Stinner7efa3b82013-04-08 00:26:43 +0200203 const STRINGLIB_CHAR *ss = s + m - 1;
204 const STRINGLIB_CHAR *pp = p + m - 1;
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000205
206 /* create compressed boyer-moore delta 1 table */
207
208 /* process pattern[:-1] */
Antoine Pitrouf2c54842010-01-13 08:07:53 +0000209 for (i = 0; i < mlast; i++) {
Antoine Pitrouf068f942010-01-13 14:19:12 +0000210 STRINGLIB_BLOOM_ADD(mask, p[i]);
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000211 if (p[i] == p[mlast])
212 skip = mlast - i - 1;
213 }
214 /* process pattern[-1] outside the loop */
Antoine Pitrouf068f942010-01-13 14:19:12 +0000215 STRINGLIB_BLOOM_ADD(mask, p[mlast]);
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000216
217 for (i = 0; i <= w; i++) {
218 /* note: using mlast in the skip path slows things down on x86 */
Victor Stinner7efa3b82013-04-08 00:26:43 +0200219 if (ss[i] == pp[0]) {
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000220 /* candidate match */
221 for (j = 0; j < mlast; j++)
222 if (s[i+j] != p[j])
223 break;
224 if (j == mlast) {
225 /* got a match! */
226 if (mode != FAST_COUNT)
227 return i;
228 count++;
Antoine Pitrouf2c54842010-01-13 08:07:53 +0000229 if (count == maxcount)
230 return maxcount;
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000231 i = i + mlast;
232 continue;
233 }
234 /* miss: check if next character is part of pattern */
Victor Stinner7efa3b82013-04-08 00:26:43 +0200235 if (!STRINGLIB_BLOOM(mask, ss[i+1]))
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000236 i = i + m;
237 else
238 i = i + skip;
239 } else {
240 /* skip: check if next character is part of pattern */
Victor Stinner7efa3b82013-04-08 00:26:43 +0200241 if (!STRINGLIB_BLOOM(mask, ss[i+1]))
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000242 i = i + m;
Thomas Wouters477c8d52006-05-27 19:21:47 +0000243 }
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000244 }
245 } else { /* FAST_RSEARCH */
246
247 /* create compressed boyer-moore delta 1 table */
248
249 /* process pattern[0] outside the loop */
Antoine Pitrouf068f942010-01-13 14:19:12 +0000250 STRINGLIB_BLOOM_ADD(mask, p[0]);
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000251 /* process pattern[:0:-1] */
252 for (i = mlast; i > 0; i--) {
Antoine Pitrouf068f942010-01-13 14:19:12 +0000253 STRINGLIB_BLOOM_ADD(mask, p[i]);
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000254 if (p[i] == p[0])
255 skip = i - 1;
256 }
257
258 for (i = w; i >= 0; i--) {
259 if (s[i] == p[0]) {
260 /* candidate match */
261 for (j = mlast; j > 0; j--)
262 if (s[i+j] != p[j])
263 break;
264 if (j == 0)
265 /* got a match! */
266 return i;
267 /* miss: check if previous character is part of pattern */
Florent Xiclunaeb6f3ea2010-08-08 22:07:16 +0000268 if (i > 0 && !STRINGLIB_BLOOM(mask, s[i-1]))
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000269 i = i - m;
270 else
271 i = i - skip;
272 } else {
273 /* skip: check if previous character is part of pattern */
Florent Xiclunaeb6f3ea2010-08-08 22:07:16 +0000274 if (i > 0 && !STRINGLIB_BLOOM(mask, s[i-1]))
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000275 i = i - m;
276 }
Thomas Wouters477c8d52006-05-27 19:21:47 +0000277 }
278 }
279
280 if (mode != FAST_COUNT)
281 return -1;
282 return count;
283}
284