blob: ecf885e7e12491dbe6e0dffab1e4d126d0ae2a7e [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{
41 void *candidate;
42 const STRINGLIB_CHAR *found;
43
44#define DO_MEMCHR(memchr, s, needle, nchars) do { \
45 candidate = memchr((const void *) (s), (needle), (nchars) * sizeof(STRINGLIB_CHAR)); \
Antoine Pitrouca8aa4a2012-09-20 20:56:47 +020046 found = (const STRINGLIB_CHAR *) _Py_ALIGN_DOWN(candidate, sizeof(STRINGLIB_CHAR)); \
Antoine Pitrou2c3b2302011-10-11 20:29:21 +020047 } while (0)
48
49 if (mode == FAST_SEARCH) {
Victor Stinnerb3f55012012-08-02 23:05:01 +020050 const STRINGLIB_CHAR *ptr = s;
Antoine Pitrou2c3b2302011-10-11 20:29:21 +020051 const STRINGLIB_CHAR *e = s + n;
Victor Stinnerb3f55012012-08-02 23:05:01 +020052 while (ptr < e) {
53 DO_MEMCHR(memchr, ptr, needle, e - ptr);
Antoine Pitrou2c3b2302011-10-11 20:29:21 +020054 if (found == NULL)
55 return -1;
56 if (sizeof(STRINGLIB_CHAR) == 1 || *found == ch)
Victor Stinnerb3f55012012-08-02 23:05:01 +020057 return (found - s);
Antoine Pitrou2c3b2302011-10-11 20:29:21 +020058 /* False positive */
Victor Stinnerb3f55012012-08-02 23:05:01 +020059 ptr = found + 1;
Antoine Pitrou2c3b2302011-10-11 20:29:21 +020060 }
61 return -1;
62 }
63#ifdef HAVE_MEMRCHR
64 /* memrchr() is a GNU extension, available since glibc 2.1.91.
65 it doesn't seem as optimized as memchr(), but is still quite
66 faster than our hand-written loop in FASTSEARCH below */
67 else if (mode == FAST_RSEARCH) {
68 while (n > 0) {
69 DO_MEMCHR(memrchr, s, needle, n);
70 if (found == NULL)
71 return -1;
72 n = found - s;
73 if (sizeof(STRINGLIB_CHAR) == 1 || *found == ch)
74 return n;
75 /* False positive */
76 }
77 return -1;
78 }
79#endif
80 else {
81 assert(0); /* Should never get here */
82 return 0;
83 }
84
85#undef DO_MEMCHR
86}
87
Thomas Wouters477c8d52006-05-27 19:21:47 +000088Py_LOCAL_INLINE(Py_ssize_t)
Martin v. Löwisd63a3b82011-09-28 07:41:54 +020089FASTSEARCH(const STRINGLIB_CHAR* s, Py_ssize_t n,
Thomas Wouters477c8d52006-05-27 19:21:47 +000090 const STRINGLIB_CHAR* p, Py_ssize_t m,
Antoine Pitrouf2c54842010-01-13 08:07:53 +000091 Py_ssize_t maxcount, int mode)
Thomas Wouters477c8d52006-05-27 19:21:47 +000092{
Antoine Pitrouf068f942010-01-13 14:19:12 +000093 unsigned long mask;
Thomas Wouters477c8d52006-05-27 19:21:47 +000094 Py_ssize_t skip, count = 0;
95 Py_ssize_t i, j, mlast, w;
96
97 w = n - m;
98
Antoine Pitrouf2c54842010-01-13 08:07:53 +000099 if (w < 0 || (mode == FAST_COUNT && maxcount == 0))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000100 return -1;
101
102 /* look for special cases */
103 if (m <= 1) {
104 if (m <= 0)
105 return -1;
106 /* use special case for 1-character strings */
Antoine Pitrou2c3b2302011-10-11 20:29:21 +0200107 if (n > 10 && (mode == FAST_SEARCH
108#ifdef HAVE_MEMRCHR
109 || mode == FAST_RSEARCH
110#endif
111 )) {
112 /* use memchr if we can choose a needle without two many likely
113 false positives */
114 unsigned char needle;
Antoine Pitrou2c3b2302011-10-11 20:29:21 +0200115 needle = p[0] & 0xff;
Victor Stinner8cc70dc2011-10-11 23:22:22 +0200116#if STRINGLIB_SIZEOF_CHAR > 1
Antoine Pitrou5b9f4c12011-10-17 19:21:04 +0200117 /* If looking for a multiple of 256, we'd have too
Antoine Pitrouc198d052011-10-13 18:07:37 +0200118 many false positives looking for the '\0' byte in UCS2
119 and UCS4 representations. */
Antoine Pitroudda339e2011-10-13 17:58:11 +0200120 if (needle != 0)
Victor Stinner8cc70dc2011-10-11 23:22:22 +0200121#endif
Antoine Pitrou2c3b2302011-10-11 20:29:21 +0200122 return STRINGLIB(fastsearch_memchr_1char)
123 (s, n, p[0], needle, maxcount, mode);
124 }
Thomas Wouters477c8d52006-05-27 19:21:47 +0000125 if (mode == FAST_COUNT) {
126 for (i = 0; i < n; i++)
Antoine Pitrouf2c54842010-01-13 08:07:53 +0000127 if (s[i] == p[0]) {
Thomas Wouters477c8d52006-05-27 19:21:47 +0000128 count++;
Antoine Pitrouf2c54842010-01-13 08:07:53 +0000129 if (count == maxcount)
130 return maxcount;
131 }
Thomas Wouters477c8d52006-05-27 19:21:47 +0000132 return count;
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000133 } else if (mode == FAST_SEARCH) {
Thomas Wouters477c8d52006-05-27 19:21:47 +0000134 for (i = 0; i < n; i++)
135 if (s[i] == p[0])
136 return i;
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000137 } else { /* FAST_RSEARCH */
138 for (i = n - 1; i > -1; i--)
139 if (s[i] == p[0])
140 return i;
Thomas Wouters477c8d52006-05-27 19:21:47 +0000141 }
142 return -1;
143 }
144
145 mlast = m - 1;
Thomas Wouters477c8d52006-05-27 19:21:47 +0000146 skip = mlast - 1;
Antoine Pitrouf2c54842010-01-13 08:07:53 +0000147 mask = 0;
Thomas Wouters477c8d52006-05-27 19:21:47 +0000148
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000149 if (mode != FAST_RSEARCH) {
150
151 /* create compressed boyer-moore delta 1 table */
152
153 /* process pattern[:-1] */
Antoine Pitrouf2c54842010-01-13 08:07:53 +0000154 for (i = 0; i < mlast; i++) {
Antoine Pitrouf068f942010-01-13 14:19:12 +0000155 STRINGLIB_BLOOM_ADD(mask, p[i]);
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000156 if (p[i] == p[mlast])
157 skip = mlast - i - 1;
158 }
159 /* process pattern[-1] outside the loop */
Antoine Pitrouf068f942010-01-13 14:19:12 +0000160 STRINGLIB_BLOOM_ADD(mask, p[mlast]);
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000161
162 for (i = 0; i <= w; i++) {
163 /* note: using mlast in the skip path slows things down on x86 */
164 if (s[i+m-1] == p[m-1]) {
165 /* candidate match */
166 for (j = 0; j < mlast; j++)
167 if (s[i+j] != p[j])
168 break;
169 if (j == mlast) {
170 /* got a match! */
171 if (mode != FAST_COUNT)
172 return i;
173 count++;
Antoine Pitrouf2c54842010-01-13 08:07:53 +0000174 if (count == maxcount)
175 return maxcount;
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000176 i = i + mlast;
177 continue;
178 }
179 /* miss: check if next character is part of pattern */
Antoine Pitrouf068f942010-01-13 14:19:12 +0000180 if (!STRINGLIB_BLOOM(mask, s[i+m]))
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000181 i = i + m;
182 else
183 i = i + skip;
184 } else {
185 /* skip: check if next character is part of pattern */
Antoine Pitrouf068f942010-01-13 14:19:12 +0000186 if (!STRINGLIB_BLOOM(mask, s[i+m]))
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000187 i = i + m;
Thomas Wouters477c8d52006-05-27 19:21:47 +0000188 }
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000189 }
190 } else { /* FAST_RSEARCH */
191
192 /* create compressed boyer-moore delta 1 table */
193
194 /* process pattern[0] outside the loop */
Antoine Pitrouf068f942010-01-13 14:19:12 +0000195 STRINGLIB_BLOOM_ADD(mask, p[0]);
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000196 /* process pattern[:0:-1] */
197 for (i = mlast; i > 0; i--) {
Antoine Pitrouf068f942010-01-13 14:19:12 +0000198 STRINGLIB_BLOOM_ADD(mask, p[i]);
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000199 if (p[i] == p[0])
200 skip = i - 1;
201 }
202
203 for (i = w; i >= 0; i--) {
204 if (s[i] == p[0]) {
205 /* candidate match */
206 for (j = mlast; j > 0; j--)
207 if (s[i+j] != p[j])
208 break;
209 if (j == 0)
210 /* got a match! */
211 return i;
212 /* miss: check if previous character is part of pattern */
Florent Xiclunaeb6f3ea2010-08-08 22:07:16 +0000213 if (i > 0 && !STRINGLIB_BLOOM(mask, s[i-1]))
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000214 i = i - m;
215 else
216 i = i - skip;
217 } else {
218 /* skip: check if previous character is part of pattern */
Florent Xiclunaeb6f3ea2010-08-08 22:07:16 +0000219 if (i > 0 && !STRINGLIB_BLOOM(mask, s[i-1]))
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000220 i = i - m;
221 }
Thomas Wouters477c8d52006-05-27 19:21:47 +0000222 }
223 }
224
225 if (mode != FAST_COUNT)
226 return -1;
227 return count;
228}
229