blob: 33ab6ff94e6ff44510effc26f5b5e2b7eb809a05 [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;
118 if (needle == 0 && sizeof(STRINGLIB_CHAR) > 1) {
119 needle = (p[0] >> 8) & 0xff;
120 if (needle >= 32)
121 use_needle = 0;
122 }
123 if (use_needle)
124 return STRINGLIB(fastsearch_memchr_1char)
125 (s, n, p[0], needle, maxcount, mode);
126 }
Thomas Wouters477c8d52006-05-27 19:21:47 +0000127 if (mode == FAST_COUNT) {
128 for (i = 0; i < n; i++)
Antoine Pitrouf2c54842010-01-13 08:07:53 +0000129 if (s[i] == p[0]) {
Thomas Wouters477c8d52006-05-27 19:21:47 +0000130 count++;
Antoine Pitrouf2c54842010-01-13 08:07:53 +0000131 if (count == maxcount)
132 return maxcount;
133 }
Thomas Wouters477c8d52006-05-27 19:21:47 +0000134 return count;
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000135 } else if (mode == FAST_SEARCH) {
Thomas Wouters477c8d52006-05-27 19:21:47 +0000136 for (i = 0; i < n; i++)
137 if (s[i] == p[0])
138 return i;
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000139 } else { /* FAST_RSEARCH */
140 for (i = n - 1; i > -1; i--)
141 if (s[i] == p[0])
142 return i;
Thomas Wouters477c8d52006-05-27 19:21:47 +0000143 }
144 return -1;
145 }
146
147 mlast = m - 1;
Thomas Wouters477c8d52006-05-27 19:21:47 +0000148 skip = mlast - 1;
Antoine Pitrouf2c54842010-01-13 08:07:53 +0000149 mask = 0;
Thomas Wouters477c8d52006-05-27 19:21:47 +0000150
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000151 if (mode != FAST_RSEARCH) {
152
153 /* create compressed boyer-moore delta 1 table */
154
155 /* process pattern[:-1] */
Antoine Pitrouf2c54842010-01-13 08:07:53 +0000156 for (i = 0; i < mlast; i++) {
Antoine Pitrouf068f942010-01-13 14:19:12 +0000157 STRINGLIB_BLOOM_ADD(mask, p[i]);
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000158 if (p[i] == p[mlast])
159 skip = mlast - i - 1;
160 }
161 /* process pattern[-1] outside the loop */
Antoine Pitrouf068f942010-01-13 14:19:12 +0000162 STRINGLIB_BLOOM_ADD(mask, p[mlast]);
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000163
164 for (i = 0; i <= w; i++) {
165 /* note: using mlast in the skip path slows things down on x86 */
166 if (s[i+m-1] == p[m-1]) {
167 /* candidate match */
168 for (j = 0; j < mlast; j++)
169 if (s[i+j] != p[j])
170 break;
171 if (j == mlast) {
172 /* got a match! */
173 if (mode != FAST_COUNT)
174 return i;
175 count++;
Antoine Pitrouf2c54842010-01-13 08:07:53 +0000176 if (count == maxcount)
177 return maxcount;
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000178 i = i + mlast;
179 continue;
180 }
181 /* miss: check if next character is part of pattern */
Antoine Pitrouf068f942010-01-13 14:19:12 +0000182 if (!STRINGLIB_BLOOM(mask, s[i+m]))
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000183 i = i + m;
184 else
185 i = i + skip;
186 } else {
187 /* skip: 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;
Thomas Wouters477c8d52006-05-27 19:21:47 +0000190 }
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000191 }
192 } else { /* FAST_RSEARCH */
193
194 /* create compressed boyer-moore delta 1 table */
195
196 /* process pattern[0] outside the loop */
Antoine Pitrouf068f942010-01-13 14:19:12 +0000197 STRINGLIB_BLOOM_ADD(mask, p[0]);
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000198 /* process pattern[:0:-1] */
199 for (i = mlast; i > 0; i--) {
Antoine Pitrouf068f942010-01-13 14:19:12 +0000200 STRINGLIB_BLOOM_ADD(mask, p[i]);
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000201 if (p[i] == p[0])
202 skip = i - 1;
203 }
204
205 for (i = w; i >= 0; i--) {
206 if (s[i] == p[0]) {
207 /* candidate match */
208 for (j = mlast; j > 0; j--)
209 if (s[i+j] != p[j])
210 break;
211 if (j == 0)
212 /* got a match! */
213 return i;
214 /* miss: check if previous character is part of pattern */
Florent Xiclunaeb6f3ea2010-08-08 22:07:16 +0000215 if (i > 0 && !STRINGLIB_BLOOM(mask, s[i-1]))
Antoine Pitrouda2ecaf2010-01-02 21:40:36 +0000216 i = i - m;
217 else
218 i = i - skip;
219 } else {
220 /* skip: 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 }
Thomas Wouters477c8d52006-05-27 19:21:47 +0000224 }
225 }
226
227 if (mode != FAST_COUNT)
228 return -1;
229 return count;
230}
231