blob: 085ec6a3d2b8333089d6d4ea5d5cf0bafe426c63 [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;
116 int use_needle = 1;
117 needle = p[0] & 0xff;
Victor Stinner8cc70dc2011-10-11 23:22:22 +0200118#if STRINGLIB_SIZEOF_CHAR > 1
119 if (needle == 0) {
Antoine Pitrou2c3b2302011-10-11 20:29:21 +0200120 needle = (p[0] >> 8) & 0xff;
Victor Stinner8cc70dc2011-10-11 23:22:22 +0200121#if STRINGLIB_SIZEOF_CHAR > 2
122 if (needle == 0)
123 needle = (p[0] >> 16) & 0xff;
124#endif
125 if (needle >= 32 || needle == 0)
Antoine Pitrou2c3b2302011-10-11 20:29:21 +0200126 use_needle = 0;
127 }
Victor Stinner8cc70dc2011-10-11 23:22:22 +0200128#endif
Antoine Pitrou2c3b2302011-10-11 20:29:21 +0200129 if (use_needle)
130 return STRINGLIB(fastsearch_memchr_1char)
131 (s, n, p[0], needle, maxcount, mode);
132 }
Thomas Wouters477c8d52006-05-27 19:21:47 +0000133 if (mode == FAST_COUNT) {
134 for (i = 0; i < n; i++)
Antoine Pitrouf2c54842010-01-13 08:07:53 +0000135 if (s[i] == p[0]) {
Thomas Wouters477c8d52006-05-27 19:21:47 +0000136 count++;
Antoine Pitrouf2c54842010-01-13 08:07:53 +0000137 if (count == maxcount)
138 return maxcount;
139 }
Thomas Wouters477c8d52006-05-27 19:21:47 +0000140 return count;
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000141 } else if (mode == FAST_SEARCH) {
Thomas Wouters477c8d52006-05-27 19:21:47 +0000142 for (i = 0; i < n; i++)
143 if (s[i] == p[0])
144 return i;
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000145 } else { /* FAST_RSEARCH */
146 for (i = n - 1; i > -1; i--)
147 if (s[i] == p[0])
148 return i;
Thomas Wouters477c8d52006-05-27 19:21:47 +0000149 }
150 return -1;
151 }
152
153 mlast = m - 1;
Thomas Wouters477c8d52006-05-27 19:21:47 +0000154 skip = mlast - 1;
Antoine Pitrouf2c54842010-01-13 08:07:53 +0000155 mask = 0;
Thomas Wouters477c8d52006-05-27 19:21:47 +0000156
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000157 if (mode != FAST_RSEARCH) {
158
159 /* create compressed boyer-moore delta 1 table */
160
161 /* process pattern[:-1] */
Antoine Pitrouf2c54842010-01-13 08:07:53 +0000162 for (i = 0; i < mlast; i++) {
Antoine Pitrouf068f942010-01-13 14:19:12 +0000163 STRINGLIB_BLOOM_ADD(mask, p[i]);
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000164 if (p[i] == p[mlast])
165 skip = mlast - i - 1;
166 }
167 /* process pattern[-1] outside the loop */
Antoine Pitrouf068f942010-01-13 14:19:12 +0000168 STRINGLIB_BLOOM_ADD(mask, p[mlast]);
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000169
170 for (i = 0; i <= w; i++) {
171 /* note: using mlast in the skip path slows things down on x86 */
172 if (s[i+m-1] == p[m-1]) {
173 /* candidate match */
174 for (j = 0; j < mlast; j++)
175 if (s[i+j] != p[j])
176 break;
177 if (j == mlast) {
178 /* got a match! */
179 if (mode != FAST_COUNT)
180 return i;
181 count++;
Antoine Pitrouf2c54842010-01-13 08:07:53 +0000182 if (count == maxcount)
183 return maxcount;
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000184 i = i + mlast;
185 continue;
186 }
187 /* miss: check if next character is part of pattern */
Antoine Pitrouf068f942010-01-13 14:19:12 +0000188 if (!STRINGLIB_BLOOM(mask, s[i+m]))
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000189 i = i + m;
190 else
191 i = i + skip;
192 } else {
193 /* skip: check if next character is part of pattern */
Antoine Pitrouf068f942010-01-13 14:19:12 +0000194 if (!STRINGLIB_BLOOM(mask, s[i+m]))
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000195 i = i + m;
Thomas Wouters477c8d52006-05-27 19:21:47 +0000196 }
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000197 }
198 } else { /* FAST_RSEARCH */
199
200 /* create compressed boyer-moore delta 1 table */
201
202 /* process pattern[0] outside the loop */
Antoine Pitrouf068f942010-01-13 14:19:12 +0000203 STRINGLIB_BLOOM_ADD(mask, p[0]);
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000204 /* process pattern[:0:-1] */
205 for (i = mlast; i > 0; i--) {
Antoine Pitrouf068f942010-01-13 14:19:12 +0000206 STRINGLIB_BLOOM_ADD(mask, p[i]);
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000207 if (p[i] == p[0])
208 skip = i - 1;
209 }
210
211 for (i = w; i >= 0; i--) {
212 if (s[i] == p[0]) {
213 /* candidate match */
214 for (j = mlast; j > 0; j--)
215 if (s[i+j] != p[j])
216 break;
217 if (j == 0)
218 /* got a match! */
219 return i;
220 /* miss: check if previous character is part of pattern */
Florent Xiclunaeb6f3ea2010-08-08 22:07:16 +0000221 if (i > 0 && !STRINGLIB_BLOOM(mask, s[i-1]))
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000222 i = i - m;
223 else
224 i = i - skip;
225 } else {
226 /* skip: check if previous character is part of pattern */
Florent Xiclunaeb6f3ea2010-08-08 22:07:16 +0000227 if (i > 0 && !STRINGLIB_BLOOM(mask, s[i-1]))
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000228 i = i - m;
229 }
Thomas Wouters477c8d52006-05-27 19:21:47 +0000230 }
231 }
232
233 if (mode != FAST_COUNT)
234 return -1;
235 return count;
236}
237