tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Pass.cpp (41595B)


      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 <cmath>
     35 #include "inc/Segment.h"
     36 #include "inc/Code.h"
     37 #include "inc/Rule.h"
     38 #include "inc/Error.h"
     39 #include "inc/Collider.h"
     40 
     41 using namespace graphite2;
     42 using vm::Machine;
     43 typedef Machine::Code  Code;
     44 
     45 enum KernCollison
     46 {
     47    None       = 0,
     48    CrossSpace = 1,
     49    InWord     = 2,
     50    reserved   = 3
     51 };
     52 
     53 Pass::Pass()
     54 : m_silf(0),
     55  m_cols(0),
     56  m_rules(0),
     57  m_ruleMap(0),
     58  m_startStates(0),
     59  m_transitions(0),
     60  m_states(0),
     61  m_codes(0),
     62  m_progs(0),
     63  m_numCollRuns(0),
     64  m_kernColls(0),
     65  m_iMaxLoop(0),
     66  m_numGlyphs(0),
     67  m_numRules(0),
     68  m_numStates(0),
     69  m_numTransition(0),
     70  m_numSuccess(0),
     71  m_successStart(0),
     72  m_numColumns(0),
     73  m_minPreCtxt(0),
     74  m_maxPreCtxt(0),
     75  m_colThreshold(0),
     76  m_isReverseDir(false)
     77 {
     78 }
     79 
     80 Pass::~Pass()
     81 {
     82    free(m_cols);
     83    free(m_startStates);
     84    free(m_transitions);
     85    free(m_states);
     86    free(m_ruleMap);
     87 
     88    if (m_rules) delete [] m_rules;
     89    if (m_codes) delete [] m_codes;
     90    free(m_progs);
     91 }
     92 
     93 bool Pass::readPass(const byte * const pass_start, size_t pass_length, size_t subtable_base,
     94        GR_MAYBE_UNUSED Face & face, passtype pt, GR_MAYBE_UNUSED uint32 version, Error &e)
     95 {
     96    const byte * p              = pass_start,
     97               * const pass_end = p + pass_length;
     98    size_t numRanges;
     99 
    100    if (e.test(pass_length < 40, E_BADPASSLENGTH)) return face.error(e);
    101    // Read in basic values
    102    const byte flags = be::read<byte>(p);
    103    if (e.test((flags & 0x1f) &&
    104            (pt < PASS_TYPE_POSITIONING || !m_silf->aCollision() || !face.glyphs().hasBoxes() || !(m_silf->flags() & 0x20)),
    105            E_BADCOLLISIONPASS))
    106        return face.error(e);
    107    m_numCollRuns = flags & 0x7;
    108    m_kernColls   = (flags >> 3) & 0x3;
    109    m_isReverseDir = (flags >> 5) & 0x1;
    110    m_iMaxLoop = be::read<byte>(p);
    111    if (m_iMaxLoop < 1) m_iMaxLoop = 1;
    112    be::skip<byte>(p,2); // skip maxContext & maxBackup
    113    m_numRules = be::read<uint16>(p);
    114    if (e.test(!m_numRules && m_numCollRuns == 0, E_BADEMPTYPASS)) return face.error(e);
    115    be::skip<uint16>(p);   // fsmOffset - not sure why we would want this
    116    const byte * const pcCode = pass_start + be::read<uint32>(p) - subtable_base,
    117               * const rcCode = pass_start + be::read<uint32>(p) - subtable_base,
    118               * const aCode  = pass_start + be::read<uint32>(p) - subtable_base;
    119    be::skip<uint32>(p);
    120    m_numStates = be::read<uint16>(p);
    121    m_numTransition = be::read<uint16>(p);
    122    m_numSuccess = be::read<uint16>(p);
    123    m_numColumns = be::read<uint16>(p);
    124    numRanges = be::read<uint16>(p);
    125    be::skip<uint16>(p, 3); // skip searchRange, entrySelector & rangeShift.
    126    assert(p - pass_start == 40);
    127    // Perform some sanity checks.
    128    if ( e.test(m_numTransition > m_numStates, E_BADNUMTRANS)
    129            || e.test(m_numSuccess > m_numStates, E_BADNUMSUCCESS)
    130            || e.test(m_numSuccess + m_numTransition < m_numStates, E_BADNUMSTATES)
    131            || e.test(m_numRules && numRanges == 0, E_NORANGES)
    132            || e.test(m_numColumns > 0x7FFF, E_BADNUMCOLUMNS))
    133        return face.error(e);
    134 
    135    m_successStart = m_numStates - m_numSuccess;
    136    // test for beyond end - 1 to account for reading uint16
    137    if (e.test(p + numRanges * 6 - 2 > pass_end, E_BADPASSLENGTH)) return face.error(e);
    138    m_numGlyphs = be::peek<uint16>(p + numRanges * 6 - 4) + 1;
    139    // Calculate the start of various arrays.
    140    const byte * const ranges = p;
    141    be::skip<uint16>(p, numRanges*3);
    142    const byte * const o_rule_map = p;
    143    be::skip<uint16>(p, m_numSuccess + 1);
    144 
    145    // More sanity checks
    146    if (e.test(reinterpret_cast<const byte *>(o_rule_map + m_numSuccess*sizeof(uint16)) > pass_end
    147            || p > pass_end, E_BADRULEMAPLEN))
    148        return face.error(e);
    149    const size_t numEntries = be::peek<uint16>(o_rule_map + m_numSuccess*sizeof(uint16));
    150    const byte * const   rule_map = p;
    151    be::skip<uint16>(p, numEntries);
    152 
    153    if (e.test(p + 2*sizeof(uint8) > pass_end, E_BADPASSLENGTH)) return face.error(e);
    154    m_minPreCtxt = be::read<uint8>(p);
    155    m_maxPreCtxt = be::read<uint8>(p);
    156    if (e.test(m_minPreCtxt > m_maxPreCtxt, E_BADCTXTLENBOUNDS)) return face.error(e);
    157    const byte * const start_states = p;
    158    be::skip<int16>(p, m_maxPreCtxt - m_minPreCtxt + 1);
    159    const uint16 * const sort_keys = reinterpret_cast<const uint16 *>(p);
    160    be::skip<uint16>(p, m_numRules);
    161    const byte * const precontext = p;
    162    be::skip<byte>(p, m_numRules);
    163 
    164    if (e.test(p + sizeof(uint16) + sizeof(uint8) > pass_end, E_BADCTXTLENS)) return face.error(e);
    165    m_colThreshold = be::read<uint8>(p);
    166    if (m_colThreshold == 0) m_colThreshold = 10;       // A default
    167    const size_t pass_constraint_len = be::read<uint16>(p);
    168 
    169    const uint16 * const o_constraint = reinterpret_cast<const uint16 *>(p);
    170    be::skip<uint16>(p, m_numRules + 1);
    171    const uint16 * const o_actions = reinterpret_cast<const uint16 *>(p);
    172    be::skip<uint16>(p, m_numRules + 1);
    173    const byte * const states = p;
    174    if (e.test(2u*m_numTransition*m_numColumns >= (unsigned)(pass_end - p), E_BADPASSLENGTH)
    175            || e.test(p >= pass_end, E_BADPASSLENGTH))
    176        return face.error(e);
    177    be::skip<int16>(p, m_numTransition*m_numColumns);
    178    be::skip<uint8>(p);
    179    if (e.test(p != pcCode, E_BADPASSCCODEPTR)) return face.error(e);
    180    be::skip<byte>(p, pass_constraint_len);
    181    if (e.test(p != rcCode, E_BADRULECCODEPTR)
    182        || e.test(size_t(rcCode - pcCode) != pass_constraint_len, E_BADCCODELEN)) return face.error(e);
    183    be::skip<byte>(p, be::peek<uint16>(o_constraint + m_numRules));
    184    if (e.test(p != aCode, E_BADACTIONCODEPTR)) return face.error(e);
    185    be::skip<byte>(p, be::peek<uint16>(o_actions + m_numRules));
    186 
    187    // We should be at the end or within the pass
    188    if (e.test(p > pass_end, E_BADPASSLENGTH)) return face.error(e);
    189 
    190    // Load the pass constraint if there is one.
    191    if (pass_constraint_len)
    192    {
    193        face.error_context(face.error_context() + 1);
    194        m_cPConstraint = vm::Machine::Code(true, pcCode, pcCode + pass_constraint_len,
    195                                  precontext[0], be::peek<uint16>(sort_keys), *m_silf, face, PASS_TYPE_UNKNOWN);
    196        if (e.test(!m_cPConstraint, E_OUTOFMEM)
    197                || e.test(m_cPConstraint.status() != Code::loaded, m_cPConstraint.status() + E_CODEFAILURE))
    198            return face.error(e);
    199        face.error_context(face.error_context() - 1);
    200    }
    201    if (m_numRules)
    202    {
    203        if (!readRanges(ranges, numRanges, e)) return face.error(e);
    204        if (!readRules(rule_map, numEntries,  precontext, sort_keys,
    205                   o_constraint, rcCode, o_actions, aCode, face, pt, e)) return false;
    206    }
    207 #ifdef GRAPHITE2_TELEMETRY
    208    telemetry::category _states_cat(face.tele.states);
    209 #endif
    210    return m_numRules ? readStates(start_states, states, o_rule_map, face, e) : true;
    211 }
    212 
    213 
    214 bool Pass::readRules(const byte * rule_map, const size_t num_entries,
    215                     const byte *precontext, const uint16 * sort_key,
    216                     const uint16 * o_constraint, const byte *rc_data,
    217                     const uint16 * o_action,     const byte * ac_data,
    218                     Face & face, passtype pt, Error &e)
    219 {
    220    const byte * const ac_data_end = ac_data + be::peek<uint16>(o_action + m_numRules);
    221    const byte * const rc_data_end = rc_data + be::peek<uint16>(o_constraint + m_numRules);
    222 
    223    precontext += m_numRules;
    224    sort_key   += m_numRules;
    225    o_constraint += m_numRules;
    226    o_action += m_numRules;
    227 
    228    // Load rules.
    229    const byte * ac_begin = 0, * rc_begin = 0,
    230               * ac_end = ac_data + be::peek<uint16>(o_action),
    231               * rc_end = rc_data + be::peek<uint16>(o_constraint);
    232 
    233    // Allocate pools
    234    m_rules = new Rule [m_numRules];
    235    m_codes = new Code [m_numRules*2];
    236    int totalSlots = 0;
    237    const uint16 *tsort = sort_key;
    238    for (int i = 0; i < m_numRules; ++i)
    239        totalSlots += be::peek<uint16>(--tsort);
    240    const size_t prog_pool_sz = vm::Machine::Code::estimateCodeDataOut(ac_end - ac_data + rc_end - rc_data, 2 * m_numRules, totalSlots);
    241    m_progs = gralloc<byte>(prog_pool_sz);
    242    byte * prog_pool_free = m_progs,
    243         * prog_pool_end  = m_progs + prog_pool_sz;
    244    if (e.test(!(m_rules && m_codes && m_progs), E_OUTOFMEM)) return face.error(e);
    245 
    246    Rule * r = m_rules + m_numRules - 1;
    247    for (size_t n = m_numRules; r >= m_rules; --n, --r, ac_end = ac_begin, rc_end = rc_begin)
    248    {
    249        face.error_context((face.error_context() & 0xFFFF00) + EC_ARULE + int((n - 1) << 24));
    250        r->preContext = *--precontext;
    251        r->sort       = be::peek<uint16>(--sort_key);
    252 #ifndef NDEBUG
    253        r->rule_idx   = uint16(n - 1);
    254 #endif
    255        if (r->sort > 63 || r->preContext >= r->sort || r->preContext > m_maxPreCtxt || r->preContext < m_minPreCtxt)
    256            return false;
    257        ac_begin      = ac_data + be::peek<uint16>(--o_action);
    258        --o_constraint;
    259        rc_begin      = be::peek<uint16>(o_constraint) ? rc_data + be::peek<uint16>(o_constraint) : rc_end;
    260 
    261        if (ac_begin > ac_end || ac_begin > ac_data_end || ac_end > ac_data_end
    262                || rc_begin > rc_end || rc_begin > rc_data_end || rc_end > rc_data_end
    263                || vm::Machine::Code::estimateCodeDataOut(ac_end - ac_begin + rc_end - rc_begin, 2, r->sort) > size_t(prog_pool_end - prog_pool_free))
    264            return false;
    265        r->action     = new (m_codes+n*2-2) vm::Machine::Code(false, ac_begin, ac_end, r->preContext, r->sort, *m_silf, face, pt, &prog_pool_free);
    266        r->constraint = new (m_codes+n*2-1) vm::Machine::Code(true,  rc_begin, rc_end, r->preContext, r->sort, *m_silf, face, pt, &prog_pool_free);
    267 
    268        if (e.test(!r->action || !r->constraint, E_OUTOFMEM)
    269                || e.test(r->action->status() != Code::loaded, r->action->status() + E_CODEFAILURE)
    270                || e.test(r->constraint->status() != Code::loaded, r->constraint->status() + E_CODEFAILURE)
    271                || e.test(!r->constraint->immutable(), E_MUTABLECCODE))
    272            return face.error(e);
    273    }
    274 
    275    byte * const moved_progs = prog_pool_free > m_progs ? static_cast<byte *>(realloc(m_progs, prog_pool_free - m_progs)) : 0;
    276    if (e.test(!moved_progs, E_OUTOFMEM))
    277    {
    278        free(m_progs);
    279        m_progs = 0;
    280        return face.error(e);
    281    }
    282 
    283    if (moved_progs != m_progs)
    284    {
    285        for (Code * c = m_codes, * const ce = c + m_numRules*2; c != ce; ++c)
    286        {
    287            c->externalProgramMoved(moved_progs - m_progs);
    288        }
    289        m_progs = moved_progs;
    290    }
    291 
    292    // Load the rule entries map
    293    face.error_context((face.error_context() & 0xFFFF00) + EC_APASS);
    294    //TODO: Coverity: 1315804: FORWARD_NULL
    295    RuleEntry * re = m_ruleMap = gralloc<RuleEntry>(num_entries);
    296    if (e.test(!re, E_OUTOFMEM)) return face.error(e);
    297    for (size_t n = num_entries; n; --n, ++re)
    298    {
    299        const ptrdiff_t rn = be::read<uint16>(rule_map);
    300        if (e.test(rn >= m_numRules, E_BADRULENUM))  return face.error(e);
    301        re->rule = m_rules + rn;
    302    }
    303 
    304    return true;
    305 }
    306 
    307 static int cmpRuleEntry(const void *a, const void *b) { return (*(RuleEntry *)a < *(RuleEntry *)b ? -1 :
    308                                                                (*(RuleEntry *)b < *(RuleEntry *)a ? 1 : 0)); }
    309 
    310 bool Pass::readStates(const byte * starts, const byte *states, const byte * o_rule_map, GR_MAYBE_UNUSED Face & face, Error &e)
    311 {
    312 #ifdef GRAPHITE2_TELEMETRY
    313    telemetry::category _states_cat(face.tele.starts);
    314 #endif
    315    m_startStates = gralloc<uint16>(m_maxPreCtxt - m_minPreCtxt + 1);
    316 #ifdef GRAPHITE2_TELEMETRY
    317    telemetry::set_category(face.tele.states);
    318 #endif
    319    m_states      = gralloc<State>(m_numStates);
    320 #ifdef GRAPHITE2_TELEMETRY
    321    telemetry::set_category(face.tele.transitions);
    322 #endif
    323    m_transitions      = gralloc<uint16>(m_numTransition * m_numColumns);
    324 
    325    if (e.test(!m_startStates || !m_states || !m_transitions, E_OUTOFMEM)) return face.error(e);
    326    // load start states
    327    for (uint16 * s = m_startStates,
    328                * const s_end = s + m_maxPreCtxt - m_minPreCtxt + 1; s != s_end; ++s)
    329    {
    330        *s = be::read<uint16>(starts);
    331        if (e.test(*s >= m_numStates, E_BADSTATE))
    332        {
    333            face.error_context((face.error_context() & 0xFFFF00) + EC_ASTARTS + int((s - m_startStates) << 24));
    334            return face.error(e); // true;
    335        }
    336    }
    337 
    338    // load state transition table.
    339    for (uint16 * t = m_transitions,
    340                * const t_end = t + m_numTransition*m_numColumns; t != t_end; ++t)
    341    {
    342        *t = be::read<uint16>(states);
    343        if (e.test(*t >= m_numStates, E_BADSTATE))
    344        {
    345            face.error_context((face.error_context() & 0xFFFF00) + EC_ATRANS + int(((t - m_transitions) / m_numColumns) << 8));
    346            return face.error(e);
    347        }
    348    }
    349 
    350    State * s = m_states,
    351          * const success_begin = m_states + m_numStates - m_numSuccess;
    352    const RuleEntry * rule_map_end = m_ruleMap + be::peek<uint16>(o_rule_map + m_numSuccess*sizeof(uint16));
    353    for (size_t n = m_numStates; n; --n, ++s)
    354    {
    355        RuleEntry * const begin = s < success_begin ? 0 : m_ruleMap + be::read<uint16>(o_rule_map),
    356                  * const end   = s < success_begin ? 0 : m_ruleMap + be::peek<uint16>(o_rule_map);
    357 
    358        if (e.test(begin >= rule_map_end || end > rule_map_end || begin > end, E_BADRULEMAPPING))
    359        {
    360            face.error_context((face.error_context() & 0xFFFF00) + EC_ARULEMAP + int(n << 24));
    361            return face.error(e);
    362        }
    363        s->rules = begin;
    364        s->rules_end = (end - begin <= FiniteStateMachine::MAX_RULES)? end :
    365            begin + FiniteStateMachine::MAX_RULES;
    366        if (begin)      // keep UBSan happy can't call qsort with null begin
    367            qsort(begin, end - begin, sizeof(RuleEntry), &cmpRuleEntry);
    368    }
    369 
    370    return true;
    371 }
    372 
    373 bool Pass::readRanges(const byte * ranges, size_t num_ranges, Error &e)
    374 {
    375    m_cols = gralloc<uint16>(m_numGlyphs);
    376    if (e.test(!m_cols, E_OUTOFMEM)) return false;
    377    memset(m_cols, 0xFF, m_numGlyphs * sizeof(uint16));
    378    for (size_t n = num_ranges; n; --n)
    379    {
    380        uint16     * ci     = m_cols + be::read<uint16>(ranges),
    381                   * ci_end = m_cols + be::read<uint16>(ranges) + 1,
    382                     col    = be::read<uint16>(ranges);
    383 
    384        if (e.test(ci >= ci_end || ci_end > m_cols+m_numGlyphs || col >= m_numColumns, E_BADRANGE))
    385            return false;
    386 
    387        // A glyph must only belong to one column at a time
    388        while (ci != ci_end && *ci == 0xffff)
    389            *ci++ = col;
    390 
    391        if (e.test(ci != ci_end, E_BADRANGE))
    392            return false;
    393    }
    394    return true;
    395 }
    396 
    397 
    398 bool Pass::runGraphite(vm::Machine & m, FiniteStateMachine & fsm, bool reverse) const
    399 {
    400    Slot *s = m.slotMap().segment.first();
    401    if (!s || !testPassConstraint(m)) return true;
    402    if (reverse)
    403    {
    404        m.slotMap().segment.reverseSlots();
    405        s = m.slotMap().segment.first();
    406    }
    407    if (m_numRules)
    408    {
    409        Slot *currHigh = s->next();
    410 
    411 #if !defined GRAPHITE2_NTRACING
    412        if (fsm.dbgout)  *fsm.dbgout << "rules" << json::array;
    413        json::closer rules_array_closer(fsm.dbgout);
    414 #endif
    415 
    416        m.slotMap().highwater(currHigh);
    417        int lc = m_iMaxLoop;
    418        do
    419        {
    420            findNDoRule(s, m, fsm);
    421            if (m.status() != Machine::finished) return false;
    422            if (s && (s == m.slotMap().highwater() || m.slotMap().highpassed() || --lc == 0)) {
    423                if (!lc)
    424                    s = m.slotMap().highwater();
    425                lc = m_iMaxLoop;
    426                if (s)
    427                    m.slotMap().highwater(s->next());
    428            }
    429        } while (s);
    430    }
    431    //TODO: Use enums for flags
    432    const bool collisions = m_numCollRuns || m_kernColls;
    433 
    434    if (!collisions || !m.slotMap().segment.hasCollisionInfo())
    435        return true;
    436 
    437    if (m_numCollRuns)
    438    {
    439        if (!(m.slotMap().segment.flags() & Segment::SEG_INITCOLLISIONS))
    440        {
    441            m.slotMap().segment.positionSlots(0, 0, 0, m.slotMap().dir(), true);
    442 //            m.slotMap().segment.flags(m.slotMap().segment.flags() | Segment::SEG_INITCOLLISIONS);
    443        }
    444        if (!collisionShift(&m.slotMap().segment, m.slotMap().dir(), fsm.dbgout))
    445            return false;
    446    }
    447    if ((m_kernColls) && !collisionKern(&m.slotMap().segment, m.slotMap().dir(), fsm.dbgout))
    448        return false;
    449    if (collisions && !collisionFinish(&m.slotMap().segment, fsm.dbgout))
    450        return false;
    451    return true;
    452 }
    453 
    454 bool Pass::runFSM(FiniteStateMachine& fsm, Slot * slot) const
    455 {
    456    fsm.reset(slot, m_maxPreCtxt);
    457    if (fsm.slots.context() < m_minPreCtxt)
    458        return false;
    459 
    460    uint16 state = m_startStates[m_maxPreCtxt - fsm.slots.context()];
    461    uint8  free_slots = SlotMap::MAX_SLOTS;
    462    do
    463    {
    464        fsm.slots.pushSlot(slot);
    465        if (slot->gid() >= m_numGlyphs
    466         || m_cols[slot->gid()] == 0xffffU
    467         || --free_slots == 0
    468         || state >= m_numTransition)
    469            return free_slots != 0;
    470 
    471        const uint16 * transitions = m_transitions + state*m_numColumns;
    472        state = transitions[m_cols[slot->gid()]];
    473        if (state >= m_successStart)
    474            fsm.rules.accumulate_rules(m_states[state]);
    475 
    476        slot = slot->next();
    477    } while (state != 0 && slot);
    478 
    479    fsm.slots.pushSlot(slot);
    480    return true;
    481 }
    482 
    483 #if !defined GRAPHITE2_NTRACING
    484 
    485 inline
    486 Slot * input_slot(const SlotMap &  slots, const int n)
    487 {
    488    Slot * s = slots[slots.context() + n];
    489    if (!s->isCopied())     return s;
    490 
    491    return s->prev() ? s->prev()->next() : (s->next() ? s->next()->prev() : slots.segment.last());
    492 }
    493 
    494 inline
    495 Slot * output_slot(const SlotMap &  slots, const int n)
    496 {
    497    Slot * s = slots[slots.context() + n - 1];
    498    return s ? s->next() : slots.segment.first();
    499 }
    500 
    501 #endif //!defined GRAPHITE2_NTRACING
    502 
    503 void Pass::findNDoRule(Slot * & slot, Machine &m, FiniteStateMachine & fsm) const
    504 {
    505    assert(slot);
    506 
    507    if (runFSM(fsm, slot))
    508    {
    509        // Search for the first rule which passes the constraint
    510        const RuleEntry *        r = fsm.rules.begin(),
    511                        * const re = fsm.rules.end();
    512        while (r != re && !testConstraint(*r->rule, m))
    513        {
    514            ++r;
    515            if (m.status() != Machine::finished)
    516                return;
    517        }
    518 
    519 #if !defined GRAPHITE2_NTRACING
    520        if (fsm.dbgout)
    521        {
    522            if (fsm.rules.size() != 0)
    523            {
    524                *fsm.dbgout << json::item << json::object;
    525                dumpRuleEventConsidered(fsm, *r);
    526                if (r != re)
    527                {
    528                    const int adv = doAction(r->rule->action, slot, m);
    529                    dumpRuleEventOutput(fsm, *r->rule, slot);
    530                    if (r->rule->action->deletes()) fsm.slots.collectGarbage(slot);
    531                    adjustSlot(adv, slot, fsm.slots);
    532                    *fsm.dbgout << "cursor" << objectid(dslot(&fsm.slots.segment, slot))
    533                            << json::close; // Close RuelEvent object
    534 
    535                    return;
    536                }
    537                else
    538                {
    539                    *fsm.dbgout << json::close  // close "considered" array
    540                            << "output" << json::null
    541                            << "cursor" << objectid(dslot(&fsm.slots.segment, slot->next()))
    542                            << json::close;
    543                }
    544            }
    545        }
    546        else
    547 #endif
    548        {
    549            if (r != re)
    550            {
    551                const int adv = doAction(r->rule->action, slot, m);
    552                if (m.status() != Machine::finished) return;
    553                if (r->rule->action->deletes()) fsm.slots.collectGarbage(slot);
    554                adjustSlot(adv, slot, fsm.slots);
    555                return;
    556            }
    557        }
    558    }
    559 
    560    slot = slot->next();
    561    return;
    562 }
    563 
    564 #if !defined GRAPHITE2_NTRACING
    565 
    566 void Pass::dumpRuleEventConsidered(const FiniteStateMachine & fsm, const RuleEntry & re) const
    567 {
    568    *fsm.dbgout << "considered" << json::array;
    569    for (const RuleEntry *r = fsm.rules.begin(); r != &re; ++r)
    570    {
    571        if (r->rule->preContext > fsm.slots.context())
    572            continue;
    573        *fsm.dbgout << json::flat << json::object
    574                    << "id" << r->rule - m_rules
    575                    << "failed" << true
    576                    << "input" << json::flat << json::object
    577                        << "start" << objectid(dslot(&fsm.slots.segment, input_slot(fsm.slots, -r->rule->preContext)))
    578                        << "length" << r->rule->sort
    579                        << json::close  // close "input"
    580                    << json::close; // close Rule object
    581    }
    582 }
    583 
    584 
    585 void Pass::dumpRuleEventOutput(const FiniteStateMachine & fsm, const Rule & r, Slot * const last_slot) const
    586 {
    587    *fsm.dbgout     << json::item << json::flat << json::object
    588                        << "id"     << &r - m_rules
    589                        << "failed" << false
    590                        << "input" << json::flat << json::object
    591                            << "start" << objectid(dslot(&fsm.slots.segment, input_slot(fsm.slots, 0)))
    592                            << "length" << r.sort - r.preContext
    593                            << json::close // close "input"
    594                        << json::close  // close Rule object
    595                << json::close // close considered array
    596                << "output" << json::object
    597                    << "range" << json::flat << json::object
    598                        << "start"  << objectid(dslot(&fsm.slots.segment, input_slot(fsm.slots, 0)))
    599                        << "end"    << objectid(dslot(&fsm.slots.segment, last_slot))
    600                    << json::close // close "input"
    601                    << "slots"  << json::array;
    602    const Position rsb_prepos = last_slot ? last_slot->origin() : fsm.slots.segment.advance();
    603    fsm.slots.segment.positionSlots(0, 0, 0, fsm.slots.segment.currdir());
    604 
    605    for(Slot * slot = output_slot(fsm.slots, 0); slot != last_slot; slot = slot->next())
    606        *fsm.dbgout     << dslot(&fsm.slots.segment, slot);
    607    *fsm.dbgout         << json::close  // close "slots"
    608                    << "postshift"  << (last_slot ? last_slot->origin() : fsm.slots.segment.advance()) - rsb_prepos
    609                << json::close;         // close "output" object
    610 
    611 }
    612 
    613 #endif
    614 
    615 
    616 inline
    617 bool Pass::testPassConstraint(Machine & m) const
    618 {
    619    if (!m_cPConstraint) return true;
    620 
    621    assert(m_cPConstraint.constraint());
    622 
    623    m.slotMap().reset(*m.slotMap().segment.first(), 0);
    624    m.slotMap().pushSlot(m.slotMap().segment.first());
    625    vm::slotref * map = m.slotMap().begin();
    626    const uint32 ret = m_cPConstraint.run(m, map);
    627 
    628 #if !defined GRAPHITE2_NTRACING
    629    json * const dbgout = m.slotMap().segment.getFace()->logger();
    630    if (dbgout)
    631        *dbgout << "constraint" << (ret && m.status() == Machine::finished);
    632 #endif
    633 
    634    return ret && m.status() == Machine::finished;
    635 }
    636 
    637 
    638 bool Pass::testConstraint(const Rule & r, Machine & m) const
    639 {
    640    const uint16 curr_context = m.slotMap().context();
    641    if (unsigned(r.sort + curr_context - r.preContext) > m.slotMap().size()
    642        || curr_context - r.preContext < 0) return false;
    643 
    644    vm::slotref * map = m.slotMap().begin() + curr_context - r.preContext;
    645    if (map[r.sort - 1] == 0)
    646        return false;
    647 
    648    if (!*r.constraint) return true;
    649    assert(r.constraint->constraint());
    650    for (int n = r.sort; n && map; --n, ++map)
    651    {
    652        if (!*map) continue;
    653        const int32 ret = r.constraint->run(m, map);
    654        if (!ret || m.status() != Machine::finished)
    655            return false;
    656    }
    657 
    658    return true;
    659 }
    660 
    661 
    662 void SlotMap::collectGarbage(Slot * &aSlot)
    663 {
    664    for(Slot **s = begin(), *const *const se = end() - 1; s != se; ++s) {
    665        Slot *& slot = *s;
    666        if(slot && (slot->isDeleted() || slot->isCopied()))
    667        {
    668            if (slot == aSlot)
    669                aSlot = slot->prev() ? slot->prev() : slot->next();
    670            segment.freeSlot(slot);
    671        }
    672    }
    673 }
    674 
    675 
    676 
    677 int Pass::doAction(const Code *codeptr, Slot * & slot_out, vm::Machine & m) const
    678 {
    679    assert(codeptr);
    680    if (!*codeptr) return 0;
    681    SlotMap   & smap = m.slotMap();
    682    vm::slotref * map = &smap[smap.context()];
    683    smap.highpassed(false);
    684 
    685    int32 ret = codeptr->run(m, map);
    686 
    687    if (m.status() != Machine::finished)
    688    {
    689        slot_out = NULL;
    690        smap.highwater(0);
    691        return 0;
    692    }
    693 
    694    slot_out = *map;
    695    return ret;
    696 }
    697 
    698 
    699 void Pass::adjustSlot(int delta, Slot * & slot_out, SlotMap & smap) const
    700 {
    701    if (!slot_out)
    702    {
    703        if (smap.highpassed() || slot_out == smap.highwater())
    704        {
    705            slot_out = smap.segment.last();
    706            ++delta;
    707            if (!smap.highwater() || smap.highwater() == slot_out)
    708                smap.highpassed(false);
    709        }
    710        else
    711        {
    712            slot_out = smap.segment.first();
    713            --delta;
    714        }
    715    }
    716    if (delta < 0)
    717    {
    718        while (++delta <= 0 && slot_out)
    719        {
    720            slot_out = slot_out->prev();
    721            if (smap.highpassed() && smap.highwater() == slot_out)
    722                smap.highpassed(false);
    723        }
    724    }
    725    else if (delta > 0)
    726    {
    727        while (--delta >= 0 && slot_out)
    728        {
    729            if (slot_out == smap.highwater() && slot_out)
    730                smap.highpassed(true);
    731            slot_out = slot_out->next();
    732        }
    733    }
    734 }
    735 
    736 bool Pass::collisionShift(Segment *seg, int dir, json * const dbgout) const
    737 {
    738    ShiftCollider shiftcoll(dbgout);
    739    // bool isfirst = true;
    740    bool hasCollisions = false;
    741    Slot *start = seg->first();      // turn on collision fixing for the first slot
    742    Slot *end = NULL;
    743    bool moved = false;
    744 
    745 #if !defined GRAPHITE2_NTRACING
    746    if (dbgout)
    747        *dbgout << "collisions" << json::array
    748            << json::flat << json::object << "num-loops" << m_numCollRuns << json::close;
    749 #endif
    750 
    751    while (start)
    752    {
    753 #if !defined GRAPHITE2_NTRACING
    754        if (dbgout)  *dbgout << json::object << "phase" << "1" << "moves" << json::array;
    755 #endif
    756        hasCollisions = false;
    757        end = NULL;
    758        // phase 1 : position shiftable glyphs, ignoring kernable glyphs
    759        for (Slot *s = start; s; s = s->next())
    760        {
    761            const SlotCollision * c = seg->collisionInfo(s);
    762            if (start && (c->flags() & (SlotCollision::COLL_FIX | SlotCollision::COLL_KERN)) == SlotCollision::COLL_FIX
    763                      && !resolveCollisions(seg, s, start, shiftcoll, false, dir, moved, hasCollisions, dbgout))
    764                return false;
    765            if (s != start && (c->flags() & SlotCollision::COLL_END))
    766            {
    767                end = s->next();
    768                break;
    769            }
    770        }
    771 
    772 #if !defined GRAPHITE2_NTRACING
    773        if (dbgout)
    774            *dbgout << json::close << json::close; // phase-1
    775 #endif
    776 
    777        // phase 2 : loop until happy.
    778        for (int i = 0; i < m_numCollRuns - 1; ++i)
    779        {
    780            if (hasCollisions || moved)
    781            {
    782 
    783 #if !defined GRAPHITE2_NTRACING
    784                if (dbgout)
    785                    *dbgout << json::object << "phase" << "2a" << "loop" << i << "moves" << json::array;
    786 #endif
    787                // phase 2a : if any shiftable glyphs are in collision, iterate backwards,
    788                // fixing them and ignoring other non-collided glyphs. Note that this handles ONLY
    789                // glyphs that are actually in collision from phases 1 or 2b, and working backwards
    790                // has the intended effect of breaking logjams.
    791                if (hasCollisions)
    792                {
    793                    hasCollisions = false;
    794                    #if 0
    795                    moved = true;
    796                    for (Slot *s = start; s != end; s = s->next())
    797                    {
    798                        SlotCollision * c = seg->collisionInfo(s);
    799                        c->setShift(Position(0, 0));
    800                    }
    801                    #endif
    802                    Slot *lend = end ? end->prev() : seg->last();
    803                    Slot *lstart = start->prev();
    804                    for (Slot *s = lend; s != lstart; s = s->prev())
    805                    {
    806                        SlotCollision * c = seg->collisionInfo(s);
    807                        if (start && (c->flags() & (SlotCollision::COLL_FIX | SlotCollision::COLL_KERN | SlotCollision::COLL_ISCOL))
    808                                        == (SlotCollision::COLL_FIX | SlotCollision::COLL_ISCOL)) // ONLY if this glyph is still colliding
    809                        {
    810                            if (!resolveCollisions(seg, s, lend, shiftcoll, true, dir, moved, hasCollisions, dbgout))
    811                                return false;
    812                            c->setFlags(c->flags() | SlotCollision::COLL_TEMPLOCK);
    813                        }
    814                    }
    815                }
    816 
    817 #if !defined GRAPHITE2_NTRACING
    818                if (dbgout)
    819                    *dbgout << json::close << json::close // phase 2a
    820                        << json::object << "phase" << "2b" << "loop" << i << "moves" << json::array;
    821 #endif
    822 
    823                // phase 2b : redo basic diacritic positioning pass for ALL glyphs. Each successive loop adjusts
    824                // glyphs from their current adjusted position, which has the effect of gradually minimizing the
    825                // resulting adjustment; ie, the final result will be gradually closer to the original location.
    826                // Also it allows more flexibility in the final adjustment, since it is moving along the
    827                // possible 8 vectors from successively different starting locations.
    828                if (moved)
    829                {
    830                    moved = false;
    831                    for (Slot *s = start; s != end; s = s->next())
    832                    {
    833                        SlotCollision * c = seg->collisionInfo(s);
    834                        if (start && (c->flags() & (SlotCollision::COLL_FIX | SlotCollision::COLL_TEMPLOCK
    835                                                        | SlotCollision::COLL_KERN)) == SlotCollision::COLL_FIX
    836                                  && !resolveCollisions(seg, s, start, shiftcoll, false, dir, moved, hasCollisions, dbgout))
    837                            return false;
    838                        else if (c->flags() & SlotCollision::COLL_TEMPLOCK)
    839                            c->setFlags(c->flags() & ~SlotCollision::COLL_TEMPLOCK);
    840                    }
    841                }
    842        //      if (!hasCollisions) // no, don't leave yet because phase 2b will continue to improve things
    843        //          break;
    844 #if !defined GRAPHITE2_NTRACING
    845                if (dbgout)
    846                    *dbgout << json::close << json::close; // phase 2
    847 #endif
    848            }
    849        }
    850        if (!end)
    851            break;
    852        start = NULL;
    853        for (Slot *s = end->prev(); s; s = s->next())
    854        {
    855            if (seg->collisionInfo(s)->flags() & SlotCollision::COLL_START)
    856            {
    857                start = s;
    858                break;
    859            }
    860        }
    861    }
    862    return true;
    863 }
    864 
    865 bool Pass::collisionKern(Segment *seg, int dir, json * const dbgout) const
    866 {
    867    Slot *start = seg->first();
    868    float ymin = 1e38f;
    869    float ymax = -1e38f;
    870    const GlyphCache &gc = seg->getFace()->glyphs();
    871 
    872    // phase 3 : handle kerning of clusters
    873 #if !defined GRAPHITE2_NTRACING
    874    if (dbgout)
    875        *dbgout << json::object << "phase" << "3" << "moves" << json::array;
    876 #endif
    877 
    878    for (Slot *s = seg->first(); s; s = s->next())
    879    {
    880        if (!gc.check(s->gid()))
    881            return false;
    882        const SlotCollision * c = seg->collisionInfo(s);
    883        const Rect &bbox = seg->theGlyphBBoxTemporary(s->gid());
    884        float y = s->origin().y + c->shift().y;
    885        if (!(c->flags() & SlotCollision::COLL_ISSPACE))
    886        {
    887            ymax = max(y + bbox.tr.y, ymax);
    888            ymin = min(y + bbox.bl.y, ymin);
    889        }
    890        if (start && (c->flags() & (SlotCollision::COLL_KERN | SlotCollision::COLL_FIX))
    891                        == (SlotCollision::COLL_KERN | SlotCollision::COLL_FIX))
    892            resolveKern(seg, s, start, dir, ymin, ymax, dbgout);
    893        if (c->flags() & SlotCollision::COLL_END)
    894            start = NULL;
    895        if (c->flags() & SlotCollision::COLL_START)
    896            start = s;
    897    }
    898 
    899 #if !defined GRAPHITE2_NTRACING
    900    if (dbgout)
    901        *dbgout << json::close << json::close; // phase 3
    902 #endif
    903    return true;
    904 }
    905 
    906 bool Pass::collisionFinish(Segment *seg, GR_MAYBE_UNUSED json * const dbgout) const
    907 {
    908    for (Slot *s = seg->first(); s; s = s->next())
    909    {
    910        SlotCollision *c = seg->collisionInfo(s);
    911        if (c->shift().x != 0 || c->shift().y != 0)
    912        {
    913            const Position newOffset = c->shift();
    914            const Position nullPosition(0, 0);
    915            c->setOffset(newOffset + c->offset());
    916            c->setShift(nullPosition);
    917        }
    918    }
    919 //    seg->positionSlots();
    920 
    921 #if !defined GRAPHITE2_NTRACING
    922        if (dbgout)
    923            *dbgout << json::close;
    924 #endif
    925    return true;
    926 }
    927 
    928 // Can slot s be kerned, or is it attached to something that can be kerned?
    929 static bool inKernCluster(Segment *seg, Slot *s)
    930 {
    931    SlotCollision *c = seg->collisionInfo(s);
    932    if (c->flags() & SlotCollision::COLL_KERN /** && c->flags() & SlotCollision::COLL_FIX **/ )
    933        return true;
    934    while (s->attachedTo())
    935    {
    936        s = s->attachedTo();
    937        c = seg->collisionInfo(s);
    938        if (c->flags() & SlotCollision::COLL_KERN /** && c->flags() & SlotCollision::COLL_FIX **/ )
    939            return true;
    940    }
    941    return false;
    942 }
    943 
    944 // Fix collisions for the given slot.
    945 // Return true if everything was fixed, false if there are still collisions remaining.
    946 // isRev means be we are processing backwards.
    947 bool Pass::resolveCollisions(Segment *seg, Slot *slotFix, Slot *start,
    948        ShiftCollider &coll, GR_MAYBE_UNUSED bool isRev, int dir, bool &moved, bool &hasCol,
    949        json * const dbgout) const
    950 {
    951    Slot * nbor;  // neighboring slot
    952    SlotCollision *cFix = seg->collisionInfo(slotFix);
    953    if (!coll.initSlot(seg, slotFix, cFix->limit(), cFix->margin(), cFix->marginWt(),
    954            cFix->shift(), cFix->offset(), dir, dbgout))
    955        return false;
    956    bool collides = false;
    957    // When we're processing forward, ignore kernable glyphs that preceed the target glyph.
    958    // When processing backward, don't ignore these until we pass slotFix.
    959    bool ignoreForKern = !isRev;
    960    bool rtl = dir & 1;
    961    Slot *base = slotFix;
    962    while (base->attachedTo())
    963        base = base->attachedTo();
    964    Position zero(0., 0.);
    965 
    966    // Look for collisions with the neighboring glyphs.
    967    for (nbor = start; nbor; nbor = isRev ? nbor->prev() : nbor->next())
    968    {
    969        SlotCollision *cNbor = seg->collisionInfo(nbor);
    970        bool sameCluster = nbor->isChildOf(base);
    971        if (nbor != slotFix         						// don't process if this is the slot of interest
    972                      && !(cNbor->ignore())    				// don't process if ignoring
    973                      && (nbor == base || sameCluster       // process if in the same cluster as slotFix
    974                            || !inKernCluster(seg, nbor))   // or this cluster is not to be kerned
    975 //                            || (rtl ^ ignoreForKern))       // or it comes before(ltr) or after(rtl)
    976                      && (!isRev    // if processing forwards then good to merge otherwise only:
    977                            || !(cNbor->flags() & SlotCollision::COLL_FIX)     // merge in immovable stuff
    978                            || ((cNbor->flags() & SlotCollision::COLL_KERN) && !sameCluster)     // ignore other kernable clusters
    979                            || (cNbor->flags() & SlotCollision::COLL_ISCOL))   // test against other collided glyphs
    980                      && !coll.mergeSlot(seg, nbor, cNbor, cNbor->shift(), !ignoreForKern, sameCluster, collides, false, dbgout))
    981            return false;
    982        else if (nbor == slotFix)
    983            // Switching sides of this glyph - if we were ignoring kernable stuff before, don't anymore.
    984            ignoreForKern = !ignoreForKern;
    985 
    986        if (nbor != start && (cNbor->flags() & (isRev ? SlotCollision::COLL_START : SlotCollision::COLL_END)))
    987            break;
    988    }
    989    bool isCol = false;
    990    if (collides || cFix->shift().x != 0.f || cFix->shift().y != 0.f)
    991    {
    992        Position shift = coll.resolve(seg, isCol, dbgout);
    993        // isCol has been set to true if a collision remains.
    994        if (std::fabs(shift.x) < 1e38f && std::fabs(shift.y) < 1e38f)
    995        {
    996            if (sqr(shift.x-cFix->shift().x) + sqr(shift.y-cFix->shift().y) >= m_colThreshold * m_colThreshold)
    997                moved = true;
    998            cFix->setShift(shift);
    999            if (slotFix->firstChild())
   1000            {
   1001                Rect bbox;
   1002                Position here = slotFix->origin() + shift;
   1003                float clusterMin = here.x;
   1004                slotFix->firstChild()->finalise(seg, NULL, here, bbox, 0, clusterMin, rtl, false);
   1005            }
   1006        }
   1007    }
   1008    else
   1009    {
   1010        // This glyph is not colliding with anything.
   1011 #if !defined GRAPHITE2_NTRACING
   1012        if (dbgout)
   1013        {
   1014            *dbgout << json::object
   1015                            << "missed" << objectid(dslot(seg, slotFix));
   1016            coll.outputJsonDbg(dbgout, seg, -1);
   1017            *dbgout << json::close;
   1018        }
   1019 #endif
   1020    }
   1021 
   1022    // Set the is-collision flag bit.
   1023    if (isCol)
   1024    { cFix->setFlags(cFix->flags() | SlotCollision::COLL_ISCOL | SlotCollision::COLL_KNOWN); }
   1025    else
   1026    { cFix->setFlags((cFix->flags() & ~SlotCollision::COLL_ISCOL) | SlotCollision::COLL_KNOWN); }
   1027    hasCol |= isCol;
   1028    return true;
   1029 }
   1030 
   1031 float Pass::resolveKern(Segment *seg, Slot *slotFix, GR_MAYBE_UNUSED Slot *start, int dir,
   1032    float &ymin, float &ymax, json *const dbgout) const
   1033 {
   1034    Slot *nbor; // neighboring slot
   1035    float currSpace = 0.;
   1036    bool collides = false;
   1037    unsigned int space_count = 0;
   1038    Slot *base = slotFix;
   1039    while (base->attachedTo())
   1040        base = base->attachedTo();
   1041    SlotCollision *cFix = seg->collisionInfo(base);
   1042    const GlyphCache &gc = seg->getFace()->glyphs();
   1043    const Rect &bbb = seg->theGlyphBBoxTemporary(slotFix->gid());
   1044    const float by = slotFix->origin().y + cFix->shift().y;
   1045 
   1046    if (base != slotFix)
   1047    {
   1048        cFix->setFlags(cFix->flags() | SlotCollision::COLL_KERN | SlotCollision::COLL_FIX);
   1049        return 0;
   1050    }
   1051    bool seenEnd = (cFix->flags() & SlotCollision::COLL_END) != 0;
   1052    bool isInit = false;
   1053    KernCollider coll(dbgout);
   1054 
   1055    ymax = max(by + bbb.tr.y, ymax);
   1056    ymin = min(by + bbb.bl.y, ymin);
   1057    for (nbor = slotFix->next(); nbor; nbor = nbor->next())
   1058    {
   1059        if (nbor->isChildOf(base))
   1060            continue;
   1061        if (!gc.check(nbor->gid()))
   1062            return 0.;
   1063        const Rect &bb = seg->theGlyphBBoxTemporary(nbor->gid());
   1064        SlotCollision *cNbor = seg->collisionInfo(nbor);
   1065        if ((bb.bl.y == 0.f && bb.tr.y == 0.f) || (cNbor->flags() & SlotCollision::COLL_ISSPACE))
   1066        {
   1067            if (m_kernColls == InWord)
   1068                break;
   1069            // Add space for a space glyph.
   1070            currSpace += nbor->advance();
   1071            ++space_count;
   1072        }
   1073        else
   1074        {
   1075            space_count = 0;
   1076            if (nbor != slotFix && !cNbor->ignore())
   1077            {
   1078                seenEnd = true;
   1079                if (!isInit)
   1080                {
   1081                    if (!coll.initSlot(seg, slotFix, cFix->limit(), cFix->margin(),
   1082                                    cFix->shift(), cFix->offset(), dir, ymin, ymax, dbgout))
   1083                        return 0.;
   1084                    isInit = true;
   1085                }
   1086                collides |= coll.mergeSlot(seg, nbor, cNbor->shift(), currSpace, dir, dbgout);
   1087            }
   1088        }
   1089        if (cNbor->flags() & SlotCollision::COLL_END)
   1090        {
   1091            if (seenEnd && space_count < 2)
   1092                break;
   1093            else
   1094                seenEnd = true;
   1095        }
   1096    }
   1097    if (collides)
   1098    {
   1099        Position mv = coll.resolve(seg, slotFix, dir, dbgout);
   1100        coll.shift(mv, dir);
   1101        Position delta = slotFix->advancePos() + mv - cFix->shift();
   1102        slotFix->advance(delta);
   1103        cFix->setShift(mv);
   1104        return mv.x;
   1105    }
   1106    return 0.;
   1107 }