blob: f3e0461b77ea302df40347e101f9a72d10aa24af [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)); \
46 found = (const STRINGLIB_CHAR *) \
47 ((Py_ssize_t) candidate & (~ ((Py_ssize_t) sizeof(STRINGLIB_CHAR) - 1))); \
48 } while (0)
49
50 if (mode == FAST_SEARCH) {
51 const STRINGLIB_CHAR *_s = s;
52 const STRINGLIB_CHAR *e = s + n;
53 while (_s < e) {
54 DO_MEMCHR(memchr, _s, needle, e - _s);
55 if (found == NULL)
56 return -1;
57 if (sizeof(STRINGLIB_CHAR) == 1 || *found == ch)
58 return (found - _s);
59 /* False positive */
60 _s = found + 1;
61 }
62 return -1;
63 }
64#ifdef HAVE_MEMRCHR
65 /* memrchr() is a GNU extension, available since glibc 2.1.91.
66 it doesn't seem as optimized as memchr(), but is still quite
67 faster than our hand-written loop in FASTSEARCH below */
68 else if (mode == FAST_RSEARCH) {
69 while (n > 0) {
70 DO_MEMCHR(memrchr, s, needle, n);
71 if (found == NULL)
72 return -1;
73 n = found - s;
74 if (sizeof(STRINGLIB_CHAR) == 1 || *found == ch)
75 return n;
76 /* False positive */
77 }
78 return -1;
79 }
80#endif
81 else {
82 assert(0); /* Should never get here */
83 return 0;
84 }
85
86#undef DO_MEMCHR
87}
88
Thomas Wouters477c8d52006-05-27 19:21:47 +000089Py_LOCAL_INLINE(Py_ssize_t)
Martin v. Löwisd63a3b82011-09-28 07:41:54 +020090FASTSEARCH(const STRINGLIB_CHAR* s, Py_ssize_t n,
Thomas Wouters477c8d52006-05-27 19:21:47 +000091 const STRINGLIB_CHAR* p, Py_ssize_t m,
Antoine Pitrouf2c54842010-01-13 08:07:53 +000092 Py_ssize_t maxcount, int mode)
Thomas Wouters477c8d52006-05-27 19:21:47 +000093{
Antoine Pitrouf068f942010-01-13 14:19:12 +000094 unsigned long mask;
Thomas Wouters477c8d52006-05-27 19:21:47 +000095 Py_ssize_t skip, count = 0;
96 Py_ssize_t i, j, mlast, w;
97
98 w = n - m;
99
Antoine Pitrouf2c54842010-01-13 08:07:53 +0000100 if (w < 0 || (mode == FAST_COUNT && maxcount == 0))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000101 return -1;
102
103 /* look for special cases */
104 if (m <= 1) {
105 if (m <= 0)
106 return -1;
107 /* use special case for 1-character strings */
Antoine Pitrou2c3b2302011-10-11 20:29:21 +0200108 if (n > 10 && (mode == FAST_SEARCH
109#ifdef HAVE_MEMRCHR
110 || mode == FAST_RSEARCH
111#endif
112 )) {
113 /* use memchr if we can choose a needle without two many likely
114 false positives */
115 unsigned char needle;
Antoine Pitrou2c3b2302011-10-11 20:29:21 +0200116 needle = p[0] & 0xff;
Victor Stinner8cc70dc2011-10-11 23:22:22 +0200117#if STRINGLIB_SIZEOF_CHAR > 1
Antoine Pitrou5b9f4c12011-10-17 19:21:04 +0200118 /* If looking for a multiple of 256, we'd have too
Antoine Pitrouc198d052011-10-13 18:07:37 +0200119 many false positives looking for the '\0' byte in UCS2
120 and UCS4 representations. */
Antoine Pitroudda339e2011-10-13 17:58:11 +0200121 if (needle != 0)
Victor Stinner8cc70dc2011-10-11 23:22:22 +0200122#endif
Antoine Pitrou2c3b2302011-10-11 20:29:21 +0200123 return STRINGLIB(fastsearch_memchr_1char)
124 (s, n, p[0], needle, maxcount, mode);
125 }
Thomas Wouters477c8d52006-05-27 19:21:47 +0000126 if (mode == FAST_COUNT) {
127 for (i = 0; i < n; i++)
Antoine Pitrouf2c54842010-01-13 08:07:53 +0000128 if (s[i] == p[0]) {
Thomas Wouters477c8d52006-05-27 19:21:47 +0000129 count++;
Antoine Pitrouf2c54842010-01-13 08:07:53 +0000130 if (count == maxcount)
131 return maxcount;
132 }
Thomas Wouters477c8d52006-05-27 19:21:47 +0000133 return count;
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000134 } else if (mode == FAST_SEARCH) {
Thomas Wouters477c8d52006-05-27 19:21:47 +0000135 for (i = 0; i < n; i++)
136 if (s[i] == p[0])
137 return i;
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000138 } else { /* FAST_RSEARCH */
139 for (i = n - 1; i > -1; i--)
140 if (s[i] == p[0])
141 return i;
Thomas Wouters477c8d52006-05-27 19:21:47 +0000142 }
143 return -1;
144 }
145
146 mlast = m - 1;
Thomas Wouters477c8d52006-05-27 19:21:47 +0000147 skip = mlast - 1;
Antoine Pitrouf2c54842010-01-13 08:07:53 +0000148 mask = 0;
Thomas Wouters477c8d52006-05-27 19:21:47 +0000149
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000150 if (mode != FAST_RSEARCH) {
151
152 /* create compressed boyer-moore delta 1 table */
153
154 /* process pattern[:-1] */
Antoine Pitrouf2c54842010-01-13 08:07:53 +0000155 for (i = 0; i < mlast; i++) {
Antoine Pitrouf068f942010-01-13 14:19:12 +0000156 STRINGLIB_BLOOM_ADD(mask, p[i]);
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000157 if (p[i] == p[mlast])
158 skip = mlast - i - 1;
159 }
160 /* process pattern[-1] outside the loop */
Antoine Pitrouf068f942010-01-13 14:19:12 +0000161 STRINGLIB_BLOOM_ADD(mask, p[mlast]);
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000162
163 for (i = 0; i <= w; i++) {
164 /* note: using mlast in the skip path slows things down on x86 */
165 if (s[i+m-1] == p[m-1]) {
166 /* candidate match */
167 for (j = 0; j < mlast; j++)
168 if (s[i+j] != p[j])
169 break;
170 if (j == mlast) {
171 /* got a match! */
172 if (mode != FAST_COUNT)
173 return i;
174 count++;
Antoine Pitrouf2c54842010-01-13 08:07:53 +0000175 if (count == maxcount)
176 return maxcount;
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000177 i = i + mlast;
178 continue;
179 }
180 /* miss: check if next character is part of pattern */
Antoine Pitrouf068f942010-01-13 14:19:12 +0000181 if (!STRINGLIB_BLOOM(mask, s[i+m]))
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000182 i = i + m;
183 else
184 i = i + skip;
185 } else {
186 /* skip: check if next character is part of pattern */
Antoine Pitrouf068f942010-01-13 14:19:12 +0000187 if (!STRINGLIB_BLOOM(mask, s[i+m]))
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000188 i = i + m;
Thomas Wouters477c8d52006-05-27 19:21:47 +0000189 }
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000190 }
191 } else { /* FAST_RSEARCH */
192
193 /* create compressed boyer-moore delta 1 table */
194
195 /* process pattern[0] outside the loop */
Antoine Pitrouf068f942010-01-13 14:19:12 +0000196 STRINGLIB_BLOOM_ADD(mask, p[0]);
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000197 /* process pattern[:0:-1] */
198 for (i = mlast; i > 0; i--) {
Antoine Pitrouf068f942010-01-13 14:19:12 +0000199 STRINGLIB_BLOOM_ADD(mask, p[i]);
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000200 if (p[i] == p[0])
201 skip = i - 1;
202 }
203
204 for (i = w; i >= 0; i--) {
205 if (s[i] == p[0]) {
206 /* candidate match */
207 for (j = mlast; j > 0; j--)
208 if (s[i+j] != p[j])
209 break;
210 if (j == 0)
211 /* got a match! */
212 return i;
213 /* miss: check if previous character is part of pattern */
Florent Xiclunaeb6f3ea2010-08-08 22:07:16 +0000214 if (i > 0 && !STRINGLIB_BLOOM(mask, s[i-1]))
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000215 i = i - m;
216 else
217 i = i - skip;
218 } else {
219 /* skip: check if previous character is part of pattern */
Florent Xiclunaeb6f3ea2010-08-08 22:07:16 +0000220 if (i > 0 && !STRINGLIB_BLOOM(mask, s[i-1]))
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000221 i = i - m;
222 }
Thomas Wouters477c8d52006-05-27 19:21:47 +0000223 }
224 }
225
226 if (mode != FAST_COUNT)
227 return -1;
228 return count;
229}
230