xref: /netbsd-src/external/gpl3/gcc/dist/libphobos/src/std/regex/internal/kickstart.d (revision b1e838363e3c6fc78a55519254d99869742dd33c)
1181254a7Smrg /*
2181254a7Smrg     Kickstart is a coarse-grained "filter" engine that finds likely matches
3181254a7Smrg     to be verified by full-blown matcher.
4181254a7Smrg */
5181254a7Smrg module std.regex.internal.kickstart;
6181254a7Smrg 
7181254a7Smrg package(std.regex):
8181254a7Smrg 
9181254a7Smrg import std.range.primitives, std.utf;
10181254a7Smrg import std.regex.internal.ir;
11181254a7Smrg 
12181254a7Smrg //utility for shiftOr, returns a minimum number of bytes to test in a Char
effectiveSize(Char)13181254a7Smrg uint effectiveSize(Char)()
14181254a7Smrg {
15181254a7Smrg     static if (is(Char == char))
16181254a7Smrg         return 1;
17181254a7Smrg     else static if (is(Char == wchar))
18181254a7Smrg         return 2;
19181254a7Smrg     else static if (is(Char == dchar))
20181254a7Smrg         return 3;
21181254a7Smrg     else
22181254a7Smrg         static assert(0);
23181254a7Smrg }
24181254a7Smrg 
25181254a7Smrg /*
26181254a7Smrg     Kickstart engine using ShiftOr algorithm,
27181254a7Smrg     a bit parallel technique for inexact string searching.
28181254a7Smrg */
ShiftOr(Char)29181254a7Smrg struct ShiftOr(Char)
30181254a7Smrg {
31181254a7Smrg private:
32181254a7Smrg     uint[] table;
33181254a7Smrg     uint fChar;
34181254a7Smrg     uint n_length;
35181254a7Smrg     enum charSize =  effectiveSize!Char();
36181254a7Smrg     //maximum number of chars in CodepointSet to process
37181254a7Smrg     enum uint charsetThreshold = 32_000;
38181254a7Smrg     static struct ShiftThread
39181254a7Smrg     {
40181254a7Smrg         uint[] tab;
41181254a7Smrg         uint mask;
42181254a7Smrg         uint idx;
43181254a7Smrg         uint pc, counter, hops;
44181254a7Smrg         this(uint newPc, uint newCounter, uint[] table)
45181254a7Smrg         {
46181254a7Smrg             pc = newPc;
47181254a7Smrg             counter = newCounter;
48181254a7Smrg             mask = 1;
49181254a7Smrg             idx = 0;
50181254a7Smrg             hops = 0;
51181254a7Smrg             tab = table;
52181254a7Smrg         }
53181254a7Smrg 
54181254a7Smrg         void setMask(uint idx, uint mask)
55181254a7Smrg         {
56181254a7Smrg             tab[idx] |= mask;
57181254a7Smrg         }
58181254a7Smrg 
59181254a7Smrg         void setInvMask(uint idx, uint mask)
60181254a7Smrg         {
61181254a7Smrg             tab[idx] &= ~mask;
62181254a7Smrg         }
63181254a7Smrg 
64181254a7Smrg         void set(alias setBits = setInvMask)(dchar ch)
65181254a7Smrg         {
66181254a7Smrg             static if (charSize == 3)
67181254a7Smrg             {
68181254a7Smrg                 uint val = ch, tmask = mask;
69181254a7Smrg                 setBits(val&0xFF, tmask);
70181254a7Smrg                 tmask <<= 1;
71181254a7Smrg                 val >>= 8;
72181254a7Smrg                 setBits(val&0xFF, tmask);
73181254a7Smrg                 tmask <<= 1;
74181254a7Smrg                 val >>= 8;
75181254a7Smrg                 assert(val <= 0x10);
76181254a7Smrg                 setBits(val, tmask);
77181254a7Smrg                 tmask <<= 1;
78181254a7Smrg             }
79181254a7Smrg             else
80181254a7Smrg             {
81181254a7Smrg                 Char[dchar.sizeof/Char.sizeof] buf;
82181254a7Smrg                 uint tmask = mask;
83181254a7Smrg                 size_t total = encode(buf, ch);
84181254a7Smrg                 for (size_t i = 0; i < total; i++, tmask<<=1)
85181254a7Smrg                 {
86181254a7Smrg                     static if (charSize == 1)
87181254a7Smrg                         setBits(buf[i], tmask);
88181254a7Smrg                     else static if (charSize == 2)
89181254a7Smrg                     {
90181254a7Smrg                         setBits(buf[i]&0xFF, tmask);
91181254a7Smrg                         tmask <<= 1;
92181254a7Smrg                         setBits(buf[i]>>8, tmask);
93181254a7Smrg                     }
94181254a7Smrg                 }
95181254a7Smrg             }
96181254a7Smrg         }
97181254a7Smrg         void add(dchar ch){ return set!setInvMask(ch); }
98181254a7Smrg         void advance(uint s)
99181254a7Smrg         {
100181254a7Smrg             mask <<= s;
101181254a7Smrg             idx += s;
102181254a7Smrg         }
103181254a7Smrg         @property bool full(){    return !mask; }
104181254a7Smrg     }
105181254a7Smrg 
106181254a7Smrg     static ShiftThread fork(ShiftThread t, uint newPc, uint newCounter)
107181254a7Smrg     {
108181254a7Smrg         ShiftThread nt = t;
109181254a7Smrg         nt.pc = newPc;
110181254a7Smrg         nt.counter = newCounter;
111181254a7Smrg         return nt;
112181254a7Smrg     }
113181254a7Smrg 
114181254a7Smrg     @trusted static ShiftThread fetch(ref ShiftThread[] worklist)
115181254a7Smrg     {
116181254a7Smrg         auto t = worklist[$-1];
117181254a7Smrg         worklist.length -= 1;
118181254a7Smrg         if (!__ctfe)
119181254a7Smrg             cast(void) worklist.assumeSafeAppend();
120181254a7Smrg         return t;
121181254a7Smrg     }
122181254a7Smrg 
123181254a7Smrg     static uint charLen(uint ch)
124181254a7Smrg     {
125181254a7Smrg         assert(ch <= 0x10FFFF);
126181254a7Smrg         return codeLength!Char(cast(dchar) ch)*charSize;
127181254a7Smrg     }
128181254a7Smrg 
129181254a7Smrg public:
130181254a7Smrg     @trusted this(ref Regex!Char re, uint[] memory)
131181254a7Smrg     {
132181254a7Smrg         static import std.algorithm.comparison;
133181254a7Smrg         import std.algorithm.searching : countUntil;
134181254a7Smrg         import std.conv : text;
135181254a7Smrg         import std.range : assumeSorted;
136181254a7Smrg         assert(memory.length == 256);
137181254a7Smrg         fChar = uint.max;
138181254a7Smrg         // FNV-1a flavored hash (uses 32bits at a time)
139181254a7Smrg         ulong hash(uint[] tab)
140181254a7Smrg         {
141181254a7Smrg             ulong h = 0xcbf29ce484222325;
142181254a7Smrg             foreach (v; tab)
143181254a7Smrg             {
144181254a7Smrg                 h ^= v;
145181254a7Smrg                 h *= 0x100000001b3;
146181254a7Smrg             }
147181254a7Smrg             return h;
148181254a7Smrg         }
149181254a7Smrg     L_FindChar:
150181254a7Smrg         for (size_t i = 0;;)
151181254a7Smrg         {
152181254a7Smrg             switch (re.ir[i].code)
153181254a7Smrg             {
154181254a7Smrg                 case IR.Char:
155181254a7Smrg                     fChar = re.ir[i].data;
156181254a7Smrg                     static if (charSize != 3)
157181254a7Smrg                     {
158181254a7Smrg                         Char[dchar.sizeof/Char.sizeof] buf;
159181254a7Smrg                         encode(buf, fChar);
160181254a7Smrg                         fChar = buf[0];
161181254a7Smrg                     }
162181254a7Smrg                     fChar = fChar & 0xFF;
163181254a7Smrg                     break L_FindChar;
164181254a7Smrg                 case IR.GroupStart, IR.GroupEnd:
165181254a7Smrg                     i += IRL!(IR.GroupStart);
166181254a7Smrg                     break;
167181254a7Smrg                 case IR.Bof, IR.Bol, IR.Wordboundary, IR.Notwordboundary:
168181254a7Smrg                     i += IRL!(IR.Bol);
169181254a7Smrg                     break;
170181254a7Smrg                 default:
171181254a7Smrg                     break L_FindChar;
172181254a7Smrg             }
173181254a7Smrg         }
174181254a7Smrg         table = memory;
175181254a7Smrg         table[] =  uint.max;
176181254a7Smrg         alias MergeTab = bool[ulong];
177181254a7Smrg         // use reasonably complex hash to identify equivalent tables
178181254a7Smrg         auto merge = new MergeTab[re.hotspotTableSize];
179181254a7Smrg         ShiftThread[] trs;
180181254a7Smrg         ShiftThread t = ShiftThread(0, 0, table);
181181254a7Smrg         //locate first fixed char if any
182181254a7Smrg         n_length = 32;
183181254a7Smrg         for (;;)
184181254a7Smrg         {
185181254a7Smrg         L_Eval_Thread:
186181254a7Smrg             for (;;)
187181254a7Smrg             {
188181254a7Smrg                 switch (re.ir[t.pc].code)
189181254a7Smrg                 {
190181254a7Smrg                 case IR.Char:
191181254a7Smrg                     uint s = charLen(re.ir[t.pc].data);
192181254a7Smrg                     if (t.idx+s > n_length)
193181254a7Smrg                         goto L_StopThread;
194181254a7Smrg                     t.add(re.ir[t.pc].data);
195181254a7Smrg                     t.advance(s);
196181254a7Smrg                     t.pc += IRL!(IR.Char);
197181254a7Smrg                     break;
198181254a7Smrg                 case IR.OrChar://assumes IRL!(OrChar) == 1
199181254a7Smrg                     uint len = re.ir[t.pc].sequence;
200181254a7Smrg                     uint end = t.pc + len;
201181254a7Smrg                     uint[Bytecode.maxSequence] s;
202181254a7Smrg                     uint numS;
203181254a7Smrg                     for (uint i = 0; i < len; i++)
204181254a7Smrg                     {
205181254a7Smrg                         auto x = charLen(re.ir[t.pc+i].data);
206181254a7Smrg                         if (countUntil(s[0 .. numS], x) < 0)
207181254a7Smrg                            s[numS++] = x;
208181254a7Smrg                     }
209181254a7Smrg                     for (uint i = t.pc; i < end; i++)
210181254a7Smrg                     {
211181254a7Smrg                         t.add(re.ir[i].data);
212181254a7Smrg                     }
213181254a7Smrg                     for (uint i = 0; i < numS; i++)
214181254a7Smrg                     {
215181254a7Smrg                         auto tx = fork(t, t.pc + len, t.counter);
216181254a7Smrg                         if (tx.idx + s[i] <= n_length)
217181254a7Smrg                         {
218181254a7Smrg                             tx.advance(s[i]);
219181254a7Smrg                             trs ~= tx;
220181254a7Smrg                         }
221181254a7Smrg                     }
222181254a7Smrg                     if (!trs.empty)
223181254a7Smrg                         t = fetch(trs);
224181254a7Smrg                     else
225181254a7Smrg                         goto L_StopThread;
226181254a7Smrg                     break;
227181254a7Smrg                 case IR.CodepointSet:
228181254a7Smrg                 case IR.Trie:
229181254a7Smrg                     auto set = re.charsets[re.ir[t.pc].data];
230181254a7Smrg                     uint[4] s;
231181254a7Smrg                     uint numS;
232181254a7Smrg                     static if (charSize == 3)
233181254a7Smrg                     {
234181254a7Smrg                         s[0] = charSize;
235181254a7Smrg                         numS = 1;
236181254a7Smrg                     }
237181254a7Smrg                     else
238181254a7Smrg                     {
239181254a7Smrg 
240181254a7Smrg                         static if (charSize == 1)
241181254a7Smrg                             static immutable codeBounds = [0x0, 0x7F, 0x80, 0x7FF, 0x800, 0xFFFF, 0x10000, 0x10FFFF];
242181254a7Smrg                         else //== 2
243181254a7Smrg                             static immutable codeBounds = [0x0, 0xFFFF, 0x10000, 0x10FFFF];
244181254a7Smrg                         uint[] arr = new uint[set.byInterval.length * 2];
245181254a7Smrg                         size_t ofs = 0;
246181254a7Smrg                         foreach (ival; set.byInterval)
247181254a7Smrg                         {
248181254a7Smrg                             arr[ofs++] = ival.a;
249181254a7Smrg                             arr[ofs++] = ival.b;
250181254a7Smrg                         }
251181254a7Smrg                         auto srange = assumeSorted!"a <= b"(arr);
252181254a7Smrg                         for (uint i = 0; i < codeBounds.length/2; i++)
253181254a7Smrg                         {
254181254a7Smrg                             auto start = srange.lowerBound(codeBounds[2*i]).length;
255181254a7Smrg                             auto end = srange.lowerBound(codeBounds[2*i+1]).length;
256181254a7Smrg                             if (end > start || (end == start && (end & 1)))
257181254a7Smrg                                s[numS++] = (i+1)*charSize;
258181254a7Smrg                         }
259181254a7Smrg                     }
260181254a7Smrg                     if (numS == 0 || t.idx + s[numS-1] > n_length)
261181254a7Smrg                         goto L_StopThread;
262181254a7Smrg                     auto  chars = set.length;
263181254a7Smrg                     if (chars > charsetThreshold)
264181254a7Smrg                         goto L_StopThread;
265181254a7Smrg                     foreach (ch; set.byCodepoint)
266181254a7Smrg                     {
267181254a7Smrg                         //avoid surrogate pairs
268181254a7Smrg                         if (0xD800 <= ch && ch <= 0xDFFF)
269181254a7Smrg                             continue;
270181254a7Smrg                         t.add(ch);
271181254a7Smrg                     }
272181254a7Smrg                     for (uint i = 0; i < numS; i++)
273181254a7Smrg                     {
274181254a7Smrg                         auto tx =  fork(t, t.pc + IRL!(IR.CodepointSet), t.counter);
275181254a7Smrg                         tx.advance(s[i]);
276181254a7Smrg                         trs ~= tx;
277181254a7Smrg                     }
278181254a7Smrg                     if (!trs.empty)
279181254a7Smrg                         t = fetch(trs);
280181254a7Smrg                     else
281181254a7Smrg                         goto L_StopThread;
282181254a7Smrg                     break;
283181254a7Smrg                 case IR.Any:
284181254a7Smrg                     goto L_StopThread;
285181254a7Smrg 
286181254a7Smrg                 case IR.GotoEndOr:
287181254a7Smrg                     t.pc += IRL!(IR.GotoEndOr)+re.ir[t.pc].data;
288181254a7Smrg                     assert(re.ir[t.pc].code == IR.OrEnd);
289181254a7Smrg                     goto case;
290181254a7Smrg                 case IR.OrEnd:
291181254a7Smrg                     auto slot = re.ir[t.pc+1].raw+t.counter;
292181254a7Smrg                     auto val = hash(t.tab);
293181254a7Smrg                     if (val in merge[slot])
294181254a7Smrg                         goto L_StopThread; // merge equivalent
295181254a7Smrg                     merge[slot][val] = true;
296181254a7Smrg                     t.pc += IRL!(IR.OrEnd);
297181254a7Smrg                     break;
298181254a7Smrg                 case IR.OrStart:
299181254a7Smrg                     t.pc += IRL!(IR.OrStart);
300181254a7Smrg                     goto case;
301181254a7Smrg                 case IR.Option:
302181254a7Smrg                     uint next = t.pc + re.ir[t.pc].data + IRL!(IR.Option);
303181254a7Smrg                     //queue next Option
304181254a7Smrg                     if (re.ir[next].code == IR.Option)
305181254a7Smrg                     {
306181254a7Smrg                         trs ~= fork(t, next, t.counter);
307181254a7Smrg                     }
308181254a7Smrg                     t.pc += IRL!(IR.Option);
309181254a7Smrg                     break;
310181254a7Smrg                 case IR.RepeatStart:case IR.RepeatQStart:
311181254a7Smrg                     t.pc += IRL!(IR.RepeatStart)+re.ir[t.pc].data;
312181254a7Smrg                     goto case IR.RepeatEnd;
313181254a7Smrg                 case IR.RepeatEnd:
314181254a7Smrg                 case IR.RepeatQEnd:
315181254a7Smrg                     auto slot = re.ir[t.pc+1].raw+t.counter;
316181254a7Smrg                     auto val = hash(t.tab);
317181254a7Smrg                     if (val in merge[slot])
318181254a7Smrg                         goto L_StopThread; // merge equivalent
319181254a7Smrg                     merge[slot][val] = true;
320181254a7Smrg                     uint len = re.ir[t.pc].data;
321181254a7Smrg                     uint step = re.ir[t.pc+2].raw;
322181254a7Smrg                     uint min = re.ir[t.pc+3].raw;
323181254a7Smrg                     if (t.counter < min)
324181254a7Smrg                     {
325181254a7Smrg                         t.counter += step;
326181254a7Smrg                         t.pc -= len;
327181254a7Smrg                         break;
328181254a7Smrg                     }
329181254a7Smrg                     uint max = re.ir[t.pc+4].raw;
330181254a7Smrg                     if (t.counter < max)
331181254a7Smrg                     {
332181254a7Smrg                         trs ~= fork(t, t.pc - len, t.counter + step);
333181254a7Smrg                         t.counter = t.counter%step;
334181254a7Smrg                         t.pc += IRL!(IR.RepeatEnd);
335181254a7Smrg                     }
336181254a7Smrg                     else
337181254a7Smrg                     {
338181254a7Smrg                         t.counter = t.counter%step;
339181254a7Smrg                         t.pc += IRL!(IR.RepeatEnd);
340181254a7Smrg                     }
341181254a7Smrg                     break;
342181254a7Smrg                 case IR.InfiniteStart, IR.InfiniteQStart:
343181254a7Smrg                     t.pc += re.ir[t.pc].data + IRL!(IR.InfiniteStart);
344181254a7Smrg                     goto case IR.InfiniteEnd; //both Q and non-Q
345181254a7Smrg                 case IR.InfiniteEnd:
346181254a7Smrg                 case IR.InfiniteQEnd:
347181254a7Smrg                     auto slot = re.ir[t.pc+1].raw+t.counter;
348181254a7Smrg                     auto val = hash(t.tab);
349181254a7Smrg                     if (val in merge[slot])
350181254a7Smrg                         goto L_StopThread; // merge equivalent
351181254a7Smrg                     merge[slot][val] = true;
352181254a7Smrg                     uint len = re.ir[t.pc].data;
353181254a7Smrg                     uint pc1, pc2; //branches to take in priority order
354181254a7Smrg                     if (++t.hops == 32)
355181254a7Smrg                         goto L_StopThread;
356181254a7Smrg                     pc1 = t.pc + IRL!(IR.InfiniteEnd);
357181254a7Smrg                     pc2 = t.pc - len;
358181254a7Smrg                     trs ~= fork(t, pc2, t.counter);
359181254a7Smrg                     t.pc = pc1;
360181254a7Smrg                     break;
361181254a7Smrg                 case IR.GroupStart, IR.GroupEnd:
362181254a7Smrg                     t.pc += IRL!(IR.GroupStart);
363181254a7Smrg                     break;
364181254a7Smrg                 case IR.Bof, IR.Bol, IR.Wordboundary, IR.Notwordboundary:
365181254a7Smrg                     t.pc += IRL!(IR.Bol);
366181254a7Smrg                     break;
367181254a7Smrg                 case IR.LookaheadStart, IR.NeglookaheadStart, IR.LookbehindStart, IR.NeglookbehindStart:
368181254a7Smrg                     t.pc += IRL!(IR.LookaheadStart) + IRL!(IR.LookaheadEnd) + re.ir[t.pc].data;
369181254a7Smrg                     break;
370181254a7Smrg                 default:
371181254a7Smrg                 L_StopThread:
372181254a7Smrg                     assert(re.ir[t.pc].code >= 0x80, text(re.ir[t.pc].code));
373181254a7Smrg                     debug (fred_search) writeln("ShiftOr stumbled on ",re.ir[t.pc].mnemonic);
374181254a7Smrg                     n_length = std.algorithm.comparison.min(t.idx, n_length);
375181254a7Smrg                     break L_Eval_Thread;
376181254a7Smrg                 }
377181254a7Smrg             }
378181254a7Smrg             if (trs.empty)
379181254a7Smrg                 break;
380181254a7Smrg             t = fetch(trs);
381181254a7Smrg         }
382181254a7Smrg         debug(std_regex_search)
383181254a7Smrg         {
384181254a7Smrg             writeln("Min length: ", n_length);
385181254a7Smrg         }
386181254a7Smrg     }
387181254a7Smrg 
388181254a7Smrg     @property bool empty() const {  return n_length == 0; }
389181254a7Smrg 
390181254a7Smrg     @property uint length() const{ return n_length/charSize; }
391181254a7Smrg 
392181254a7Smrg     // lookup compatible bit pattern in haystack, return starting index
393181254a7Smrg     // has a useful trait: if supplied with valid UTF indexes,
394181254a7Smrg     // returns only valid UTF indexes
395181254a7Smrg     // (that given the haystack in question is valid UTF string)
396*b1e83836Smrg     @trusted size_t search(const(Char)[] haystack, size_t idx) const
397181254a7Smrg     {//@BUG: apparently assumes little endian machines
398181254a7Smrg         import core.stdc.string : memchr;
399181254a7Smrg         import std.conv : text;
400181254a7Smrg         assert(!empty);
401181254a7Smrg         auto p = cast(const(ubyte)*)(haystack.ptr+idx);
402181254a7Smrg         uint state = uint.max;
403181254a7Smrg         uint limit = 1u<<(n_length - 1u);
404181254a7Smrg         debug(std_regex_search) writefln("Limit: %32b",limit);
405181254a7Smrg         if (fChar != uint.max)
406181254a7Smrg         {
407181254a7Smrg             const(ubyte)* end = cast(ubyte*)(haystack.ptr + haystack.length);
408181254a7Smrg             const orginalAlign = cast(size_t) p & (Char.sizeof-1);
409181254a7Smrg             while (p != end)
410181254a7Smrg             {
411181254a7Smrg                 if (!~state)
412181254a7Smrg                 {//speed up seeking first matching place
413181254a7Smrg                     for (;;)
414181254a7Smrg                     {
415181254a7Smrg                         assert(p <= end, text(p," vs ", end));
416181254a7Smrg                         p = cast(ubyte*) memchr(p, fChar, end - p);
417181254a7Smrg                         if (!p)
418181254a7Smrg                             return haystack.length;
419181254a7Smrg                         if ((cast(size_t) p & (Char.sizeof-1)) == orginalAlign)
420181254a7Smrg                             break;
421181254a7Smrg                         if (++p == end)
422181254a7Smrg                             return haystack.length;
423181254a7Smrg                     }
424181254a7Smrg                     state = ~1u;
425181254a7Smrg                     assert((cast(size_t) p & (Char.sizeof-1)) == orginalAlign);
426181254a7Smrg                     static if (charSize == 3)
427181254a7Smrg                     {
428181254a7Smrg                         state = (state << 1) | table[p[1]];
429181254a7Smrg                         state = (state << 1) | table[p[2]];
430181254a7Smrg                         p += 4;
431181254a7Smrg                     }
432181254a7Smrg                     else
433181254a7Smrg                         p++;
434181254a7Smrg                     //first char is tested, see if that's all
435181254a7Smrg                     if (!(state & limit))
436181254a7Smrg                         return (p-cast(ubyte*) haystack.ptr)/Char.sizeof
437181254a7Smrg                             -length;
438181254a7Smrg                 }
439181254a7Smrg                 else
440181254a7Smrg                 {//have some bits/states for possible matches,
441181254a7Smrg                  //use the usual shift-or cycle
442181254a7Smrg                     static if (charSize == 3)
443181254a7Smrg                     {
444181254a7Smrg                         state = (state << 1) | table[p[0]];
445181254a7Smrg                         state = (state << 1) | table[p[1]];
446181254a7Smrg                         state = (state << 1) | table[p[2]];
447181254a7Smrg                         p += 4;
448181254a7Smrg                     }
449181254a7Smrg                     else
450181254a7Smrg                     {
451181254a7Smrg                         state = (state << 1) | table[p[0]];
452181254a7Smrg                         p++;
453181254a7Smrg                     }
454181254a7Smrg                     if (!(state & limit))
455181254a7Smrg                         return (p-cast(ubyte*) haystack.ptr)/Char.sizeof
456181254a7Smrg                             -length;
457181254a7Smrg                 }
458181254a7Smrg                 debug(std_regex_search) writefln("State: %32b", state);
459181254a7Smrg             }
460181254a7Smrg         }
461181254a7Smrg         else
462181254a7Smrg         {
463181254a7Smrg             //normal path, partially unrolled for char/wchar
464181254a7Smrg             static if (charSize == 3)
465181254a7Smrg             {
466181254a7Smrg                 const(ubyte)* end = cast(ubyte*)(haystack.ptr + haystack.length);
467181254a7Smrg                 while (p != end)
468181254a7Smrg                 {
469181254a7Smrg                     state = (state << 1) | table[p[0]];
470181254a7Smrg                     state = (state << 1) | table[p[1]];
471181254a7Smrg                     state = (state << 1) | table[p[2]];
472181254a7Smrg                     p += 4;
473181254a7Smrg                     if (!(state & limit))//division rounds down for dchar
474181254a7Smrg                         return (p-cast(ubyte*) haystack.ptr)/Char.sizeof
475181254a7Smrg                         -length;
476181254a7Smrg                 }
477181254a7Smrg             }
478181254a7Smrg             else
479181254a7Smrg             {
480181254a7Smrg                 auto len = cast(ubyte*)(haystack.ptr + haystack.length) - p;
481181254a7Smrg                 size_t i  = 0;
482181254a7Smrg                 if (len & 1)
483181254a7Smrg                 {
484181254a7Smrg                     state = (state << 1) | table[p[i++]];
485181254a7Smrg                     if (!(state & limit))
486181254a7Smrg                         return idx+i/Char.sizeof-length;
487181254a7Smrg                 }
488181254a7Smrg                 while (i < len)
489181254a7Smrg                 {
490181254a7Smrg                     state = (state << 1) | table[p[i++]];
491181254a7Smrg                     if (!(state & limit))
492181254a7Smrg                         return idx+i/Char.sizeof
493181254a7Smrg                             -length;
494181254a7Smrg                     state = (state << 1) | table[p[i++]];
495181254a7Smrg                     if (!(state & limit))
496181254a7Smrg                         return idx+i/Char.sizeof
497181254a7Smrg                             -length;
498181254a7Smrg                     debug(std_regex_search) writefln("State: %32b", state);
499181254a7Smrg                 }
500181254a7Smrg             }
501181254a7Smrg         }
502181254a7Smrg         return haystack.length;
503181254a7Smrg     }
504181254a7Smrg 
505181254a7Smrg     @system debug static void dump(uint[] table)
506181254a7Smrg     {//@@@BUG@@@ writef(ln) is @system
507181254a7Smrg         import std.stdio : writefln;
508181254a7Smrg         for (size_t i = 0; i < table.length; i += 4)
509181254a7Smrg         {
510181254a7Smrg             writefln("%32b %32b %32b %32b",table[i], table[i+1], table[i+2], table[i+3]);
511181254a7Smrg         }
512181254a7Smrg     }
513181254a7Smrg }
514181254a7Smrg 
515181254a7Smrg @system unittest
516181254a7Smrg {
517181254a7Smrg     import std.conv, std.regex;
test_fixed(alias Kick)518181254a7Smrg     @trusted void test_fixed(alias Kick)()
519181254a7Smrg     {
520*b1e83836Smrg         static foreach (i, v; AliasSeq!(char, wchar, dchar))
521*b1e83836Smrg         {{
522181254a7Smrg             alias Char = v;
523181254a7Smrg             alias String = immutable(v)[];
524181254a7Smrg             auto r = regex(to!String(`abc$`));
525181254a7Smrg             auto kick = Kick!Char(r, new uint[256]);
526181254a7Smrg             assert(kick.length == 3, text(Kick.stringof," ",v.stringof, " == ", kick.length));
527181254a7Smrg             auto r2 = regex(to!String(`(abc){2}a+`));
528181254a7Smrg             kick = Kick!Char(r2, new uint[256]);
529181254a7Smrg             assert(kick.length == 7, text(Kick.stringof,v.stringof," == ", kick.length));
530181254a7Smrg             auto r3 = regex(to!String(`\b(a{2}b{3}){2,4}`));
531181254a7Smrg             kick = Kick!Char(r3, new uint[256]);
532181254a7Smrg             assert(kick.length == 10, text(Kick.stringof,v.stringof," == ", kick.length));
533181254a7Smrg             auto r4 = regex(to!String(`\ba{2}c\bxyz`));
534181254a7Smrg             kick = Kick!Char(r4, new uint[256]);
535181254a7Smrg             assert(kick.length == 6, text(Kick.stringof,v.stringof, " == ", kick.length));
536181254a7Smrg             auto r5 = regex(to!String(`\ba{2}c\b`));
537181254a7Smrg             kick = Kick!Char(r5, new uint[256]);
538181254a7Smrg             size_t x = kick.search("aabaacaa", 0);
539181254a7Smrg             assert(x == 3, text(Kick.stringof,v.stringof," == ", kick.length));
540181254a7Smrg             x = kick.search("aabaacaa", x+1);
541181254a7Smrg             assert(x == 8, text(Kick.stringof,v.stringof," == ", kick.length));
542*b1e83836Smrg         }}
543181254a7Smrg     }
test_flex(alias Kick)544181254a7Smrg     @trusted void test_flex(alias Kick)()
545181254a7Smrg     {
546*b1e83836Smrg         static foreach (i, v; AliasSeq!(char, wchar, dchar))
547*b1e83836Smrg         {{
548181254a7Smrg             alias Char = v;
549181254a7Smrg             alias String = immutable(v)[];
550181254a7Smrg             auto r = regex(to!String(`abc[a-z]`));
551181254a7Smrg             auto kick = Kick!Char(r, new uint[256]);
552181254a7Smrg             auto x = kick.search(to!String("abbabca"), 0);
553181254a7Smrg             assert(x == 3, text("real x is ", x, " ",v.stringof));
554181254a7Smrg 
555181254a7Smrg             auto r2 = regex(to!String(`(ax|bd|cdy)`));
556181254a7Smrg             String s2 = to!String("abdcdyabax");
557181254a7Smrg             kick = Kick!Char(r2, new uint[256]);
558181254a7Smrg             x = kick.search(s2, 0);
559181254a7Smrg             assert(x == 1, text("real x is ", x));
560181254a7Smrg             x = kick.search(s2, x+1);
561181254a7Smrg             assert(x == 3, text("real x is ", x));
562181254a7Smrg             x = kick.search(s2, x+1);
563181254a7Smrg             assert(x == 8, text("real x is ", x));
564181254a7Smrg             auto rdot = regex(to!String(`...`));
565181254a7Smrg             kick = Kick!Char(rdot, new uint[256]);
566181254a7Smrg             assert(kick.length == 0);
567181254a7Smrg             auto rN = regex(to!String(`a(b+|c+)x`));
568181254a7Smrg             kick = Kick!Char(rN, new uint[256]);
569181254a7Smrg             assert(kick.length == 3, to!string(kick.length));
570181254a7Smrg             assert(kick.search("ababx",0) == 2);
571181254a7Smrg             assert(kick.search("abaacba",0) == 3);//expected inexact
572181254a7Smrg 
573*b1e83836Smrg         }}
574181254a7Smrg     }
575181254a7Smrg     test_fixed!(ShiftOr)();
576181254a7Smrg     test_flex!(ShiftOr)();
577181254a7Smrg }
578181254a7Smrg 
579181254a7Smrg alias Kickstart = ShiftOr;
580