blob: cd7cac40fa4abe7fe3b14f0546e634e6f94667f5 [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
Antoine Pitrou2c3b2302011-10-11 20:29:21 +020035
36Py_LOCAL_INLINE(Py_ssize_t)
37STRINGLIB(fastsearch_memchr_1char)(const STRINGLIB_CHAR* s, Py_ssize_t n,
38 STRINGLIB_CHAR ch, unsigned char needle,
39 Py_ssize_t maxcount, int mode)
40{
Antoine Pitrou2c3b2302011-10-11 20:29:21 +020041 if (mode == FAST_SEARCH) {
Victor Stinnerb3f55012012-08-02 23:05:01 +020042 const STRINGLIB_CHAR *ptr = s;
Antoine Pitrou2c3b2302011-10-11 20:29:21 +020043 const STRINGLIB_CHAR *e = s + n;
Victor Stinnerb3f55012012-08-02 23:05:01 +020044 while (ptr < e) {
Serhiy Storchaka18ba40b2013-01-15 13:27:28 +020045 void *candidate = memchr((const void *) ptr, needle, (e - ptr) * sizeof(STRINGLIB_CHAR));
46 if (candidate == NULL)
Antoine Pitrou2c3b2302011-10-11 20:29:21 +020047 return -1;
Serhiy Storchaka18ba40b2013-01-15 13:27:28 +020048 ptr = (const STRINGLIB_CHAR *) _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
49 if (sizeof(STRINGLIB_CHAR) == 1 || *ptr == ch)
50 return (ptr - s);
Antoine Pitrou2c3b2302011-10-11 20:29:21 +020051 /* False positive */
Serhiy Storchaka18ba40b2013-01-15 13:27:28 +020052 ptr++;
Antoine Pitrou2c3b2302011-10-11 20:29:21 +020053 }
54 return -1;
55 }
56#ifdef HAVE_MEMRCHR
57 /* memrchr() is a GNU extension, available since glibc 2.1.91.
58 it doesn't seem as optimized as memchr(), but is still quite
59 faster than our hand-written loop in FASTSEARCH below */
60 else if (mode == FAST_RSEARCH) {
61 while (n > 0) {
Serhiy Storchaka18ba40b2013-01-15 13:27:28 +020062 const STRINGLIB_CHAR *found;
63 void *candidate = memrchr((const void *) s, needle, n * sizeof(STRINGLIB_CHAR));
64 if (candidate == NULL)
Antoine Pitrou2c3b2302011-10-11 20:29:21 +020065 return -1;
Serhiy Storchaka18ba40b2013-01-15 13:27:28 +020066 found = (const STRINGLIB_CHAR *) _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR));
Antoine Pitrou2c3b2302011-10-11 20:29:21 +020067 n = found - s;
68 if (sizeof(STRINGLIB_CHAR) == 1 || *found == ch)
69 return n;
70 /* False positive */
71 }
72 return -1;
73 }
74#endif
75 else {
76 assert(0); /* Should never get here */
77 return 0;
78 }
79
80#undef DO_MEMCHR
81}
82
Thomas Wouters477c8d52006-05-27 19:21:47 +000083Py_LOCAL_INLINE(Py_ssize_t)
Martin v. Löwisd63a3b82011-09-28 07:41:54 +020084FASTSEARCH(const STRINGLIB_CHAR* s, Py_ssize_t n,
Thomas Wouters477c8d52006-05-27 19:21:47 +000085 const STRINGLIB_CHAR* p, Py_ssize_t m,
Antoine Pitrouf2c54842010-01-13 08:07:53 +000086 Py_ssize_t maxcount, int mode)
Thomas Wouters477c8d52006-05-27 19:21:47 +000087{
Antoine Pitrouf068f942010-01-13 14:19:12 +000088 unsigned long mask;
Thomas Wouters477c8d52006-05-27 19:21:47 +000089 Py_ssize_t skip, count = 0;
90 Py_ssize_t i, j, mlast, w;
91
92 w = n - m;
93
Antoine Pitrouf2c54842010-01-13 08:07:53 +000094 if (w < 0 || (mode == FAST_COUNT && maxcount == 0))
Thomas Wouters477c8d52006-05-27 19:21:47 +000095 return -1;
96
97 /* look for special cases */
98 if (m <= 1) {
99 if (m <= 0)
100 return -1;
101 /* use special case for 1-character strings */
Antoine Pitrou2c3b2302011-10-11 20:29:21 +0200102 if (n > 10 && (mode == FAST_SEARCH
103#ifdef HAVE_MEMRCHR
104 || mode == FAST_RSEARCH
105#endif
106 )) {
107 /* use memchr if we can choose a needle without two many likely
108 false positives */
109 unsigned char needle;
Antoine Pitrou2c3b2302011-10-11 20:29:21 +0200110 needle = p[0] & 0xff;
Victor Stinner8cc70dc2011-10-11 23:22:22 +0200111#if STRINGLIB_SIZEOF_CHAR > 1
Antoine Pitrou5b9f4c12011-10-17 19:21:04 +0200112 /* If looking for a multiple of 256, we'd have too
Antoine Pitrouc198d052011-10-13 18:07:37 +0200113 many false positives looking for the '\0' byte in UCS2
114 and UCS4 representations. */
Antoine Pitroudda339e2011-10-13 17:58:11 +0200115 if (needle != 0)
Victor Stinner8cc70dc2011-10-11 23:22:22 +0200116#endif
Antoine Pitrou2c3b2302011-10-11 20:29:21 +0200117 return STRINGLIB(fastsearch_memchr_1char)
118 (s, n, p[0], needle, maxcount, mode);
119 }
Thomas Wouters477c8d52006-05-27 19:21:47 +0000120 if (mode == FAST_COUNT) {
121 for (i = 0; i < n; i++)
Antoine Pitrouf2c54842010-01-13 08:07:53 +0000122 if (s[i] == p[0]) {
Thomas Wouters477c8d52006-05-27 19:21:47 +0000123 count++;
Antoine Pitrouf2c54842010-01-13 08:07:53 +0000124 if (count == maxcount)
125 return maxcount;
126 }
Thomas Wouters477c8d52006-05-27 19:21:47 +0000127 return count;
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000128 } else if (mode == FAST_SEARCH) {
Thomas Wouters477c8d52006-05-27 19:21:47 +0000129 for (i = 0; i < n; i++)
130 if (s[i] == p[0])
131 return i;
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000132 } else { /* FAST_RSEARCH */
133 for (i = n - 1; i > -1; i--)
134 if (s[i] == p[0])
135 return i;
Thomas Wouters477c8d52006-05-27 19:21:47 +0000136 }
137 return -1;
138 }
139
140 mlast = m - 1;
Thomas Wouters477c8d52006-05-27 19:21:47 +0000141 skip = mlast - 1;
Antoine Pitrouf2c54842010-01-13 08:07:53 +0000142 mask = 0;
Thomas Wouters477c8d52006-05-27 19:21:47 +0000143
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000144 if (mode != FAST_RSEARCH) {
Victor Stinner7efa3b82013-04-08 00:26:43 +0200145 const STRINGLIB_CHAR *ss = s + m - 1;
146 const STRINGLIB_CHAR *pp = p + m - 1;
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000147
148 /* create compressed boyer-moore delta 1 table */
149
150 /* process pattern[:-1] */
Antoine Pitrouf2c54842010-01-13 08:07:53 +0000151 for (i = 0; i < mlast; i++) {
Antoine Pitrouf068f942010-01-13 14:19:12 +0000152 STRINGLIB_BLOOM_ADD(mask, p[i]);
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000153 if (p[i] == p[mlast])
154 skip = mlast - i - 1;
155 }
156 /* process pattern[-1] outside the loop */
Antoine Pitrouf068f942010-01-13 14:19:12 +0000157 STRINGLIB_BLOOM_ADD(mask, p[mlast]);
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000158
159 for (i = 0; i <= w; i++) {
160 /* note: using mlast in the skip path slows things down on x86 */
Victor Stinner7efa3b82013-04-08 00:26:43 +0200161 if (ss[i] == pp[0]) {
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000162 /* candidate match */
163 for (j = 0; j < mlast; j++)
164 if (s[i+j] != p[j])
165 break;
166 if (j == mlast) {
167 /* got a match! */
168 if (mode != FAST_COUNT)
169 return i;
170 count++;
Antoine Pitrouf2c54842010-01-13 08:07:53 +0000171 if (count == maxcount)
172 return maxcount;
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000173 i = i + mlast;
174 continue;
175 }
176 /* miss: check if next character is part of pattern */
Victor Stinner7efa3b82013-04-08 00:26:43 +0200177 if (!STRINGLIB_BLOOM(mask, ss[i+1]))
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000178 i = i + m;
179 else
180 i = i + skip;
181 } else {
182 /* skip: check if next character is part of pattern */
Victor Stinner7efa3b82013-04-08 00:26:43 +0200183 if (!STRINGLIB_BLOOM(mask, ss[i+1]))
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000184 i = i + m;
Thomas Wouters477c8d52006-05-27 19:21:47 +0000185 }
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000186 }
187 } else { /* FAST_RSEARCH */
188
189 /* create compressed boyer-moore delta 1 table */
190
191 /* process pattern[0] outside the loop */
Antoine Pitrouf068f942010-01-13 14:19:12 +0000192 STRINGLIB_BLOOM_ADD(mask, p[0]);
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000193 /* process pattern[:0:-1] */
194 for (i = mlast; i > 0; i--) {
Antoine Pitrouf068f942010-01-13 14:19:12 +0000195 STRINGLIB_BLOOM_ADD(mask, p[i]);
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000196 if (p[i] == p[0])
197 skip = i - 1;
198 }
199
200 for (i = w; i >= 0; i--) {
201 if (s[i] == p[0]) {
202 /* candidate match */
203 for (j = mlast; j > 0; j--)
204 if (s[i+j] != p[j])
205 break;
206 if (j == 0)
207 /* got a match! */
208 return i;
209 /* miss: check if previous character is part of pattern */
Florent Xiclunaeb6f3ea2010-08-08 22:07:16 +0000210 if (i > 0 && !STRINGLIB_BLOOM(mask, s[i-1]))
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000211 i = i - m;
212 else
213 i = i - skip;
214 } else {
215 /* skip: check if previous character is part of pattern */
Florent Xiclunaeb6f3ea2010-08-08 22:07:16 +0000216 if (i > 0 && !STRINGLIB_BLOOM(mask, s[i-1]))
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000217 i = i - m;
218 }
Thomas Wouters477c8d52006-05-27 19:21:47 +0000219 }
220 }
221
222 if (mode != FAST_COUNT)
223 return -1;
224 return count;
225}
226