blob: 0f7aea74a985495d147ffd80f5b7d29ecd5b4257 [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 Pitroudda339e2011-10-13 17:58:11 +0200118 if (needle != 0)
Victor Stinner8cc70dc2011-10-11 23:22:22 +0200119#endif
Antoine Pitrou2c3b2302011-10-11 20:29:21 +0200120 return STRINGLIB(fastsearch_memchr_1char)
121 (s, n, p[0], needle, maxcount, mode);
122 }
Thomas Wouters477c8d52006-05-27 19:21:47 +0000123 if (mode == FAST_COUNT) {
124 for (i = 0; i < n; i++)
Antoine Pitrouf2c54842010-01-13 08:07:53 +0000125 if (s[i] == p[0]) {
Thomas Wouters477c8d52006-05-27 19:21:47 +0000126 count++;
Antoine Pitrouf2c54842010-01-13 08:07:53 +0000127 if (count == maxcount)
128 return maxcount;
129 }
Thomas Wouters477c8d52006-05-27 19:21:47 +0000130 return count;
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000131 } else if (mode == FAST_SEARCH) {
Thomas Wouters477c8d52006-05-27 19:21:47 +0000132 for (i = 0; i < n; i++)
133 if (s[i] == p[0])
134 return i;
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000135 } else { /* FAST_RSEARCH */
136 for (i = n - 1; i > -1; i--)
137 if (s[i] == p[0])
138 return i;
Thomas Wouters477c8d52006-05-27 19:21:47 +0000139 }
140 return -1;
141 }
142
143 mlast = m - 1;
Thomas Wouters477c8d52006-05-27 19:21:47 +0000144 skip = mlast - 1;
Antoine Pitrouf2c54842010-01-13 08:07:53 +0000145 mask = 0;
Thomas Wouters477c8d52006-05-27 19:21:47 +0000146
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000147 if (mode != FAST_RSEARCH) {
148
149 /* create compressed boyer-moore delta 1 table */
150
151 /* process pattern[:-1] */
Antoine Pitrouf2c54842010-01-13 08:07:53 +0000152 for (i = 0; i < mlast; i++) {
Antoine Pitrouf068f942010-01-13 14:19:12 +0000153 STRINGLIB_BLOOM_ADD(mask, p[i]);
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000154 if (p[i] == p[mlast])
155 skip = mlast - i - 1;
156 }
157 /* process pattern[-1] outside the loop */
Antoine Pitrouf068f942010-01-13 14:19:12 +0000158 STRINGLIB_BLOOM_ADD(mask, p[mlast]);
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000159
160 for (i = 0; i <= w; i++) {
161 /* note: using mlast in the skip path slows things down on x86 */
162 if (s[i+m-1] == p[m-1]) {
163 /* candidate match */
164 for (j = 0; j < mlast; j++)
165 if (s[i+j] != p[j])
166 break;
167 if (j == mlast) {
168 /* got a match! */
169 if (mode != FAST_COUNT)
170 return i;
171 count++;
Antoine Pitrouf2c54842010-01-13 08:07:53 +0000172 if (count == maxcount)
173 return maxcount;
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000174 i = i + mlast;
175 continue;
176 }
177 /* miss: check if next character is part of pattern */
Antoine Pitrouf068f942010-01-13 14:19:12 +0000178 if (!STRINGLIB_BLOOM(mask, s[i+m]))
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000179 i = i + m;
180 else
181 i = i + skip;
182 } else {
183 /* skip: check if next character is part of pattern */
Antoine Pitrouf068f942010-01-13 14:19:12 +0000184 if (!STRINGLIB_BLOOM(mask, s[i+m]))
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000185 i = i + m;
Thomas Wouters477c8d52006-05-27 19:21:47 +0000186 }
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000187 }
188 } else { /* FAST_RSEARCH */
189
190 /* create compressed boyer-moore delta 1 table */
191
192 /* process pattern[0] outside the loop */
Antoine Pitrouf068f942010-01-13 14:19:12 +0000193 STRINGLIB_BLOOM_ADD(mask, p[0]);
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000194 /* process pattern[:0:-1] */
195 for (i = mlast; i > 0; i--) {
Antoine Pitrouf068f942010-01-13 14:19:12 +0000196 STRINGLIB_BLOOM_ADD(mask, p[i]);
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000197 if (p[i] == p[0])
198 skip = i - 1;
199 }
200
201 for (i = w; i >= 0; i--) {
202 if (s[i] == p[0]) {
203 /* candidate match */
204 for (j = mlast; j > 0; j--)
205 if (s[i+j] != p[j])
206 break;
207 if (j == 0)
208 /* got a match! */
209 return i;
210 /* miss: check if previous character is part of pattern */
Florent Xiclunaeb6f3ea2010-08-08 22:07:16 +0000211 if (i > 0 && !STRINGLIB_BLOOM(mask, s[i-1]))
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000212 i = i - m;
213 else
214 i = i - skip;
215 } else {
216 /* skip: check if previous character is part of pattern */
Florent Xiclunaeb6f3ea2010-08-08 22:07:16 +0000217 if (i > 0 && !STRINGLIB_BLOOM(mask, s[i-1]))
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000218 i = i - m;
219 }
Thomas Wouters477c8d52006-05-27 19:21:47 +0000220 }
221 }
222
223 if (mode != FAST_COUNT)
224 return -1;
225 return count;
226}
227