|
1 /* GRAPHITE2 LICENSING |
|
2 |
|
3 Copyright 2010, SIL International |
|
4 All rights reserved. |
|
5 |
|
6 This library is free software; you can redistribute it and/or modify |
|
7 it under the terms of the GNU Lesser General Public License as published |
|
8 by the Free Software Foundation; either version 2.1 of License, or |
|
9 (at your option) any later version. |
|
10 |
|
11 This program is distributed in the hope that it will be useful, |
|
12 but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
14 Lesser General Public License for more details. |
|
15 |
|
16 You should also have received a copy of the GNU Lesser General Public |
|
17 License along with this library in the file named "LICENSE". |
|
18 If not, write to the Free Software Foundation, 51 Franklin Street, |
|
19 Suite 500, Boston, MA 02110-1335, USA or visit their web page on the |
|
20 internet at http://www.fsf.org/licenses/lgpl.html. |
|
21 |
|
22 Alternatively, the contents of this file may be used under the terms of the |
|
23 Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public |
|
24 License, as published by the Free Software Foundation, either version 2 |
|
25 of the License or (at your option) any later version. |
|
26 */ |
|
27 #include "inc/Main.h" |
|
28 #include "inc/debug.h" |
|
29 #include "inc/Endian.h" |
|
30 #include "inc/Pass.h" |
|
31 #include <cstring> |
|
32 #include <cstdlib> |
|
33 #include <cassert> |
|
34 #include "inc/Segment.h" |
|
35 #include "inc/Code.h" |
|
36 #include "inc/Rule.h" |
|
37 #include "inc/Error.h" |
|
38 |
|
39 using namespace graphite2; |
|
40 using vm::Machine; |
|
41 typedef Machine::Code Code; |
|
42 |
|
43 |
|
44 Pass::Pass() |
|
45 : m_silf(0), |
|
46 m_cols(0), |
|
47 m_rules(0), |
|
48 m_ruleMap(0), |
|
49 m_startStates(0), |
|
50 m_transitions(0), |
|
51 m_states(0), |
|
52 m_flags(0), |
|
53 m_iMaxLoop(0), |
|
54 m_numGlyphs(0), |
|
55 m_numRules(0), |
|
56 m_numStates(0), |
|
57 m_numTransition(0), |
|
58 m_numSuccess(0), |
|
59 m_numColumns(0), |
|
60 m_minPreCtxt(0), |
|
61 m_maxPreCtxt(0) |
|
62 { |
|
63 } |
|
64 |
|
65 Pass::~Pass() |
|
66 { |
|
67 free(m_cols); |
|
68 free(m_startStates); |
|
69 free(m_transitions); |
|
70 free(m_states); |
|
71 free(m_ruleMap); |
|
72 |
|
73 delete [] m_rules; |
|
74 } |
|
75 |
|
76 bool Pass::readPass(const byte * const pass_start, size_t pass_length, size_t subtable_base, GR_MAYBE_UNUSED Face & face, Error &e) |
|
77 { |
|
78 const byte * p = pass_start, |
|
79 * const pass_end = p + pass_length; |
|
80 size_t numRanges; |
|
81 |
|
82 if (e.test(pass_length < 40, E_BADPASSLENGTH)) return face.error(e); |
|
83 // Read in basic values |
|
84 m_flags = be::read<byte>(p); |
|
85 m_iMaxLoop = be::read<byte>(p); |
|
86 be::skip<byte>(p,2); // skip maxContext & maxBackup |
|
87 m_numRules = be::read<uint16>(p); |
|
88 be::skip<uint16>(p); // fsmOffset - not sure why we would want this |
|
89 const byte * const pcCode = pass_start + be::read<uint32>(p) - subtable_base, |
|
90 * const rcCode = pass_start + be::read<uint32>(p) - subtable_base, |
|
91 * const aCode = pass_start + be::read<uint32>(p) - subtable_base; |
|
92 be::skip<uint32>(p); |
|
93 m_numStates = be::read<uint16>(p); |
|
94 m_numTransition = be::read<uint16>(p); |
|
95 m_numSuccess = be::read<uint16>(p); |
|
96 m_numColumns = be::read<uint16>(p); |
|
97 numRanges = be::read<uint16>(p); |
|
98 be::skip<uint16>(p, 3); // skip searchRange, entrySelector & rangeShift. |
|
99 assert(p - pass_start == 40); |
|
100 // Perform some sanity checks. |
|
101 if ( e.test(m_numTransition > m_numStates, E_BADNUMTRANS) |
|
102 || e.test(m_numSuccess > m_numStates, E_BADNUMSUCCESS) |
|
103 || e.test(m_numSuccess + m_numTransition < m_numStates, E_BADNUMSTATES) |
|
104 || e.test(numRanges == 0, E_NORANGES)) |
|
105 return face.error(e); |
|
106 |
|
107 m_successStart = m_numStates - m_numSuccess; |
|
108 if (e.test(p + numRanges * 6 - 4 > pass_end, E_BADPASSLENGTH)) return face.error(e); |
|
109 m_numGlyphs = be::peek<uint16>(p + numRanges * 6 - 4) + 1; |
|
110 // Calculate the start of various arrays. |
|
111 const byte * const ranges = p; |
|
112 be::skip<uint16>(p, numRanges*3); |
|
113 const byte * const o_rule_map = p; |
|
114 be::skip<uint16>(p, m_numSuccess + 1); |
|
115 |
|
116 // More sanity checks |
|
117 if (e.test(reinterpret_cast<const byte *>(o_rule_map + m_numSuccess*sizeof(uint16)) > pass_end |
|
118 || p > pass_end, E_BADRULEMAPLEN)) |
|
119 return face.error(e); |
|
120 const size_t numEntries = be::peek<uint16>(o_rule_map + m_numSuccess*sizeof(uint16)); |
|
121 const byte * const rule_map = p; |
|
122 be::skip<uint16>(p, numEntries); |
|
123 |
|
124 if (e.test(p + 2*sizeof(uint8) > pass_end, E_BADPASSLENGTH)) return face.error(e); |
|
125 m_minPreCtxt = be::read<uint8>(p); |
|
126 m_maxPreCtxt = be::read<uint8>(p); |
|
127 if (e.test(m_minPreCtxt > m_maxPreCtxt, E_BADCTXTLENBOUNDS)) return face.error(e); |
|
128 const byte * const start_states = p; |
|
129 be::skip<int16>(p, m_maxPreCtxt - m_minPreCtxt + 1); |
|
130 const uint16 * const sort_keys = reinterpret_cast<const uint16 *>(p); |
|
131 be::skip<uint16>(p, m_numRules); |
|
132 const byte * const precontext = p; |
|
133 be::skip<byte>(p, m_numRules); |
|
134 be::skip<byte>(p); // skip reserved byte |
|
135 |
|
136 if (e.test(p + sizeof(uint16) > pass_end, E_BADCTXTLENS)) return face.error(e); |
|
137 const size_t pass_constraint_len = be::read<uint16>(p); |
|
138 const uint16 * const o_constraint = reinterpret_cast<const uint16 *>(p); |
|
139 be::skip<uint16>(p, m_numRules + 1); |
|
140 const uint16 * const o_actions = reinterpret_cast<const uint16 *>(p); |
|
141 be::skip<uint16>(p, m_numRules + 1); |
|
142 const byte * const states = p; |
|
143 be::skip<int16>(p, m_numTransition*m_numColumns); |
|
144 be::skip<byte>(p); // skip reserved byte |
|
145 if (e.test(p != pcCode, E_BADPASSCCODEPTR) || e.test(p >= pass_end, E_BADPASSLENGTH)) return face.error(e); |
|
146 be::skip<byte>(p, pass_constraint_len); |
|
147 if (e.test(p != rcCode, E_BADRULECCODEPTR) || e.test(p >= pass_end, E_BADPASSLENGTH) |
|
148 || e.test(size_t(rcCode - pcCode) != pass_constraint_len, E_BADCCODELEN)) return face.error(e); |
|
149 be::skip<byte>(p, be::peek<uint16>(o_constraint + m_numRules)); |
|
150 if (e.test(p != aCode, E_BADACTIONCODEPTR) || e.test(p >= pass_end, E_BADPASSLENGTH)) return face.error(e); |
|
151 be::skip<byte>(p, be::peek<uint16>(o_actions + m_numRules)); |
|
152 |
|
153 // We should be at the end or within the pass |
|
154 if (e.test(p > pass_end, E_BADPASSLENGTH)) return face.error(e); |
|
155 |
|
156 // Load the pass constraint if there is one. |
|
157 if (pass_constraint_len) |
|
158 { |
|
159 face.error_context(face.error_context() + 1); |
|
160 m_cPConstraint = vm::Machine::Code(true, pcCode, pcCode + pass_constraint_len, |
|
161 precontext[0], be::peek<uint16>(sort_keys), *m_silf, face); |
|
162 if (e.test(!m_cPConstraint, E_OUTOFMEM) |
|
163 || e.test(m_cPConstraint.status(), m_cPConstraint.status() + E_CODEFAILURE)) |
|
164 return face.error(e); |
|
165 face.error_context(face.error_context() - 1); |
|
166 } |
|
167 if (!readRanges(ranges, numRanges, e)) return face.error(e); |
|
168 if (!readRules(rule_map, numEntries, precontext, sort_keys, |
|
169 o_constraint, rcCode, o_actions, aCode, face, e)) return false; |
|
170 #ifdef GRAPHITE2_TELEMETRY |
|
171 telemetry::category _states_cat(face.tele.states); |
|
172 #endif |
|
173 return readStates(start_states, states, o_rule_map, face, e); |
|
174 } |
|
175 |
|
176 |
|
177 bool Pass::readRules(const byte * rule_map, const size_t num_entries, |
|
178 const byte *precontext, const uint16 * sort_key, |
|
179 const uint16 * o_constraint, const byte *rc_data, |
|
180 const uint16 * o_action, const byte * ac_data, |
|
181 Face & face, Error &e) |
|
182 { |
|
183 const byte * const ac_data_end = ac_data + be::peek<uint16>(o_action + m_numRules); |
|
184 const byte * const rc_data_end = rc_data + be::peek<uint16>(o_constraint + m_numRules); |
|
185 |
|
186 if (e.test(!(m_rules = new Rule [m_numRules]), E_OUTOFMEM)) return face.error(e); |
|
187 precontext += m_numRules; |
|
188 sort_key += m_numRules; |
|
189 o_constraint += m_numRules; |
|
190 o_action += m_numRules; |
|
191 |
|
192 // Load rules. |
|
193 const byte * ac_begin = 0, * rc_begin = 0, |
|
194 * ac_end = ac_data + be::peek<uint16>(o_action), |
|
195 * rc_end = rc_data + be::peek<uint16>(o_constraint); |
|
196 Rule * r = m_rules + m_numRules - 1; |
|
197 for (size_t n = m_numRules; n; --n, --r, ac_end = ac_begin, rc_end = rc_begin) |
|
198 { |
|
199 face.error_context((face.error_context() & 0xFFFF00) + EC_ARULE + ((n - 1) << 24)); |
|
200 r->preContext = *--precontext; |
|
201 r->sort = be::peek<uint16>(--sort_key); |
|
202 #ifndef NDEBUG |
|
203 r->rule_idx = n - 1; |
|
204 #endif |
|
205 if (r->sort > 63 || r->preContext >= r->sort || r->preContext > m_maxPreCtxt || r->preContext < m_minPreCtxt) |
|
206 return false; |
|
207 ac_begin = ac_data + be::peek<uint16>(--o_action); |
|
208 --o_constraint; |
|
209 rc_begin = be::peek<uint16>(o_constraint) ? rc_data + be::peek<uint16>(o_constraint) : rc_end; |
|
210 |
|
211 if (ac_begin > ac_end || ac_begin > ac_data_end || ac_end > ac_data_end |
|
212 || rc_begin > rc_end || rc_begin > rc_data_end || rc_end > rc_data_end) |
|
213 return false; |
|
214 r->action = new vm::Machine::Code(false, ac_begin, ac_end, r->preContext, r->sort, *m_silf, face); |
|
215 r->constraint = new vm::Machine::Code(true, rc_begin, rc_end, r->preContext, r->sort, *m_silf, face); |
|
216 |
|
217 if (e.test(!r->action || !r->constraint, E_OUTOFMEM) |
|
218 || e.test(r->action->status() != Code::loaded, r->action->status() + E_CODEFAILURE) |
|
219 || e.test(r->constraint->status() != Code::loaded, r->constraint->status() + E_CODEFAILURE) |
|
220 || e.test(!r->constraint->immutable(), E_MUTABLECCODE)) |
|
221 return face.error(e); |
|
222 } |
|
223 |
|
224 // Load the rule entries map |
|
225 face.error_context((face.error_context() & 0xFFFF00) + EC_APASS); |
|
226 RuleEntry * re = m_ruleMap = gralloc<RuleEntry>(num_entries); |
|
227 if (e.test(!re, E_OUTOFMEM)) return face.error(e); |
|
228 for (size_t n = num_entries; n; --n, ++re) |
|
229 { |
|
230 const ptrdiff_t rn = be::read<uint16>(rule_map); |
|
231 if (e.test(rn >= m_numRules, E_BADRULENUM)) return face.error(e); |
|
232 re->rule = m_rules + rn; |
|
233 } |
|
234 |
|
235 return true; |
|
236 } |
|
237 |
|
238 static int cmpRuleEntry(const void *a, const void *b) { return (*(RuleEntry *)a < *(RuleEntry *)b ? -1 : |
|
239 (*(RuleEntry *)b < *(RuleEntry *)a ? 1 : 0)); } |
|
240 |
|
241 bool Pass::readStates(const byte * starts, const byte *states, const byte * o_rule_map, GR_MAYBE_UNUSED Face & face, Error &e) |
|
242 { |
|
243 #ifdef GRAPHITE2_TELEMETRY |
|
244 telemetry::category _states_cat(face.tele.starts); |
|
245 #endif |
|
246 m_startStates = gralloc<uint16>(m_maxPreCtxt - m_minPreCtxt + 1); |
|
247 #ifdef GRAPHITE2_TELEMETRY |
|
248 telemetry::set_category(face.tele.states); |
|
249 #endif |
|
250 m_states = gralloc<State>(m_numStates); |
|
251 #ifdef GRAPHITE2_TELEMETRY |
|
252 telemetry::set_category(face.tele.transitions); |
|
253 #endif |
|
254 m_transitions = gralloc<uint16>(m_numTransition * m_numColumns); |
|
255 |
|
256 if (e.test(!m_startStates || !m_states || !m_transitions, E_OUTOFMEM)) return face.error(e); |
|
257 // load start states |
|
258 for (uint16 * s = m_startStates, |
|
259 * const s_end = s + m_maxPreCtxt - m_minPreCtxt + 1; s != s_end; ++s) |
|
260 { |
|
261 *s = be::read<uint16>(starts); |
|
262 if (e.test(*s >= m_numStates, E_BADSTATE)) |
|
263 { |
|
264 face.error_context((face.error_context() & 0xFFFF00) + EC_ASTARTS + ((s - m_startStates) << 24)); |
|
265 return face.error(e); // true; |
|
266 } |
|
267 } |
|
268 |
|
269 // load state transition table. |
|
270 for (uint16 * t = m_transitions, |
|
271 * const t_end = t + m_numTransition*m_numColumns; t != t_end; ++t) |
|
272 { |
|
273 *t = be::read<uint16>(states); |
|
274 if (e.test(*t >= m_numStates, E_BADSTATE)) |
|
275 { |
|
276 face.error_context((face.error_context() & 0xFFFF00) + EC_ATRANS + (((t - m_transitions) / m_numColumns) << 24)); |
|
277 return face.error(e); |
|
278 } |
|
279 } |
|
280 |
|
281 State * s = m_states, |
|
282 * const success_begin = m_states + m_numStates - m_numSuccess; |
|
283 const RuleEntry * rule_map_end = m_ruleMap + be::peek<uint16>(o_rule_map + m_numSuccess*sizeof(uint16)); |
|
284 for (size_t n = m_numStates; n; --n, ++s) |
|
285 { |
|
286 RuleEntry * const begin = s < success_begin ? 0 : m_ruleMap + be::read<uint16>(o_rule_map), |
|
287 * const end = s < success_begin ? 0 : m_ruleMap + be::peek<uint16>(o_rule_map); |
|
288 |
|
289 if (e.test(begin >= rule_map_end || end > rule_map_end || begin > end, E_BADRULEMAPPING)) |
|
290 { |
|
291 face.error_context((face.error_context() & 0xFFFF00) + EC_ARULEMAP + (n << 24)); |
|
292 return face.error(e); |
|
293 } |
|
294 s->rules = begin; |
|
295 s->rules_end = (end - begin <= FiniteStateMachine::MAX_RULES)? end : |
|
296 begin + FiniteStateMachine::MAX_RULES; |
|
297 qsort(begin, end - begin, sizeof(RuleEntry), &cmpRuleEntry); |
|
298 } |
|
299 |
|
300 return true; |
|
301 } |
|
302 |
|
303 bool Pass::readRanges(const byte * ranges, size_t num_ranges, Error &e) |
|
304 { |
|
305 m_cols = gralloc<uint16>(m_numGlyphs); |
|
306 if (e.test(!m_cols, E_OUTOFMEM)) return false; |
|
307 memset(m_cols, 0xFF, m_numGlyphs * sizeof(uint16)); |
|
308 for (size_t n = num_ranges; n; --n) |
|
309 { |
|
310 uint16 * ci = m_cols + be::read<uint16>(ranges), |
|
311 * ci_end = m_cols + be::read<uint16>(ranges) + 1, |
|
312 col = be::read<uint16>(ranges); |
|
313 |
|
314 if (e.test(ci >= ci_end || ci_end > m_cols+m_numGlyphs || col >= m_numColumns, E_BADRANGE)) |
|
315 return false; |
|
316 |
|
317 // A glyph must only belong to one column at a time |
|
318 while (ci != ci_end && *ci == 0xffff) |
|
319 *ci++ = col; |
|
320 |
|
321 if (e.test(ci != ci_end, E_BADRANGE)) |
|
322 return false; |
|
323 } |
|
324 return true; |
|
325 } |
|
326 |
|
327 |
|
328 void Pass::runGraphite(Machine & m, FiniteStateMachine & fsm) const |
|
329 { |
|
330 Slot *s = m.slotMap().segment.first(); |
|
331 if (!s || !testPassConstraint(m)) return; |
|
332 Slot *currHigh = s->next(); |
|
333 |
|
334 #if !defined GRAPHITE2_NTRACING |
|
335 if (fsm.dbgout) *fsm.dbgout << "rules" << json::array; |
|
336 json::closer rules_array_closer(fsm.dbgout); |
|
337 #endif |
|
338 |
|
339 m.slotMap().highwater(currHigh); |
|
340 int lc = m_iMaxLoop; |
|
341 do |
|
342 { |
|
343 findNDoRule(s, m, fsm); |
|
344 if (s && (m.slotMap().highpassed() || s == m.slotMap().highwater() || --lc == 0)) { |
|
345 if (!lc) |
|
346 { |
|
347 // if (dbgout) *dbgout << json::item << json::flat << rule_event(-1, s, 1); |
|
348 s = m.slotMap().highwater(); |
|
349 } |
|
350 lc = m_iMaxLoop; |
|
351 if (s) |
|
352 m.slotMap().highwater(s->next()); |
|
353 } |
|
354 } while (s); |
|
355 } |
|
356 |
|
357 bool Pass::runFSM(FiniteStateMachine& fsm, Slot * slot) const |
|
358 { |
|
359 fsm.reset(slot, m_maxPreCtxt); |
|
360 if (fsm.slots.context() < m_minPreCtxt) |
|
361 return false; |
|
362 |
|
363 uint16 state = m_startStates[m_maxPreCtxt - fsm.slots.context()]; |
|
364 uint8 free_slots = SlotMap::MAX_SLOTS; |
|
365 do |
|
366 { |
|
367 fsm.slots.pushSlot(slot); |
|
368 if (--free_slots == 0 |
|
369 || slot->gid() >= m_numGlyphs |
|
370 || m_cols[slot->gid()] == 0xffffU |
|
371 || state >= m_numTransition) |
|
372 return free_slots != 0; |
|
373 |
|
374 const uint16 * transitions = m_transitions + state*m_numColumns; |
|
375 state = transitions[m_cols[slot->gid()]]; |
|
376 if (state >= m_successStart) |
|
377 fsm.rules.accumulate_rules(m_states[state]); |
|
378 |
|
379 slot = slot->next(); |
|
380 } while (state != 0 && slot); |
|
381 |
|
382 fsm.slots.pushSlot(slot); |
|
383 return true; |
|
384 } |
|
385 |
|
386 #if !defined GRAPHITE2_NTRACING |
|
387 |
|
388 inline |
|
389 Slot * input_slot(const SlotMap & slots, const int n) |
|
390 { |
|
391 Slot * s = slots[slots.context() + n]; |
|
392 if (!s->isCopied()) return s; |
|
393 |
|
394 return s->prev() ? s->prev()->next() : (s->next() ? s->next()->prev() : slots.segment.last()); |
|
395 } |
|
396 |
|
397 inline |
|
398 Slot * output_slot(const SlotMap & slots, const int n) |
|
399 { |
|
400 Slot * s = slots[slots.context() + n - 1]; |
|
401 return s ? s->next() : slots.segment.first(); |
|
402 } |
|
403 |
|
404 #endif //!defined GRAPHITE2_NTRACING |
|
405 |
|
406 void Pass::findNDoRule(Slot * & slot, Machine &m, FiniteStateMachine & fsm) const |
|
407 { |
|
408 assert(slot); |
|
409 |
|
410 if (runFSM(fsm, slot)) |
|
411 { |
|
412 // Search for the first rule which passes the constraint |
|
413 const RuleEntry * r = fsm.rules.begin(), |
|
414 * const re = fsm.rules.end(); |
|
415 while (r != re && !testConstraint(*r->rule, m)) ++r; |
|
416 |
|
417 #if !defined GRAPHITE2_NTRACING |
|
418 if (fsm.dbgout) |
|
419 { |
|
420 if (fsm.rules.size() != 0) |
|
421 { |
|
422 *fsm.dbgout << json::item << json::object; |
|
423 dumpRuleEventConsidered(fsm, *r); |
|
424 if (r != re) |
|
425 { |
|
426 const int adv = doAction(r->rule->action, slot, m); |
|
427 dumpRuleEventOutput(fsm, *r->rule, slot); |
|
428 if (r->rule->action->deletes()) fsm.slots.collectGarbage(); |
|
429 adjustSlot(adv, slot, fsm.slots); |
|
430 *fsm.dbgout << "cursor" << objectid(dslot(&fsm.slots.segment, slot)) |
|
431 << json::close; // Close RuelEvent object |
|
432 |
|
433 return; |
|
434 } |
|
435 else |
|
436 { |
|
437 *fsm.dbgout << json::close // close "considered" array |
|
438 << "output" << json::null |
|
439 << "cursor" << objectid(dslot(&fsm.slots.segment, slot->next())) |
|
440 << json::close; |
|
441 } |
|
442 } |
|
443 } |
|
444 else |
|
445 #endif |
|
446 { |
|
447 if (r != re) |
|
448 { |
|
449 const int adv = doAction(r->rule->action, slot, m); |
|
450 if (r->rule->action->deletes()) fsm.slots.collectGarbage(); |
|
451 adjustSlot(adv, slot, fsm.slots); |
|
452 return; |
|
453 } |
|
454 } |
|
455 } |
|
456 |
|
457 slot = slot->next(); |
|
458 } |
|
459 |
|
460 #if !defined GRAPHITE2_NTRACING |
|
461 |
|
462 void Pass::dumpRuleEventConsidered(const FiniteStateMachine & fsm, const RuleEntry & re) const |
|
463 { |
|
464 *fsm.dbgout << "considered" << json::array; |
|
465 for (const RuleEntry *r = fsm.rules.begin(); r != &re; ++r) |
|
466 { |
|
467 if (r->rule->preContext > fsm.slots.context()) continue; |
|
468 *fsm.dbgout << json::flat << json::object |
|
469 << "id" << r->rule - m_rules |
|
470 << "failed" << true |
|
471 << "input" << json::flat << json::object |
|
472 << "start" << objectid(dslot(&fsm.slots.segment, input_slot(fsm.slots, -r->rule->preContext))) |
|
473 << "length" << r->rule->sort |
|
474 << json::close // close "input" |
|
475 << json::close; // close Rule object |
|
476 } |
|
477 } |
|
478 |
|
479 |
|
480 void Pass::dumpRuleEventOutput(const FiniteStateMachine & fsm, const Rule & r, Slot * const last_slot) const |
|
481 { |
|
482 *fsm.dbgout << json::item << json::flat << json::object |
|
483 << "id" << &r - m_rules |
|
484 << "failed" << false |
|
485 << "input" << json::flat << json::object |
|
486 << "start" << objectid(dslot(&fsm.slots.segment, input_slot(fsm.slots, 0))) |
|
487 << "length" << r.sort - r.preContext |
|
488 << json::close // close "input" |
|
489 << json::close // close Rule object |
|
490 << json::close // close considered array |
|
491 << "output" << json::object |
|
492 << "range" << json::flat << json::object |
|
493 << "start" << objectid(dslot(&fsm.slots.segment, input_slot(fsm.slots, 0))) |
|
494 << "end" << objectid(dslot(&fsm.slots.segment, last_slot)) |
|
495 << json::close // close "input" |
|
496 << "slots" << json::array; |
|
497 const Position rsb_prepos = last_slot ? last_slot->origin() : fsm.slots.segment.advance(); |
|
498 fsm.slots.segment.positionSlots(0); |
|
499 |
|
500 for(Slot * slot = output_slot(fsm.slots, 0); slot != last_slot; slot = slot->next()) |
|
501 *fsm.dbgout << dslot(&fsm.slots.segment, slot); |
|
502 *fsm.dbgout << json::close // close "slots" |
|
503 << "postshift" << (last_slot ? last_slot->origin() : fsm.slots.segment.advance()) - rsb_prepos |
|
504 << json::close; // close "output" object |
|
505 |
|
506 } |
|
507 |
|
508 #endif |
|
509 |
|
510 |
|
511 inline |
|
512 bool Pass::testPassConstraint(Machine & m) const |
|
513 { |
|
514 if (!m_cPConstraint) return true; |
|
515 |
|
516 assert(m_cPConstraint.constraint()); |
|
517 |
|
518 m.slotMap().reset(*m.slotMap().segment.first(), 0); |
|
519 m.slotMap().pushSlot(m.slotMap().segment.first()); |
|
520 vm::slotref * map = m.slotMap().begin(); |
|
521 const uint32 ret = m_cPConstraint.run(m, map); |
|
522 |
|
523 #if !defined GRAPHITE2_NTRACING |
|
524 json * const dbgout = m.slotMap().segment.getFace()->logger(); |
|
525 if (dbgout) |
|
526 *dbgout << "constraint" << (ret && m.status() == Machine::finished); |
|
527 #endif |
|
528 |
|
529 return ret && m.status() == Machine::finished; |
|
530 } |
|
531 |
|
532 |
|
533 bool Pass::testConstraint(const Rule & r, Machine & m) const |
|
534 { |
|
535 const uint16 curr_context = m.slotMap().context(); |
|
536 if (unsigned(r.sort - r.preContext) > m.slotMap().size() - curr_context |
|
537 || curr_context - r.preContext < 0) return false; |
|
538 if (!*r.constraint) return true; |
|
539 assert(r.constraint->constraint()); |
|
540 |
|
541 vm::slotref * map = m.slotMap().begin() + curr_context - r.preContext; |
|
542 for (int n = r.sort; n && map; --n, ++map) |
|
543 { |
|
544 if (!*map) continue; |
|
545 const int32 ret = r.constraint->run(m, map); |
|
546 if (!ret || m.status() != Machine::finished) |
|
547 return false; |
|
548 } |
|
549 |
|
550 return true; |
|
551 } |
|
552 |
|
553 |
|
554 void SlotMap::collectGarbage() |
|
555 { |
|
556 for(Slot **s = begin(), *const *const se = end() - 1; s != se; ++s) { |
|
557 Slot *& slot = *s; |
|
558 if(slot->isDeleted() || slot->isCopied()) |
|
559 segment.freeSlot(slot); |
|
560 } |
|
561 } |
|
562 |
|
563 |
|
564 |
|
565 int Pass::doAction(const Code *codeptr, Slot * & slot_out, vm::Machine & m) const |
|
566 { |
|
567 assert(codeptr); |
|
568 if (!*codeptr) return 0; |
|
569 SlotMap & smap = m.slotMap(); |
|
570 vm::slotref * map = &smap[smap.context()]; |
|
571 smap.highpassed(false); |
|
572 |
|
573 int32 ret = codeptr->run(m, map); |
|
574 |
|
575 if (m.status() != Machine::finished) |
|
576 { |
|
577 slot_out = NULL; |
|
578 smap.highwater(0); |
|
579 return 0; |
|
580 } |
|
581 |
|
582 slot_out = *map; |
|
583 return ret; |
|
584 } |
|
585 |
|
586 |
|
587 void Pass::adjustSlot(int delta, Slot * & slot_out, SlotMap & smap) const |
|
588 { |
|
589 if (delta < 0) |
|
590 { |
|
591 if (!slot_out) |
|
592 { |
|
593 slot_out = smap.segment.last(); |
|
594 ++delta; |
|
595 if (smap.highpassed() && !smap.highwater()) |
|
596 smap.highpassed(false); |
|
597 } |
|
598 while (++delta <= 0 && slot_out) |
|
599 { |
|
600 if (smap.highpassed() && smap.highwater() == slot_out) |
|
601 smap.highpassed(false); |
|
602 slot_out = slot_out->prev(); |
|
603 } |
|
604 } |
|
605 else if (delta > 0) |
|
606 { |
|
607 if (!slot_out) |
|
608 { |
|
609 slot_out = smap.segment.first(); |
|
610 --delta; |
|
611 } |
|
612 while (--delta >= 0 && slot_out) |
|
613 { |
|
614 slot_out = slot_out->next(); |
|
615 if (slot_out == smap.highwater() && slot_out) |
|
616 smap.highpassed(true); |
|
617 } |
|
618 } |
|
619 } |
|
620 |