michael@0: /* GRAPHITE2 LICENSING michael@0: michael@0: Copyright 2010, SIL International michael@0: All rights reserved. michael@0: michael@0: This library is free software; you can redistribute it and/or modify michael@0: it under the terms of the GNU Lesser General Public License as published michael@0: by the Free Software Foundation; either version 2.1 of License, or michael@0: (at your option) any later version. michael@0: michael@0: This program is distributed in the hope that it will be useful, michael@0: but WITHOUT ANY WARRANTY; without even the implied warranty of michael@0: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU michael@0: Lesser General Public License for more details. michael@0: michael@0: You should also have received a copy of the GNU Lesser General Public michael@0: License along with this library in the file named "LICENSE". michael@0: If not, write to the Free Software Foundation, 51 Franklin Street, michael@0: Suite 500, Boston, MA 02110-1335, USA or visit their web page on the michael@0: internet at http://www.fsf.org/licenses/lgpl.html. michael@0: michael@0: Alternatively, the contents of this file may be used under the terms of the michael@0: Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public michael@0: License, as published by the Free Software Foundation, either version 2 michael@0: of the License or (at your option) any later version. michael@0: */ michael@0: #include "inc/Main.h" michael@0: #include "inc/debug.h" michael@0: #include "inc/Endian.h" michael@0: #include "inc/Pass.h" michael@0: #include michael@0: #include michael@0: #include michael@0: #include "inc/Segment.h" michael@0: #include "inc/Code.h" michael@0: #include "inc/Rule.h" michael@0: #include "inc/Error.h" michael@0: michael@0: using namespace graphite2; michael@0: using vm::Machine; michael@0: typedef Machine::Code Code; michael@0: michael@0: michael@0: Pass::Pass() michael@0: : m_silf(0), michael@0: m_cols(0), michael@0: m_rules(0), michael@0: m_ruleMap(0), michael@0: m_startStates(0), michael@0: m_transitions(0), michael@0: m_states(0), michael@0: m_flags(0), michael@0: m_iMaxLoop(0), michael@0: m_numGlyphs(0), michael@0: m_numRules(0), michael@0: m_numStates(0), michael@0: m_numTransition(0), michael@0: m_numSuccess(0), michael@0: m_numColumns(0), michael@0: m_minPreCtxt(0), michael@0: m_maxPreCtxt(0) michael@0: { michael@0: } michael@0: michael@0: Pass::~Pass() michael@0: { michael@0: free(m_cols); michael@0: free(m_startStates); michael@0: free(m_transitions); michael@0: free(m_states); michael@0: free(m_ruleMap); michael@0: michael@0: delete [] m_rules; michael@0: } michael@0: michael@0: 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: { michael@0: const byte * p = pass_start, michael@0: * const pass_end = p + pass_length; michael@0: size_t numRanges; michael@0: michael@0: if (e.test(pass_length < 40, E_BADPASSLENGTH)) return face.error(e); michael@0: // Read in basic values michael@0: m_flags = be::read(p); michael@0: m_iMaxLoop = be::read(p); michael@0: be::skip(p,2); // skip maxContext & maxBackup michael@0: m_numRules = be::read(p); michael@0: be::skip(p); // fsmOffset - not sure why we would want this michael@0: const byte * const pcCode = pass_start + be::read(p) - subtable_base, michael@0: * const rcCode = pass_start + be::read(p) - subtable_base, michael@0: * const aCode = pass_start + be::read(p) - subtable_base; michael@0: be::skip(p); michael@0: m_numStates = be::read(p); michael@0: m_numTransition = be::read(p); michael@0: m_numSuccess = be::read(p); michael@0: m_numColumns = be::read(p); michael@0: numRanges = be::read(p); michael@0: be::skip(p, 3); // skip searchRange, entrySelector & rangeShift. michael@0: assert(p - pass_start == 40); michael@0: // Perform some sanity checks. michael@0: if ( e.test(m_numTransition > m_numStates, E_BADNUMTRANS) michael@0: || e.test(m_numSuccess > m_numStates, E_BADNUMSUCCESS) michael@0: || e.test(m_numSuccess + m_numTransition < m_numStates, E_BADNUMSTATES) michael@0: || e.test(numRanges == 0, E_NORANGES)) michael@0: return face.error(e); michael@0: michael@0: m_successStart = m_numStates - m_numSuccess; michael@0: if (e.test(p + numRanges * 6 - 4 > pass_end, E_BADPASSLENGTH)) return face.error(e); michael@0: m_numGlyphs = be::peek(p + numRanges * 6 - 4) + 1; michael@0: // Calculate the start of various arrays. michael@0: const byte * const ranges = p; michael@0: be::skip(p, numRanges*3); michael@0: const byte * const o_rule_map = p; michael@0: be::skip(p, m_numSuccess + 1); michael@0: michael@0: // More sanity checks michael@0: if (e.test(reinterpret_cast(o_rule_map + m_numSuccess*sizeof(uint16)) > pass_end michael@0: || p > pass_end, E_BADRULEMAPLEN)) michael@0: return face.error(e); michael@0: const size_t numEntries = be::peek(o_rule_map + m_numSuccess*sizeof(uint16)); michael@0: const byte * const rule_map = p; michael@0: be::skip(p, numEntries); michael@0: michael@0: if (e.test(p + 2*sizeof(uint8) > pass_end, E_BADPASSLENGTH)) return face.error(e); michael@0: m_minPreCtxt = be::read(p); michael@0: m_maxPreCtxt = be::read(p); michael@0: if (e.test(m_minPreCtxt > m_maxPreCtxt, E_BADCTXTLENBOUNDS)) return face.error(e); michael@0: const byte * const start_states = p; michael@0: be::skip(p, m_maxPreCtxt - m_minPreCtxt + 1); michael@0: const uint16 * const sort_keys = reinterpret_cast(p); michael@0: be::skip(p, m_numRules); michael@0: const byte * const precontext = p; michael@0: be::skip(p, m_numRules); michael@0: be::skip(p); // skip reserved byte michael@0: michael@0: if (e.test(p + sizeof(uint16) > pass_end, E_BADCTXTLENS)) return face.error(e); michael@0: const size_t pass_constraint_len = be::read(p); michael@0: const uint16 * const o_constraint = reinterpret_cast(p); michael@0: be::skip(p, m_numRules + 1); michael@0: const uint16 * const o_actions = reinterpret_cast(p); michael@0: be::skip(p, m_numRules + 1); michael@0: const byte * const states = p; michael@0: be::skip(p, m_numTransition*m_numColumns); michael@0: be::skip(p); // skip reserved byte michael@0: if (e.test(p != pcCode, E_BADPASSCCODEPTR) || e.test(p >= pass_end, E_BADPASSLENGTH)) return face.error(e); michael@0: be::skip(p, pass_constraint_len); michael@0: if (e.test(p != rcCode, E_BADRULECCODEPTR) || e.test(p >= pass_end, E_BADPASSLENGTH) michael@0: || e.test(size_t(rcCode - pcCode) != pass_constraint_len, E_BADCCODELEN)) return face.error(e); michael@0: be::skip(p, be::peek(o_constraint + m_numRules)); michael@0: if (e.test(p != aCode, E_BADACTIONCODEPTR) || e.test(p >= pass_end, E_BADPASSLENGTH)) return face.error(e); michael@0: be::skip(p, be::peek(o_actions + m_numRules)); michael@0: michael@0: // We should be at the end or within the pass michael@0: if (e.test(p > pass_end, E_BADPASSLENGTH)) return face.error(e); michael@0: michael@0: // Load the pass constraint if there is one. michael@0: if (pass_constraint_len) michael@0: { michael@0: face.error_context(face.error_context() + 1); michael@0: m_cPConstraint = vm::Machine::Code(true, pcCode, pcCode + pass_constraint_len, michael@0: precontext[0], be::peek(sort_keys), *m_silf, face); michael@0: if (e.test(!m_cPConstraint, E_OUTOFMEM) michael@0: || e.test(m_cPConstraint.status(), m_cPConstraint.status() + E_CODEFAILURE)) michael@0: return face.error(e); michael@0: face.error_context(face.error_context() - 1); michael@0: } michael@0: if (!readRanges(ranges, numRanges, e)) return face.error(e); michael@0: if (!readRules(rule_map, numEntries, precontext, sort_keys, michael@0: o_constraint, rcCode, o_actions, aCode, face, e)) return false; michael@0: #ifdef GRAPHITE2_TELEMETRY michael@0: telemetry::category _states_cat(face.tele.states); michael@0: #endif michael@0: return readStates(start_states, states, o_rule_map, face, e); michael@0: } michael@0: michael@0: michael@0: bool Pass::readRules(const byte * rule_map, const size_t num_entries, michael@0: const byte *precontext, const uint16 * sort_key, michael@0: const uint16 * o_constraint, const byte *rc_data, michael@0: const uint16 * o_action, const byte * ac_data, michael@0: Face & face, Error &e) michael@0: { michael@0: const byte * const ac_data_end = ac_data + be::peek(o_action + m_numRules); michael@0: const byte * const rc_data_end = rc_data + be::peek(o_constraint + m_numRules); michael@0: michael@0: if (e.test(!(m_rules = new Rule [m_numRules]), E_OUTOFMEM)) return face.error(e); michael@0: precontext += m_numRules; michael@0: sort_key += m_numRules; michael@0: o_constraint += m_numRules; michael@0: o_action += m_numRules; michael@0: michael@0: // Load rules. michael@0: const byte * ac_begin = 0, * rc_begin = 0, michael@0: * ac_end = ac_data + be::peek(o_action), michael@0: * rc_end = rc_data + be::peek(o_constraint); michael@0: Rule * r = m_rules + m_numRules - 1; michael@0: for (size_t n = m_numRules; n; --n, --r, ac_end = ac_begin, rc_end = rc_begin) michael@0: { michael@0: face.error_context((face.error_context() & 0xFFFF00) + EC_ARULE + ((n - 1) << 24)); michael@0: r->preContext = *--precontext; michael@0: r->sort = be::peek(--sort_key); michael@0: #ifndef NDEBUG michael@0: r->rule_idx = n - 1; michael@0: #endif michael@0: if (r->sort > 63 || r->preContext >= r->sort || r->preContext > m_maxPreCtxt || r->preContext < m_minPreCtxt) michael@0: return false; michael@0: ac_begin = ac_data + be::peek(--o_action); michael@0: --o_constraint; michael@0: rc_begin = be::peek(o_constraint) ? rc_data + be::peek(o_constraint) : rc_end; michael@0: michael@0: if (ac_begin > ac_end || ac_begin > ac_data_end || ac_end > ac_data_end michael@0: || rc_begin > rc_end || rc_begin > rc_data_end || rc_end > rc_data_end) michael@0: return false; michael@0: r->action = new vm::Machine::Code(false, ac_begin, ac_end, r->preContext, r->sort, *m_silf, face); michael@0: r->constraint = new vm::Machine::Code(true, rc_begin, rc_end, r->preContext, r->sort, *m_silf, face); michael@0: michael@0: if (e.test(!r->action || !r->constraint, E_OUTOFMEM) michael@0: || e.test(r->action->status() != Code::loaded, r->action->status() + E_CODEFAILURE) michael@0: || e.test(r->constraint->status() != Code::loaded, r->constraint->status() + E_CODEFAILURE) michael@0: || e.test(!r->constraint->immutable(), E_MUTABLECCODE)) michael@0: return face.error(e); michael@0: } michael@0: michael@0: // Load the rule entries map michael@0: face.error_context((face.error_context() & 0xFFFF00) + EC_APASS); michael@0: RuleEntry * re = m_ruleMap = gralloc(num_entries); michael@0: if (e.test(!re, E_OUTOFMEM)) return face.error(e); michael@0: for (size_t n = num_entries; n; --n, ++re) michael@0: { michael@0: const ptrdiff_t rn = be::read(rule_map); michael@0: if (e.test(rn >= m_numRules, E_BADRULENUM)) return face.error(e); michael@0: re->rule = m_rules + rn; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: static int cmpRuleEntry(const void *a, const void *b) { return (*(RuleEntry *)a < *(RuleEntry *)b ? -1 : michael@0: (*(RuleEntry *)b < *(RuleEntry *)a ? 1 : 0)); } michael@0: michael@0: bool Pass::readStates(const byte * starts, const byte *states, const byte * o_rule_map, GR_MAYBE_UNUSED Face & face, Error &e) michael@0: { michael@0: #ifdef GRAPHITE2_TELEMETRY michael@0: telemetry::category _states_cat(face.tele.starts); michael@0: #endif michael@0: m_startStates = gralloc(m_maxPreCtxt - m_minPreCtxt + 1); michael@0: #ifdef GRAPHITE2_TELEMETRY michael@0: telemetry::set_category(face.tele.states); michael@0: #endif michael@0: m_states = gralloc(m_numStates); michael@0: #ifdef GRAPHITE2_TELEMETRY michael@0: telemetry::set_category(face.tele.transitions); michael@0: #endif michael@0: m_transitions = gralloc(m_numTransition * m_numColumns); michael@0: michael@0: if (e.test(!m_startStates || !m_states || !m_transitions, E_OUTOFMEM)) return face.error(e); michael@0: // load start states michael@0: for (uint16 * s = m_startStates, michael@0: * const s_end = s + m_maxPreCtxt - m_minPreCtxt + 1; s != s_end; ++s) michael@0: { michael@0: *s = be::read(starts); michael@0: if (e.test(*s >= m_numStates, E_BADSTATE)) michael@0: { michael@0: face.error_context((face.error_context() & 0xFFFF00) + EC_ASTARTS + ((s - m_startStates) << 24)); michael@0: return face.error(e); // true; michael@0: } michael@0: } michael@0: michael@0: // load state transition table. michael@0: for (uint16 * t = m_transitions, michael@0: * const t_end = t + m_numTransition*m_numColumns; t != t_end; ++t) michael@0: { michael@0: *t = be::read(states); michael@0: if (e.test(*t >= m_numStates, E_BADSTATE)) michael@0: { michael@0: face.error_context((face.error_context() & 0xFFFF00) + EC_ATRANS + (((t - m_transitions) / m_numColumns) << 24)); michael@0: return face.error(e); michael@0: } michael@0: } michael@0: michael@0: State * s = m_states, michael@0: * const success_begin = m_states + m_numStates - m_numSuccess; michael@0: const RuleEntry * rule_map_end = m_ruleMap + be::peek(o_rule_map + m_numSuccess*sizeof(uint16)); michael@0: for (size_t n = m_numStates; n; --n, ++s) michael@0: { michael@0: RuleEntry * const begin = s < success_begin ? 0 : m_ruleMap + be::read(o_rule_map), michael@0: * const end = s < success_begin ? 0 : m_ruleMap + be::peek(o_rule_map); michael@0: michael@0: if (e.test(begin >= rule_map_end || end > rule_map_end || begin > end, E_BADRULEMAPPING)) michael@0: { michael@0: face.error_context((face.error_context() & 0xFFFF00) + EC_ARULEMAP + (n << 24)); michael@0: return face.error(e); michael@0: } michael@0: s->rules = begin; michael@0: s->rules_end = (end - begin <= FiniteStateMachine::MAX_RULES)? end : michael@0: begin + FiniteStateMachine::MAX_RULES; michael@0: qsort(begin, end - begin, sizeof(RuleEntry), &cmpRuleEntry); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool Pass::readRanges(const byte * ranges, size_t num_ranges, Error &e) michael@0: { michael@0: m_cols = gralloc(m_numGlyphs); michael@0: if (e.test(!m_cols, E_OUTOFMEM)) return false; michael@0: memset(m_cols, 0xFF, m_numGlyphs * sizeof(uint16)); michael@0: for (size_t n = num_ranges; n; --n) michael@0: { michael@0: uint16 * ci = m_cols + be::read(ranges), michael@0: * ci_end = m_cols + be::read(ranges) + 1, michael@0: col = be::read(ranges); michael@0: michael@0: if (e.test(ci >= ci_end || ci_end > m_cols+m_numGlyphs || col >= m_numColumns, E_BADRANGE)) michael@0: return false; michael@0: michael@0: // A glyph must only belong to one column at a time michael@0: while (ci != ci_end && *ci == 0xffff) michael@0: *ci++ = col; michael@0: michael@0: if (e.test(ci != ci_end, E_BADRANGE)) michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: michael@0: void Pass::runGraphite(Machine & m, FiniteStateMachine & fsm) const michael@0: { michael@0: Slot *s = m.slotMap().segment.first(); michael@0: if (!s || !testPassConstraint(m)) return; michael@0: Slot *currHigh = s->next(); michael@0: michael@0: #if !defined GRAPHITE2_NTRACING michael@0: if (fsm.dbgout) *fsm.dbgout << "rules" << json::array; michael@0: json::closer rules_array_closer(fsm.dbgout); michael@0: #endif michael@0: michael@0: m.slotMap().highwater(currHigh); michael@0: int lc = m_iMaxLoop; michael@0: do michael@0: { michael@0: findNDoRule(s, m, fsm); michael@0: if (s && (m.slotMap().highpassed() || s == m.slotMap().highwater() || --lc == 0)) { michael@0: if (!lc) michael@0: { michael@0: // if (dbgout) *dbgout << json::item << json::flat << rule_event(-1, s, 1); michael@0: s = m.slotMap().highwater(); michael@0: } michael@0: lc = m_iMaxLoop; michael@0: if (s) michael@0: m.slotMap().highwater(s->next()); michael@0: } michael@0: } while (s); michael@0: } michael@0: michael@0: bool Pass::runFSM(FiniteStateMachine& fsm, Slot * slot) const michael@0: { michael@0: fsm.reset(slot, m_maxPreCtxt); michael@0: if (fsm.slots.context() < m_minPreCtxt) michael@0: return false; michael@0: michael@0: uint16 state = m_startStates[m_maxPreCtxt - fsm.slots.context()]; michael@0: uint8 free_slots = SlotMap::MAX_SLOTS; michael@0: do michael@0: { michael@0: fsm.slots.pushSlot(slot); michael@0: if (--free_slots == 0 michael@0: || slot->gid() >= m_numGlyphs michael@0: || m_cols[slot->gid()] == 0xffffU michael@0: || state >= m_numTransition) michael@0: return free_slots != 0; michael@0: michael@0: const uint16 * transitions = m_transitions + state*m_numColumns; michael@0: state = transitions[m_cols[slot->gid()]]; michael@0: if (state >= m_successStart) michael@0: fsm.rules.accumulate_rules(m_states[state]); michael@0: michael@0: slot = slot->next(); michael@0: } while (state != 0 && slot); michael@0: michael@0: fsm.slots.pushSlot(slot); michael@0: return true; michael@0: } michael@0: michael@0: #if !defined GRAPHITE2_NTRACING michael@0: michael@0: inline michael@0: Slot * input_slot(const SlotMap & slots, const int n) michael@0: { michael@0: Slot * s = slots[slots.context() + n]; michael@0: if (!s->isCopied()) return s; michael@0: michael@0: return s->prev() ? s->prev()->next() : (s->next() ? s->next()->prev() : slots.segment.last()); michael@0: } michael@0: michael@0: inline michael@0: Slot * output_slot(const SlotMap & slots, const int n) michael@0: { michael@0: Slot * s = slots[slots.context() + n - 1]; michael@0: return s ? s->next() : slots.segment.first(); michael@0: } michael@0: michael@0: #endif //!defined GRAPHITE2_NTRACING michael@0: michael@0: void Pass::findNDoRule(Slot * & slot, Machine &m, FiniteStateMachine & fsm) const michael@0: { michael@0: assert(slot); michael@0: michael@0: if (runFSM(fsm, slot)) michael@0: { michael@0: // Search for the first rule which passes the constraint michael@0: const RuleEntry * r = fsm.rules.begin(), michael@0: * const re = fsm.rules.end(); michael@0: while (r != re && !testConstraint(*r->rule, m)) ++r; michael@0: michael@0: #if !defined GRAPHITE2_NTRACING michael@0: if (fsm.dbgout) michael@0: { michael@0: if (fsm.rules.size() != 0) michael@0: { michael@0: *fsm.dbgout << json::item << json::object; michael@0: dumpRuleEventConsidered(fsm, *r); michael@0: if (r != re) michael@0: { michael@0: const int adv = doAction(r->rule->action, slot, m); michael@0: dumpRuleEventOutput(fsm, *r->rule, slot); michael@0: if (r->rule->action->deletes()) fsm.slots.collectGarbage(); michael@0: adjustSlot(adv, slot, fsm.slots); michael@0: *fsm.dbgout << "cursor" << objectid(dslot(&fsm.slots.segment, slot)) michael@0: << json::close; // Close RuelEvent object michael@0: michael@0: return; michael@0: } michael@0: else michael@0: { michael@0: *fsm.dbgout << json::close // close "considered" array michael@0: << "output" << json::null michael@0: << "cursor" << objectid(dslot(&fsm.slots.segment, slot->next())) michael@0: << json::close; michael@0: } michael@0: } michael@0: } michael@0: else michael@0: #endif michael@0: { michael@0: if (r != re) michael@0: { michael@0: const int adv = doAction(r->rule->action, slot, m); michael@0: if (r->rule->action->deletes()) fsm.slots.collectGarbage(); michael@0: adjustSlot(adv, slot, fsm.slots); michael@0: return; michael@0: } michael@0: } michael@0: } michael@0: michael@0: slot = slot->next(); michael@0: } michael@0: michael@0: #if !defined GRAPHITE2_NTRACING michael@0: michael@0: void Pass::dumpRuleEventConsidered(const FiniteStateMachine & fsm, const RuleEntry & re) const michael@0: { michael@0: *fsm.dbgout << "considered" << json::array; michael@0: for (const RuleEntry *r = fsm.rules.begin(); r != &re; ++r) michael@0: { michael@0: if (r->rule->preContext > fsm.slots.context()) continue; michael@0: *fsm.dbgout << json::flat << json::object michael@0: << "id" << r->rule - m_rules michael@0: << "failed" << true michael@0: << "input" << json::flat << json::object michael@0: << "start" << objectid(dslot(&fsm.slots.segment, input_slot(fsm.slots, -r->rule->preContext))) michael@0: << "length" << r->rule->sort michael@0: << json::close // close "input" michael@0: << json::close; // close Rule object michael@0: } michael@0: } michael@0: michael@0: michael@0: void Pass::dumpRuleEventOutput(const FiniteStateMachine & fsm, const Rule & r, Slot * const last_slot) const michael@0: { michael@0: *fsm.dbgout << json::item << json::flat << json::object michael@0: << "id" << &r - m_rules michael@0: << "failed" << false michael@0: << "input" << json::flat << json::object michael@0: << "start" << objectid(dslot(&fsm.slots.segment, input_slot(fsm.slots, 0))) michael@0: << "length" << r.sort - r.preContext michael@0: << json::close // close "input" michael@0: << json::close // close Rule object michael@0: << json::close // close considered array michael@0: << "output" << json::object michael@0: << "range" << json::flat << json::object michael@0: << "start" << objectid(dslot(&fsm.slots.segment, input_slot(fsm.slots, 0))) michael@0: << "end" << objectid(dslot(&fsm.slots.segment, last_slot)) michael@0: << json::close // close "input" michael@0: << "slots" << json::array; michael@0: const Position rsb_prepos = last_slot ? last_slot->origin() : fsm.slots.segment.advance(); michael@0: fsm.slots.segment.positionSlots(0); michael@0: michael@0: for(Slot * slot = output_slot(fsm.slots, 0); slot != last_slot; slot = slot->next()) michael@0: *fsm.dbgout << dslot(&fsm.slots.segment, slot); michael@0: *fsm.dbgout << json::close // close "slots" michael@0: << "postshift" << (last_slot ? last_slot->origin() : fsm.slots.segment.advance()) - rsb_prepos michael@0: << json::close; // close "output" object michael@0: michael@0: } michael@0: michael@0: #endif michael@0: michael@0: michael@0: inline michael@0: bool Pass::testPassConstraint(Machine & m) const michael@0: { michael@0: if (!m_cPConstraint) return true; michael@0: michael@0: assert(m_cPConstraint.constraint()); michael@0: michael@0: m.slotMap().reset(*m.slotMap().segment.first(), 0); michael@0: m.slotMap().pushSlot(m.slotMap().segment.first()); michael@0: vm::slotref * map = m.slotMap().begin(); michael@0: const uint32 ret = m_cPConstraint.run(m, map); michael@0: michael@0: #if !defined GRAPHITE2_NTRACING michael@0: json * const dbgout = m.slotMap().segment.getFace()->logger(); michael@0: if (dbgout) michael@0: *dbgout << "constraint" << (ret && m.status() == Machine::finished); michael@0: #endif michael@0: michael@0: return ret && m.status() == Machine::finished; michael@0: } michael@0: michael@0: michael@0: bool Pass::testConstraint(const Rule & r, Machine & m) const michael@0: { michael@0: const uint16 curr_context = m.slotMap().context(); michael@0: if (unsigned(r.sort - r.preContext) > m.slotMap().size() - curr_context michael@0: || curr_context - r.preContext < 0) return false; michael@0: if (!*r.constraint) return true; michael@0: assert(r.constraint->constraint()); michael@0: michael@0: vm::slotref * map = m.slotMap().begin() + curr_context - r.preContext; michael@0: for (int n = r.sort; n && map; --n, ++map) michael@0: { michael@0: if (!*map) continue; michael@0: const int32 ret = r.constraint->run(m, map); michael@0: if (!ret || m.status() != Machine::finished) michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: michael@0: void SlotMap::collectGarbage() michael@0: { michael@0: for(Slot **s = begin(), *const *const se = end() - 1; s != se; ++s) { michael@0: Slot *& slot = *s; michael@0: if(slot->isDeleted() || slot->isCopied()) michael@0: segment.freeSlot(slot); michael@0: } michael@0: } michael@0: michael@0: michael@0: michael@0: int Pass::doAction(const Code *codeptr, Slot * & slot_out, vm::Machine & m) const michael@0: { michael@0: assert(codeptr); michael@0: if (!*codeptr) return 0; michael@0: SlotMap & smap = m.slotMap(); michael@0: vm::slotref * map = &smap[smap.context()]; michael@0: smap.highpassed(false); michael@0: michael@0: int32 ret = codeptr->run(m, map); michael@0: michael@0: if (m.status() != Machine::finished) michael@0: { michael@0: slot_out = NULL; michael@0: smap.highwater(0); michael@0: return 0; michael@0: } michael@0: michael@0: slot_out = *map; michael@0: return ret; michael@0: } michael@0: michael@0: michael@0: void Pass::adjustSlot(int delta, Slot * & slot_out, SlotMap & smap) const michael@0: { michael@0: if (delta < 0) michael@0: { michael@0: if (!slot_out) michael@0: { michael@0: slot_out = smap.segment.last(); michael@0: ++delta; michael@0: if (smap.highpassed() && !smap.highwater()) michael@0: smap.highpassed(false); michael@0: } michael@0: while (++delta <= 0 && slot_out) michael@0: { michael@0: if (smap.highpassed() && smap.highwater() == slot_out) michael@0: smap.highpassed(false); michael@0: slot_out = slot_out->prev(); michael@0: } michael@0: } michael@0: else if (delta > 0) michael@0: { michael@0: if (!slot_out) michael@0: { michael@0: slot_out = smap.segment.first(); michael@0: --delta; michael@0: } michael@0: while (--delta >= 0 && slot_out) michael@0: { michael@0: slot_out = slot_out->next(); michael@0: if (slot_out == smap.highwater() && slot_out) michael@0: smap.highpassed(true); michael@0: } michael@0: } michael@0: } michael@0: