gfx/graphite2/src/Pass.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial