1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
open Parse
let dfa_dead = 0
let dfa_accept = 1
let dfa_bail = 2
let dfa_first = 3
let max_dfa_states = 256
let max_nfa_states = 512
let max_dfa_steps = 400000
type nfa_kind =
| Nfa_set (** Reads one byte of its set and goes to [out]. *)
| Nfa_split (** Goes to [out] and to [alt], reading nothing. *)
| Nfa_bol (** [Nfa_bol] and [Nfa_eol] go to [out] at the start and at the end of the text. *)
| Nfa_eol
| Nfa_match
type nfa_state = {
nkind : nfa_kind;
bits : string; (** The 128 bits of the set, as in {!Charset.t}. *)
mutable out : int;
alt : int;
}
type nfa_builder = {
mutable states : nfa_state array; (** The states, of which the first [count] are in use. *)
mutable count : int;
mutable failed : bool; (** The pattern has something an automaton over bytes cannot decide, or is too large. *)
}
let no_bits = String.make 16 '\000'
let no_state = { nkind = Nfa_match; bits = no_bits; out = 0; alt = 0 }
let add b nkind bits out alt =
if b.count >= max_nfa_states then begin
b.failed <- true;
0
end
else begin
if b.count = Array.length b.states then begin
let grown = Array.make ((2 * b.count) + 16) no_state in
Array.blit b.states 0 grown 0 b.count;
b.states <- grown
end;
b.states.(b.count) <- { nkind; bits; out; alt };
b.count <- b.count + 1;
b.count - 1
end
let rec compile b n next =
if b.failed then 0
else
match n.kind with
| Empty -> next
| Char ->
let bits = Bytes.make 16 '\000' in
if n.r < 128 then Bytes.set bits (n.r lsr 3) (Char.chr (1 lsl (n.r land 7)));
add b Nfa_set (Bytes.to_string bits) next 0
| Set -> add b Nfa_set n.set.ascii next 0
| Cat ->
let next = ref next in
for i = Array.length n.subs - 1 downto 0 do
next := compile b n.subs.(i) !next
done;
!next
| Alt ->
let start = ref (compile b n.subs.(Array.length n.subs - 1) next) in
for i = Array.length n.subs - 2 downto 0 do
let out = compile b n.subs.(i) next in
start := add b Nfa_split no_bits out !start
done;
!start
| Group -> compile b n.subs.(0) next
| Repeat ->
let sub = n.subs.(0) in
let next = ref next in
if n.max = unbounded then begin
let loop = add b Nfa_split no_bits 0 !next in
if not b.failed then begin
b.states.(loop).out <- compile b sub loop;
next := loop
end
end
else begin
let i = ref n.min in
while !i < n.max && not b.failed do
let out = compile b sub !next in
next := add b Nfa_split no_bits out !next;
incr i
done
end;
if b.failed then 0
else begin
let i = ref 0 in
while !i < n.min && not b.failed do
next := compile b sub !next;
incr i
done;
!next
end
| Bol -> add b Nfa_bol no_bits next 0
| Eol -> add b Nfa_eol no_bits next 0
| Line_start | Line_end | Word_boundary | Not_word_boundary | Look | Backref ->
b.failed <- true;
0
type t = {
byte_class : string; (** The class of each of the 256 bytes. *)
trans : string; (** [trans.[(state lsl shift) lor class]] is the state after a byte of that class. *)
shift : int;
start : int;
at_end : string; (** For each state: a byte that is not zero when the pattern matches if the text ends there. *)
empty : bool; (** The pattern matches the empty text. *)
}
(** The automaton. A state is a byte, a byte of the text is one of a few classes (bytes the pattern does not tell
apart), and a transition is one read of [trans]. Every part is a string, so nothing can write to it. *)
(** The pattern does not match the text. *)
let no_match = 0
(** The pattern matches the text. *)
let matched = 1
(** The text is not ASCII, which this automaton does not decide. *)
let undecided = 2
let finish s = if s = dfa_accept then matched else if s = dfa_bail then undecided else no_match
let rec run_string trans byte_class shift at_end text i hi s =
if i = hi then if String.get at_end s <> '\000' then matched else no_match
else
let c = Char.code (String.get text i) in
let s = Char.code (String.get trans ((s lsl shift) lor Char.code (String.get byte_class c))) in
if s < dfa_first then finish s else run_string trans byte_class shift at_end text (i + 1) hi s
let rec run_bytes trans byte_class shift at_end text i hi s =
if i = hi then if String.get at_end s <> '\000' then matched else no_match
else
let c = Char.code (Bytes.get text i) in
let s = Char.code (String.get trans ((s lsl shift) lor Char.code (String.get byte_class c))) in
if s < dfa_first then finish s else run_bytes trans byte_class shift at_end text (i + 1) hi s
(** [match_string d text lo hi] says whether the pattern matches the bytes [lo] up to [hi] of the text: {!matched},
{!no_match}, or {!undecided} when the text is not ASCII. It does not allocate. *)
let match_string d text lo hi =
if lo = hi then if d.empty then matched else no_match
else if d.start = dfa_accept then matched
else run_string d.trans d.byte_class d.shift d.at_end text lo hi d.start
(** [match_bytes d text lo hi] is {!match_string} for bytes. The loop is written once for each kind of text so that each
is one table read per byte with no test of the kind. *)
let match_bytes d text lo hi =
if lo = hi then if d.empty then matched else no_match
else if d.start = dfa_accept then matched
else run_bytes d.trans d.byte_class d.shift d.at_end text lo hi d.start
type list_ = { mutable items : int array; mutable n : int }
(** A list of states of the pattern, of which the first [n] elements of [items] are in use. *)
let new_list () = { items = Array.make 16 0; n = 0 }
let append list value =
if list.n = Array.length list.items then begin
let grown = Array.make ((2 * list.n) + 16) 0 in
Array.blit list.items 0 grown 0 list.n;
list.items <- grown
end;
list.items.(list.n) <- value;
list.n <- list.n + 1
type dfa_builder = {
nfa : nfa_state array;
mark : int array; (** Scratch for closure: the states visited, by generation, and the stack. *)
mutable gen : int;
stack : list_;
mutable steps : int; (** The steps left (see [max_dfa_steps]). *)
}
(** Makes the automaton's states from sets of the pattern's states. *)
let closure b set from at_start at_end =
b.gen <- b.gen + 1;
b.stack.n <- 0;
for k = 0 to from.n - 1 do
append b.stack from.items.(k)
done;
while b.stack.n > 0 do
b.stack.n <- b.stack.n - 1;
let i = b.stack.items.(b.stack.n) in
b.steps <- b.steps - 1;
if b.mark.(i) <> b.gen then begin
b.mark.(i) <- b.gen;
let s = b.nfa.(i) in
match s.nkind with
| Nfa_split ->
append b.stack s.out;
append b.stack s.alt
| Nfa_bol -> if at_start then append b.stack s.out
| Nfa_eol -> if at_end then append b.stack s.out else append set i
| Nfa_set | Nfa_match -> append set i
end
done
let has_match b set =
let rec scan k = k < set.n && (b.nfa.(set.items.(k)).nkind = Nfa_match || scan (k + 1)) in
scan 0
let set_key set =
for i = 1 to set.n - 1 do
let j = ref i in
while !j > 0 && set.items.(!j) < set.items.(!j - 1) do
let swap = set.items.(!j) in
set.items.(!j) <- set.items.(!j - 1);
set.items.(!j - 1) <- swap;
decr j
done
done;
let key = Bytes.create (2 * set.n) in
for k = 0 to set.n - 1 do
Bytes.set key (2 * k) (Char.chr (set.items.(k) land 0xFF));
Bytes.set key ((2 * k) + 1) (Char.chr (set.items.(k) lsr 8))
done;
Bytes.to_string key
exception Too_large
(** [build root] builds the automaton of a pattern for an unanchored search, from the tree the parser read. It returns
[None] when the pattern has none. The caller must not ask for the automaton of a pattern with a counted quantifier
above 1000 or with such quantifiers nested to a product above 1000: the number of states bounds the work here only
when each repetition adds a state. *)
let build root =
let nb = { states = [||]; count = 0; failed = false } in
let match_state = add nb Nfa_match no_bits 0 0 in
let start = compile nb root match_state in
if nb.failed then None
else begin
let nfa = Array.sub nb.states 0 nb.count in
let b = { nfa; mark = Array.make (Array.length nfa) 0; gen = 0; stack = new_list (); steps = max_dfa_steps } in
let byte_class = Bytes.make 256 '\000' in
let classes = ref 1 in
let signatures = Hashtbl.create 64 in
let signature = Buffer.create ((Array.length nfa / 8) + 1) in
for c = 0 to 127 do
Buffer.clear signature;
let acc = ref 0 and n = ref 0 in
Array.iter
(fun s ->
if s.nkind = Nfa_set then begin
acc := (!acc lsl 1) lor (if Charset.ascii_has s.bits c then 1 else 0) land 0xFF;
incr n;
if !n = 8 then begin
Buffer.add_char signature (Char.chr !acc);
acc := 0;
n := 0
end
end)
nfa;
Buffer.add_char signature (Char.chr !acc);
let key = Buffer.contents signature in
let class_ =
match Hashtbl.find_opt signatures key with
| Some class_ -> class_
| None ->
let class_ = !classes in
incr classes;
Hashtbl.replace signatures key class_;
class_
in
Bytes.set byte_class c (Char.chr class_)
done;
let classes = !classes in
let shift = ref 0 in
while 1 lsl !shift < classes do
incr shift
done;
let shift = !shift in
let sample = Array.make 129 0 in
for c = 127 downto 0 do
sample.(Char.code (Bytes.get byte_class c)) <- c
done;
let from_start = new_list () in
append from_start start;
let restart = new_list () in
closure b restart from_start false false;
let at_both = new_list () in
closure b at_both from_start true true;
let empty = has_match b at_both in
let ids = Hashtbl.create 64 in
let sets = ref (Array.make 16 [||]) in
let set_count = ref dfa_first in
let stride = 1 lsl shift in
let table = Bytes.make (max_dfa_states * stride) '\000' in
for class_ = 0 to stride - 1 do
Bytes.set table ((dfa_accept * stride) + class_) (Char.chr dfa_accept);
Bytes.set table ((dfa_bail * stride) + class_) (Char.chr dfa_bail)
done;
let at_end = Buffer.create 16 in
Buffer.add_string at_end "\000\000\000";
let ends = new_list () and ends_closed = new_list () in
let intern set =
if has_match b set then dfa_accept
else if set.n = 0 then dfa_dead
else
let key = set_key set in
match Hashtbl.find_opt ids key with
| Some id -> id
| None ->
if !set_count >= max_dfa_states then raise Too_large;
let id = !set_count in
Hashtbl.replace ids key id;
if id = Array.length !sets then begin
let grown = Array.make (2 * id) [||] in
Array.blit !sets 0 grown 0 id;
sets := grown
end;
!sets.(id) <- Array.sub set.items 0 set.n;
incr set_count;
ends.n <- 0;
for k = 0 to set.n - 1 do
let s = nfa.(set.items.(k)) in
if s.nkind = Nfa_eol then append ends s.out
done;
ends_closed.n <- 0;
if ends.n <> 0 then closure b ends_closed ends false true;
Buffer.add_char at_end (if ends.n <> 0 && has_match b ends_closed then '\001' else '\000');
id
in
try
let first = new_list () in
closure b first from_start true false;
let start = intern first in
let moved = new_list () and next = new_list () in
let id = ref dfa_first in
while !id < !set_count do
let set = !sets.(!id) in
for class_ = 0 to stride - 1 do
if class_ = 0 || class_ >= classes then
Bytes.set table ((!id * stride) + class_) (Char.chr dfa_bail)
else begin
let c = sample.(class_) in
moved.n <- 0;
Array.iter
(fun i ->
let s = nfa.(i) in
if s.nkind = Nfa_set && Charset.ascii_has s.bits c then append moved s.out)
set;
for k = 0 to restart.n - 1 do
append moved restart.items.(k)
done;
next.n <- 0;
closure b next moved false false;
let target = intern next in
if b.steps < 0 then raise Too_large;
Bytes.set table ((!id * stride) + class_) (Char.chr target)
end
done;
incr id
done;
Some
{
byte_class = Bytes.to_string byte_class;
trans = Bytes.sub_string table 0 (!set_count * stride);
shift;
start;
at_end = Buffer.contents at_end;
empty;
}
with Too_large -> None
end