text:
/*  GRAPHITE2 LICENSING

    Copyright 2010, SIL International
    All rights reserved.

    This library is free software; you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published
    by the Free Software Foundation; either version 2.1 of License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should also have received a copy of the GNU Lesser General Public
    License along with this library in the file named "LICENSE".
    If not, write to the Free Software Foundation, 51 Franklin Street,
    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
    internet at http://www.fsf.org/licenses/lgpl.html.

Alternatively, the contents of this file may be used under the terms of the
Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
License, as published by the Free Software Foundation, either version 2
of the License or (at your option) any later version.
*/
// This class represents loaded graphite stack machine code.  It performs
// basic sanity checks, on the incoming code to prevent more obvious problems
// from crashing graphite.
// Author: Tim Eves

#include <cassert>
#include <cstddef>
#include <cstdlib>
#include <cstring>
#include "graphite2/Segment.h"
#include "inc/Code.h"
#include "inc/Face.h"
#include "inc/GlyphFace.h"
#include "inc/GlyphCache.h"
#include "inc/Machine.h"
#include "inc/Rule.h"
#include "inc/Silf.h"

#include <cstdio>

#ifdef NDEBUG
#ifdef __GNUC__
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif
#endif

using namespace graphite2;
using namespace vm;

namespace {

inline bool is_return(const instr i) {
    const opcode_t * opmap = Machine::getOpcodeTable();
    const instr pop_ret  = *opmap[POP_RET].impl,
                ret_zero = *opmap[RET_ZERO].impl,
                ret_true = *opmap[RET_TRUE].impl;
    return i == pop_ret || i == ret_zero || i == ret_true;
}

struct context
{
    context(uint8 ref=0) : codeRef(ref) {flags.changed=false; flags.referenced=false;}
    struct {
        uint8   changed:1,
                referenced:1;
    } flags;
    uint8       codeRef;
};

} // end namespace

class Machine::Code::decoder
{
public:
    struct limits;
    static const int NUMCONTEXTS = 256;

    decoder(limits & lims, Code &code, enum passtype pt) throw();

    bool        load(const byte * bc_begin, const byte * bc_end);
    void        apply_analysis(instr * const code, instr * code_end);
    byte        max_ref() { return _max_ref; }
    int         out_index() const { return _out_index; }

private:
    void        set_ref(int index) throw();
    void        set_noref(int index) throw();
    void        set_changed(int index) throw();
    opcode      fetch_opcode(const byte * bc);
    void        analyse_opcode(const opcode, const int8 * const dp) throw();
    bool        emit_opcode(opcode opc, const byte * & bc);
    bool        validate_opcode(const byte opc, const byte * const bc);
    bool        valid_upto(const uint16 limit, const uint16 x) const throw();
    bool        test_context() const throw();
    bool        test_ref(int8 index) const throw();
    bool        test_attr(attrCode attr) const throw();
    void        failure(const status_t s) const throw() { _code.failure(s); }

    Code              & _code;
    int                 _out_index;
    uint16              _out_length;
    instr             * _instr;
    byte              * _data;
    limits            & _max;
    enum passtype       _passtype;
    int                 _stack_depth;
    bool                _in_ctxt_item;
    int16               _slotref;
    context             _contexts[NUMCONTEXTS];
    byte                _max_ref;
};

struct Machine::Code::decoder::limits
{
  const byte       * bytecode;
  const uint8        pre_context;
  const uint16       rule_length,
                     classes,
                     glyf_attrs,
                     features;
  const byte         attrid[gr_slatMax];
};

inline Machine::Code::decoder::decoder(limits & lims, Code &code, enum passtype pt) throw()
: _code(code),
  _out_index(code._constraint ? 0 : lims.pre_context),
  _out_length(code._constraint ? 1 : lims.rule_length),
  _instr(code._code), _data(code._data), _max(lims), _passtype(pt),
  _stack_depth(0),
  _in_ctxt_item(false),
  _slotref(0),
  _max_ref(0)
{ }

Machine::Code::Code(bool is_constraint, const byte * bytecode_begin, const byte * const bytecode_end,
           uint8 pre_context, uint16 rule_length, const Silf & silf, const Face & face,
           enum passtype pt, byte * * const _out)
 :  _code(0), _data(0), _data_size(0), _instr_count(0), _max_ref(0), _status(loaded),
    _constraint(is_constraint), _modify(false), _delete(false), _own(_out==0)
{
#ifdef GRAPHITE2_TELEMETRY
    telemetry::category _code_cat(face.tele.code);
#endif
    assert(bytecode_begin != 0);
    if (bytecode_begin == bytecode_end)
    {
      // ::new (this) Code();
      return;
    }
    assert(bytecode_end > bytecode_begin);
    const opcode_t *    op_to_fn = Machine::getOpcodeTable();

    // Allocate code and data target buffers, these sizes are a worst case
    // estimate.  Once we know their real sizes the we'll shrink them.
    if (_out)   _code = reinterpret_cast<instr *>(*_out);
    else        _code = static_cast<instr *>(malloc(estimateCodeDataOut(bytecode_end-bytecode_begin, 1, is_constraint ? 0 : rule_length)));
    _data = reinterpret_cast<byte *>(_code + (bytecode_end - bytecode_begin));

    if (!_code || !_data) {
        failure(alloc_failed);
        return;
    }

    decoder::limits lims = {
        bytecode_end,
        pre_context,
        rule_length,
        silf.numClasses(),
        face.glyphs().numAttrs(),
        face.numFeatures(),
        {1,1,1,1,1,1,1,1,
         1,1,1,1,1,1,1,255,
         1,1,1,1,1,1,1,1,
         1,1,1,1,1,1,0,0,
         0,0,0,0,0,0,0,0,
         0,0,0,0,0,0,0,0,
         0,0,0,0,0,0,0, silf.numUser()}
    };

    decoder dec(lims, *this, pt);
    if(!dec.load(bytecode_begin, bytecode_end))
       return;

    // Is this an empty program?
    if (_instr_count == 0)
    {
      release_buffers();
      ::new (this) Code();
      return;
    }

    // When we reach the end check we've terminated it correctly
    if (!is_return(_code[_instr_count-1])) {
        failure(missing_return);
        return;
    }

    assert((_constraint && immutable()) || !_constraint);
    dec.apply_analysis(_code, _code + _instr_count);
    _max_ref = dec.max_ref();

    // Now we know exactly how much code and data the program really needs
    // realloc the buffers to exactly the right size so we don't waste any
    // memory.
    assert((bytecode_end - bytecode_begin) >= ptrdiff_t(_instr_count));
    assert((bytecode_end - bytecode_begin) >= ptrdiff_t(_data_size));
    memmove(_code + (_instr_count+1), _data, _data_size*sizeof(byte));
    size_t const total_sz = ((_instr_count+1) + (_data_size + sizeof(instr)-1)/sizeof(instr))*sizeof(instr);
    if (_out)
        *_out += total_sz;
    else
    {
      instr * const old_code = _code;
      _code = static_cast<instr *>(realloc(_code, total_sz));
      if (!_code) free(old_code);
    }
   _data = reinterpret_cast<byte *>(_code + (_instr_count+1));

    if (!_code)
    {
        failure(alloc_failed);
        return;
    }

    // Make this RET_ZERO, we should never reach this but just in case ...
    _code[_instr_count] = op_to_fn[RET_ZERO].impl[_constraint];

#ifdef GRAPHITE2_TELEMETRY
    telemetry::count_bytes(_data_size + (_instr_count+1)*sizeof(instr));
#endif
}

Machine::Code::~Code() throw ()
{
    if (_own)
        release_buffers();
}

bool Machine::Code::decoder::load(const byte * bc, const byte * bc_end)
{
    _max.bytecode = bc_end;
    while (bc < bc_end)
    {
        const opcode opc = fetch_opcode(bc++);
        if (opc == vm::MAX_OPCODE)
            return false;

        analyse_opcode(opc, reinterpret_cast<const int8 *>(bc));

        if (!emit_opcode(opc, bc))
            return false;
    }

    return bool(_code);
}

// Validation check and fixups.
//

opcode Machine::Code::decoder::fetch_opcode(const byte * bc)
{
    const byte opc = *bc++;

    // Do some basic sanity checks based on what we know about the opcode
    if (!validate_opcode(opc, bc))  return MAX_OPCODE;

    // And check its arguments as far as possible
    switch (opcode(opc))
    {
        case NOP :
            break;
        case PUSH_BYTE :
        case PUSH_BYTEU :
        case PUSH_SHORT :
        case PUSH_SHORTU :
        case PUSH_LONG :
            ++_stack_depth;
            break;
        case ADD :
        case SUB :
        case MUL :
        case DIV :
        case MIN_ :
        case MAX_ :
        case AND :
        case OR :
        case EQUAL :
        case NOT_EQ :
        case LESS :
        case GTR :
        case LESS_EQ :
        case GTR_EQ :
        case BITOR :
        case BITAND :
            if (--_stack_depth <= 0)
                failure(underfull_stack);
            break;
        case NEG :
        case TRUNC8 :
        case TRUNC16 :
        case NOT :
        case BITNOT :
        case BITSET :
            if (_stack_depth <= 0)
                failure(underfull_stack);
            break;
        case COND :
            _stack_depth -= 2;
            if (_stack_depth <= 0)
                failure(underfull_stack);
            break;
        case NEXT_N :           // runtime checked
            break;
        case NEXT :
        case COPY_NEXT :
            ++_out_index;
            if (_out_index < -1 || _out_index > _out_length || _slotref > _max.rule_length)
                failure(out_of_range_data);
            break;
        case PUT_GLYPH_8BIT_OBS :
            valid_upto(_max.classes, bc[0]);
            test_context();
            break;
        case PUT_SUBS_8BIT_OBS :
            test_ref(int8(bc[0]));
            valid_upto(_max.classes, bc[1]);
            valid_upto(_max.classes, bc[2]);
            test_context();
            break;
        case PUT_COPY :
            test_ref(int8(bc[0]));
            test_context();
            break;
        case INSERT :
            if (_passtype >= PASS_TYPE_POSITIONING)
                failure(invalid_opcode);
            ++_out_length;
            if (_out_index < 0) ++_out_index;
            if (_out_index < -1 || _out_index >= _out_length)
                failure(out_of_range_data);
            break;
        case DELETE :
            if (_passtype >= PASS_TYPE_POSITIONING)
                failure(invalid_opcode);
            if (_out_index < _max.pre_context)
                failure(out_of_range_data);
            --_out_index;
            --_out_length;
            if (_out_index < -1 || _out_index > _out_length)
                failure(out_of_range_data);
            break;
        case ASSOC :
            if (bc[0] == 0)
                failure(out_of_range_data);
            for (uint8 num = bc[0]; num; --num)
                test_ref(int8(bc[num]));
            test_context();
            break;
        case CNTXT_ITEM :
            valid_upto(_max.rule_length, _max.pre_context + int8(bc[0]));
            if (bc + 2 + bc[1] >= _max.bytecode)    failure(jump_past_end);
            if (_in_ctxt_item)                      failure(nested_context_item);
            break;
        case ATTR_SET :
        case ATTR_ADD :
        case ATTR_SUB :
        case ATTR_SET_SLOT :
            if (--_stack_depth < 0)
                failure(underfull_stack);
            valid_upto(gr_slatMax, bc[0]);
            if (attrCode(bc[0]) == gr_slatUserDefn)     // use IATTR for user attributes
                failure(out_of_range_data);
            test_attr(attrCode(bc[0]));
            test_context();
            break;
        case IATTR_SET_SLOT :
            if (--_stack_depth < 0)
                failure(underfull_stack);
            if (valid_upto(gr_slatMax, bc[0]))
                valid_upto(_max.attrid[bc[0]], bc[1]);
            test_attr(attrCode(bc[0]));
            test_context();
            break;
        case PUSH_SLOT_ATTR :
            ++_stack_depth;
            valid_upto(gr_slatMax, bc[0]);
            test_ref(int8(bc[1]));
            if (attrCode(bc[0]) == gr_slatUserDefn)     // use IATTR for user attributes
                failure(out_of_range_data);
            test_attr(attrCode(bc[0]));
            break;
        case PUSH_GLYPH_ATTR_OBS :
        case PUSH_ATT_TO_GATTR_OBS :
            ++_stack_depth;
            valid_upto(_max.glyf_attrs, bc[0]);
            test_ref(int8(bc[1]));
            break;
        case PUSH_ATT_TO_GLYPH_METRIC :
        case PUSH_GLYPH_METRIC :
            ++_stack_depth;
            valid_upto(kgmetDescent, bc[0]);
            test_ref(int8(bc[1]));
            // level: dp[2] no check necessary
            break;
        case PUSH_FEAT :
            ++_stack_depth;
            valid_upto(_max.features, bc[0]);
            test_ref(int8(bc[1]));
            break;
        case PUSH_ISLOT_ATTR :
            ++_stack_depth;
            if (valid_upto(gr_slatMax, bc[0]))
            {
                test_ref(int8(bc[1]));
                valid_upto(_max.attrid[bc[0]], bc[2]);
            }
            test_attr(attrCode(bc[0]));
            break;
        case PUSH_IGLYPH_ATTR :// not implemented
            ++_stack_depth;
            break;
        case POP_RET :
            if (--_stack_depth < 0)
                failure(underfull_stack);
            GR_FALLTHROUGH;
            // no break
        case RET_ZERO :
        case RET_TRUE :
            break;
        case IATTR_SET :
        case IATTR_ADD :
        case IATTR_SUB :
            if (--_stack_depth < 0)
                failure(underfull_stack);
            if (valid_upto(gr_slatMax, bc[0]))
                valid_upto(_max.attrid[bc[0]], bc[1]);
            test_attr(attrCode(bc[0]));
            test_context();
            break;
        case PUSH_PROC_STATE :  // dummy: dp[0] no check necessary
        case PUSH_VERSION :
            ++_stack_depth;
            break;
        case PUT_SUBS :
            test_ref(int8(bc[0]));
            valid_upto(_max.classes, uint16(bc[1]<< 8) | bc[2]);
            valid_upto(_max.classes, uint16(bc[3]<< 8) | bc[4]);
            test_context();
            break;
        case PUT_SUBS2 :        // not implemented
        case PUT_SUBS3 :        // not implemented
            break;
        case PUT_GLYPH :
            valid_upto(_max.classes, uint16(bc[0]<< 8) | bc[1]);
            test_context();
            break;
        case PUSH_GLYPH_ATTR :
        case PUSH_ATT_TO_GLYPH_ATTR :
            ++_stack_depth;
            valid_upto(_max.glyf_attrs, uint16(bc[0]<< 8) | bc[1]);
            test_ref(int8(bc[2]));
            break;
        case SET_FEAT :
            valid_upto(_max.features, bc[0]);
            test_ref(int8(bc[1]));
            break;
        default:
            failure(invalid_opcode);
            break;
    }

    return bool(_code) ? opcode(opc) : MAX_OPCODE;
}

void Machine::Code::decoder::analyse_opcode(const opcode opc, const int8  * arg) throw()
{
  switch (opc)
  {
    case DELETE :
      _code._delete = true;
      break;
    case ASSOC :
      set_changed(0);
//      for (uint8 num = arg[0]; num; --num)
//        _analysis.set_noref(num);
      break;
    case PUT_GLYPH_8BIT_OBS :
    case PUT_GLYPH :
      _code._modify = true;
      set_changed(0);
      break;
    case ATTR_SET :
    case ATTR_ADD :
    case ATTR_SUB :
    case ATTR_SET_SLOT :
    case IATTR_SET_SLOT :
    case IATTR_SET :
    case IATTR_ADD :
    case IATTR_SUB :
      set_noref(0);
      break;
    case NEXT :
    case COPY_NEXT :
      ++_slotref;
      _contexts[_slotref] = context(uint8(_code._instr_count+1));
      // if (_analysis.slotref > _analysis.max_ref) _analysis.max_ref = _analysis.slotref;
      break;
    case INSERT :
      if (_slotref >= 0) --_slotref;
      _code._modify = true;
      break;
    case PUT_SUBS_8BIT_OBS :    // slotref on 1st parameter
    case PUT_SUBS :
      _code._modify = true;
      set_changed(0);
      GR_FALLTHROUGH;
      // no break
    case PUT_COPY :
      if (arg[0] != 0) { set_changed(0); _code._modify = true; }
      set_ref(arg[0]);
      break;
    case PUSH_GLYPH_ATTR_OBS :
    case PUSH_SLOT_ATTR :
    case PUSH_GLYPH_METRIC :
    case PUSH_ATT_TO_GATTR_OBS :
    case PUSH_ATT_TO_GLYPH_METRIC :
    case PUSH_ISLOT_ATTR :
    case PUSH_FEAT :
    case SET_FEAT :
      set_ref(arg[1]);
      break;
    case PUSH_ATT_TO_GLYPH_ATTR :
    case PUSH_GLYPH_ATTR :
      set_ref(arg[2]);
      break;
    default:
        break;
  }
}

bool Machine::Code::decoder::emit_opcode(opcode opc, const byte * & bc)
{
    const opcode_t * op_to_fn = Machine::getOpcodeTable();
    const opcode_t & op       = op_to_fn[opc];
    if (op.impl[_code._constraint] == 0)
    {
        failure(unimplemented_opcode_used);
        return false;
    }

    const size_t     param_sz = op.param_sz == VARARGS ? bc[0] + 1 : op.param_sz;

    // Add this instruction
    *_instr++ = op.impl[_code._constraint];
    ++_code._instr_count;

    // Grab the parameters
    if (param_sz) {
        memcpy(_data, bc, param_sz * sizeof(byte));
        bc               += param_sz;
        _data            += param_sz;
        _code._data_size += param_sz;
    }

    // recursively decode a context item so we can split the skip into
    // instruction and data portions.
    if (opc == CNTXT_ITEM)
    {
        assert(_out_index == 0);
        _in_ctxt_item = true;
        _out_index = _max.pre_context + int8(_data[-2]);
        _slotref = int8(_data[-2]);
        _out_length = _max.rule_length;

        const size_t ctxt_start = _code._instr_count;
        byte & instr_skip = _data[-1];
        byte & data_skip  = *_data++;
        ++_code._data_size;
        const byte *curr_end = _max.bytecode;

        if (load(bc, bc + instr_skip))
        {
            bc += instr_skip;
            data_skip  = instr_skip - byte(_code._instr_count - ctxt_start);
            instr_skip =  byte(_code._instr_count - ctxt_start);
            _max.bytecode = curr_end;

            _out_length = 1;
            _out_index = 0;
            _slotref = 0;
            _in_ctxt_item = false;
        }
        else
        {
            _out_index = 0;
            _slotref = 0;
            return false;
        }
    }

    return bool(_code);
}

void Machine::Code::decoder::apply_analysis(instr * const code, instr * code_end)
{
    // insert TEMP_COPY commands for slots that need them (that change and are referenced later)
    int tempcount = 0;
    if (_code._constraint) return;

    const instr temp_copy = Machine::getOpcodeTable()[TEMP_COPY].impl[0];
    for (const context * c = _contexts, * const ce = c + _slotref; c < ce; ++c)
    {
        if (!c->flags.referenced || !c->flags.changed) continue;

        instr * const tip = code + c->codeRef + tempcount;
        memmove(tip+1, tip, (code_end - tip) * sizeof(instr));
        *tip = temp_copy;
        ++code_end;
        ++tempcount;
        _code._delete = true;
    }

    _code._instr_count = code_end - code;
}

inline
bool Machine::Code::decoder::validate_opcode(const byte opc, const byte * const bc)
{
    if (opc >= MAX_OPCODE)
    {
        failure(invalid_opcode);
        return false;
    }
    const opcode_t & op = Machine::getOpcodeTable()[opc];
    if (op.impl[_code._constraint] == 0)
    {
        failure(unimplemented_opcode_used);
        return false;
    }
    if (op.param_sz == VARARGS && bc >= _max.bytecode)
    {
        failure(arguments_exhausted);
        return false;
    }
    const size_t param_sz = op.param_sz == VARARGS ? bc[0] + 1 : op.param_sz;
    if (bc - 1 + param_sz >= _max.bytecode)
    {
        failure(arguments_exhausted);
        return false;
    }
    return true;
}

bool Machine::Code::decoder::valid_upto(const uint16 limit, const uint16 x) const throw()
{
    const bool t = (limit != 0) && (x < limit);
    if (!t) failure(out_of_range_data);
    return t;
}

inline
bool Machine::Code::decoder::test_ref(int8 index) const throw()
{
    if (_code._constraint && !_in_ctxt_item)
    {
        if (index > 0 || -index > _max.pre_context)
        {
            failure(out_of_range_data);
            return false;
        }
    }
    else
    {
      if (_max.rule_length == 0
          || (_slotref + _max.pre_context + index >= _max.rule_length)
          || (_slotref + _max.pre_context + index < 0))
      {
        failure(out_of_range_data);
        return false;
      }
    }
    return true;
}

bool Machine::Code::decoder::test_context() const throw()
{
    if (_out_index >= _out_length || _out_index < 0 || _slotref >= NUMCONTEXTS - 1)
    {
        failure(out_of_range_data);
        return false;
    }
    return true;
}

bool Machine::Code::decoder::test_attr(attrCode) const throw()
{
#if 0   // This code is coming but causes backward compatibility problems.
    if (_passtype < PASS_TYPE_POSITIONING)
    {
        if (attr != gr_slatBreak && attr != gr_slatDir && attr != gr_slatUserDefn
                                 && attr != gr_slatCompRef)
        {
            failure(out_of_range_data);
            return false;
        }
    }
#endif
    return true;
}

inline
void Machine::Code::failure(const status_t s) throw() {
    release_buffers();
    _status = s;
}

inline
void Machine::Code::decoder::set_ref(int index) throw() {
    if (index + _slotref < 0 || index + _slotref >= NUMCONTEXTS) return;
    _contexts[index + _slotref].flags.referenced = true;
    if (index + _slotref > _max_ref) _max_ref = index + _slotref;
}

inline
void Machine::Code::decoder::set_noref(int index) throw() {
    if (index + _slotref < 0 || index + _slotref >= NUMCONTEXTS) return;
    if (index + _slotref > _max_ref) _max_ref = index + _slotref;
}

inline
void Machine::Code::decoder::set_changed(int index) throw() {
    if (index + _slotref < 0 || index + _slotref >= NUMCONTEXTS) return;
    _contexts[index + _slotref].flags.changed= true;
    if (index + _slotref > _max_ref) _max_ref = index + _slotref;
}

void Machine::Code::release_buffers() throw()
{
    if (_own)
        free(_code);
    _code = 0;
    _data = 0;
    _own  = false;
}

int32 Machine::Code::run(Machine & m, slotref * & map) const
{
//    assert(_own);
    assert(*this);          // Check we are actually runnable

    if (m.slotMap().size() <= size_t(_max_ref + m.slotMap().context())
        || m.slotMap()[_max_ref + m.slotMap().context()] == 0)
    {
        m._status = Machine::slot_offset_out_bounds;
        return 1;
//        return m.run(_code, _data, map);
    }

    return  m.run(_code, _data, map);
}


text:
/*  GRAPHITE2 LICENSING

    Copyright 2010, SIL International
    All rights reserved.

    This library is free software; you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published
    by the Free Software Foundation; either version 2.1 of License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should also have received a copy of the GNU Lesser General Public
    License along with this library in the file named "LICENSE".
    If not, write to the Free Software Foundation, 51 Franklin Street,
    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
    internet at http://www.fsf.org/licenses/lgpl.html.

Alternatively, the contents of this file may be used under the terms of the
Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
License, as published by the Free Software Foundation, either version 2
of the License or (at your option) any later version.
*/
#include <cassert>
#include "graphite2/Segment.h"
#include "inc/CharInfo.h"

extern "C"
{

unsigned int gr_cinfo_unicode_char(const gr_char_info* p/*not NULL*/)
{
    assert(p);
    return p->unicodeChar();
}

int gr_cinfo_break_weight(const gr_char_info* p/*not NULL*/)
{
    assert(p);
    return p->breakWeight();
}

int gr_cinfo_after(const gr_char_info *p/*not NULL*/)
{
    assert(p);
    return p->after();
}

int gr_cinfo_before(const gr_char_info *p/*not NULL*/)
{
    assert(p);
    return p->before();
}

size_t gr_cinfo_base(const gr_char_info *p/*not NULL*/)
{
    assert(p);
    return p->base();
}

} // extern "C"

repo_language: C

mime_type: text/x-c


text:
/*  GRAPHITE2 LICENSING

    Copyright 2010, SIL International
    All rights reserved.

    This library is free software; you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published
    by the Free Software Foundation; either version 2.1 of License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should also have received a copy of the GNU Lesser General Public
    License along with this library in the file named "LICENSE".
    If not, write to the Free Software Foundation, 51 Franklin Street,
    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
    internet at http://www.fsf.org/licenses/lgpl.html.

Alternatively, the contents of this file may be used under the terms of the
Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
License, as published by the Free Software Foundation, either version 2
of the License or (at your option) any later version.
*/
// This direct threaded interpreter implmentation for machine.h
// Author: Tim Eves

// Build either this interpreter or the call_machine implementation.
// The direct threaded interpreter is relies upon a gcc feature called
// labels-as-values so is only portable to compilers that support the
// extension (gcc only as far as I know) however it should build on any
// architecture gcc supports.
// This is twice as fast as the call threaded model and is likely faster on
// inorder processors with short pipelines and little branch prediction such
// as the ARM and possibly Atom chips.

#include <cassert>
#include <cstring>
#include "inc/Machine.h"
#include "inc/Segment.h"
#include "inc/Slot.h"
#include "inc/Rule.h"

#define STARTOP(name)           name: {
#define ENDOP                   }; goto *((sp - sb)/Machine::STACK_MAX ? &&end : *++ip);
#define EXIT(status)            { push(status); goto end; }

#define do_(name)               &&name

using namespace graphite2;
using namespace vm;

namespace {

// The GCC manual has this to say about labels as values:
//   The &&foo expressions for the same label might have different values
//   if the containing function is inlined or cloned. If a program relies
//   on them being always the same, __attribute__((__noinline__,__noclone__))
//   should be used to prevent inlining and cloning.
//
// is_return in Code.cpp relies on being able to do comparisons, so it needs
// them to be always the same.
//
// The GCC manual further adds:
//   If &&foo is used in a static variable initializer, inlining and
//   cloning is forbidden.
//
// In this file, &&foo *is* used in a static variable initializer, and it's not
// entirely clear whether this should prevent inlining of the function or not.
// In practice, though, clang 7 can end up inlining the function with ThinLTO,
// which breaks at least is_return. https://bugs.llvm.org/show_bug.cgi?id=39241
// So all in all, we need at least the __noinline__ attribute. __noclone__
// is not supported by clang.
__attribute__((__noinline__))
const void * direct_run(const bool          get_table_mode,
                        const instr       * program,
                        const byte        * data,
                        Machine::stack_t  * stack,
                        slotref         * & __map,
                        uint8                _dir,
                        Machine::status_t & status,
                        SlotMap           * __smap=0)
{
    // We need to define and return to opcode table from within this function
    // other inorder to take the addresses of the instruction bodies.
    #include "inc/opcode_table.h"
    if (get_table_mode)
        return opcode_table;

    // Declare virtual machine registers
    const instr           * ip = program;
    const byte            * dp = data;
    Machine::stack_t      * sp = stack + Machine::STACK_GUARD,
                    * const sb = sp;
    SlotMap             & smap = *__smap;
    Segment              & seg = smap.segment;
    slotref                 is = *__map,
                         * map = __map,
                  * const mapb = smap.begin()+smap.context();
    uint8                  dir = _dir;
    int8                 flags = 0;

    // start the program
    goto **ip;

    // Pull in the opcode definitions
    #include "inc/opcodes.h"

    end:
    __map  = map;
    *__map = is;
    return sp;
}

}

const opcode_t * Machine::getOpcodeTable() throw()
{
    slotref * dummy;
    Machine::status_t dumstat = Machine::finished;
    return static_cast<const opcode_t *>(direct_run(true, 0, 0, 0, dummy, 0, dumstat));
}

Machine::stack_t  Machine::run(const instr   * program,
                               const byte    * data,
                               slotref     * & is)
{
    assert(program != 0);

    const stack_t *sp = static_cast<const stack_t *>(
                direct_run(false, program, data, _stack, is, _map.dir(), _status, &_map));
    const stack_t ret = sp == _stack+STACK_GUARD+1 ? *sp-- : 0;
    check_final_stack(sp);
    return ret;
}


text:
#    GRAPHITE2 LICENSING
#
#    Copyright 2011, SIL International
#    All rights reserved.
#
#    This library is free software; you can redistribute it and/or modify
#    it under the terms of the GNU Lesser General Public License as published
#    by the Free Software Foundation; either version 2.1 of License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#    Lesser General Public License for more details.
#
#    You should also have received a copy of the GNU Lesser General Public
#    License along with this library in the file named "LICENSE".
#    If not, write to the Free Software Foundation, 51 Franklin Street,
#    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
#    internet at http://www.fsf.org/licenses/lgpl.html.
#
# Alternatively, the contents of this file may be used under the terms of the
# Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
# License, as published by the Free Software Foundation, either version 2
# of the License or (at your option) any later version.

# Makefile helper file for those wanting to build Graphite2 using make
# The including makefile should set the following variables
# _NS               Prefix to all variables this file creates (namespace)
# $(_NS)_MACHINE    Set to direct or call. Set to direct if using gcc else
#                   set to call
# $(_NS)_BASE       path to root of graphite2 project
#
# Returns:
# $(_NS)_SOURCES    List of source files (with .cpp extension)
# $(_NS)_PRIVATE_HEADERS    List of private header files (with .h extension)
# $(_NS)_PUBLIC_HEADERS     List of public header files (with .h extension)

$(_NS)_SOURCES = \
    $($(_NS)_BASE)/src/$($(_NS)_MACHINE)_machine.cpp \
    $($(_NS)_BASE)/src/gr_char_info.cpp \
    $($(_NS)_BASE)/src/gr_face.cpp \
    $($(_NS)_BASE)/src/gr_features.cpp \
    $($(_NS)_BASE)/src/gr_font.cpp \
    $($(_NS)_BASE)/src/gr_logging.cpp \
    $($(_NS)_BASE)/src/gr_segment.cpp \
    $($(_NS)_BASE)/src/gr_slot.cpp \
    $($(_NS)_BASE)/src/json.cpp \
    $($(_NS)_BASE)/src/CmapCache.cpp \
    $($(_NS)_BASE)/src/Code.cpp \
    $($(_NS)_BASE)/src/Collider.cpp \
    $($(_NS)_BASE)/src/Decompressor.cpp \
    $($(_NS)_BASE)/src/Face.cpp \
    $($(_NS)_BASE)/src/FeatureMap.cpp \
    $($(_NS)_BASE)/src/FileFace.cpp \
    $($(_NS)_BASE)/src/Font.cpp \
    $($(_NS)_BASE)/src/GlyphCache.cpp \
    $($(_NS)_BASE)/src/GlyphFace.cpp \
    $($(_NS)_BASE)/src/Intervals.cpp \
    $($(_NS)_BASE)/src/Justifier.cpp \
    $($(_NS)_BASE)/src/NameTable.cpp \
    $($(_NS)_BASE)/src/Pass.cpp \
    $($(_NS)_BASE)/src/Position.cpp \
    $($(_NS)_BASE)/src/Segment.cpp \
    $($(_NS)_BASE)/src/Silf.cpp \
    $($(_NS)_BASE)/src/Slot.cpp \
    $($(_NS)_BASE)/src/Sparse.cpp \
    $($(_NS)_BASE)/src/TtfUtil.cpp \
    $($(_NS)_BASE)/src/UtfCodec.cpp

$(_NS)_PRIVATE_HEADERS = \
    $($(_NS)_BASE)/src/inc/bits.h \
    $($(_NS)_BASE)/src/inc/debug.h \
    $($(_NS)_BASE)/src/inc/json.h \
    $($(_NS)_BASE)/src/inc/CharInfo.h \
    $($(_NS)_BASE)/src/inc/CmapCache.h \
    $($(_NS)_BASE)/src/inc/Code.h \
    $($(_NS)_BASE)/src/inc/Collider.h \
    $($(_NS)_BASE)/src/inc/Compression.h \
    $($(_NS)_BASE)/src/inc/Decompressor.h \
    $($(_NS)_BASE)/src/inc/Endian.h \
    $($(_NS)_BASE)/src/inc/Error.h \
    $($(_NS)_BASE)/src/inc/Face.h \
    $($(_NS)_BASE)/src/inc/FeatureMap.h \
    $($(_NS)_BASE)/src/inc/FeatureVal.h \
    $($(_NS)_BASE)/src/inc/FileFace.h \
    $($(_NS)_BASE)/src/inc/Font.h \
    $($(_NS)_BASE)/src/inc/GlyphCache.h \
    $($(_NS)_BASE)/src/inc/GlyphFace.h \
    $($(_NS)_BASE)/src/inc/Intervals.h \
    $($(_NS)_BASE)/src/inc/List.h \
    $($(_NS)_BASE)/src/inc/locale2lcid.h \
    $($(_NS)_BASE)/src/inc/Machine.h \
    $($(_NS)_BASE)/src/inc/Main.h \
    $($(_NS)_BASE)/src/inc/NameTable.h \
    $($(_NS)_BASE)/src/inc/opcode_table.h \
    $($(_NS)_BASE)/src/inc/opcodes.h \
    $($(_NS)_BASE)/src/inc/Pass.h \
    $($(_NS)_BASE)/src/inc/Position.h \
    $($(_NS)_BASE)/src/inc/Rule.h \
    $($(_NS)_BASE)/src/inc/Segment.h \
    $($(_NS)_BASE)/src/inc/Silf.h \
    $($(_NS)_BASE)/src/inc/Slot.h \
    $($(_NS)_BASE)/src/inc/Sparse.h \
    $($(_NS)_BASE)/src/inc/TtfTypes.h \
    $($(_NS)_BASE)/src/inc/TtfUtil.h \
    $($(_NS)_BASE)/src/inc/UtfCodec.h

$(_NS)_PUBLIC_HEADERS = \
    $($(_NS)_BASE)/include/graphite2/Font.h \
    $($(_NS)_BASE)/include/graphite2/Log.h \
    $($(_NS)_BASE)/include/graphite2/Segment.h \
    $($(_NS)_BASE)/include/graphite2/Types.h


text:
/*  GRAPHITE2 LICENSING

    Copyright 2012, SIL International
    All rights reserved.

    This library is free software; you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published
    by the Free Software Foundation; either version 2.1 of License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should also have received a copy of the GNU Lesser General Public
    License along with this library in the file named "LICENSE".
    If not, write to the Free Software Foundation, 51 Franklin Street,
    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
    internet at http://www.fsf.org/licenses/lgpl.html.

Alternatively, the contents of this file may be used under the terms of the
Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
License, as published by the Free Software Foundation, either version 2
of the License or (at your option) any later version.
*/

#include "inc/Segment.h"
#include "graphite2/Font.h"
#include "inc/debug.h"
#include "inc/CharInfo.h"
#include "inc/Slot.h"
#include "inc/Main.h"
#include <cmath>

using namespace graphite2;

class JustifyTotal {
public:
    JustifyTotal() : m_numGlyphs(0), m_tStretch(0), m_tShrink(0), m_tStep(0), m_tWeight(0) {}
    void accumulate(Slot *s, Segment *seg, int level);
    int weight() const { return m_tWeight; }

    CLASS_NEW_DELETE

private:
    int m_numGlyphs;
    int m_tStretch;
    int m_tShrink;
    int m_tStep;
    int m_tWeight;
};

void JustifyTotal::accumulate(Slot *s, Segment *seg, int level)
{
    ++m_numGlyphs;
    m_tStretch += s->getJustify(seg, level, 0);
    m_tShrink += s->getJustify(seg, level, 1);
    m_tStep += s->getJustify(seg, level, 2);
    m_tWeight += s->getJustify(seg, level, 3);
}

float Segment::justify(Slot *pSlot, const Font *font, float width, GR_MAYBE_UNUSED justFlags jflags, Slot *pFirst, Slot *pLast)
{
    Slot *end = last();
    float currWidth = 0.0;
    const float scale = font ? font->scale() : 1.0f;
    Position res;

    if (width < 0 && !(silf()->flags()))
        return width;

    if ((m_dir & 1) != m_silf->dir() && m_silf->bidiPass() != m_silf->numPasses())
    {
        reverseSlots();
        std::swap(pFirst, pLast);
    }
    if (!pFirst) pFirst = pSlot;
    while (!pFirst->isBase()) pFirst = pFirst->attachedTo();
    if (!pLast) pLast = last();
    while (!pLast->isBase()) pLast = pLast->attachedTo();
    const float base = pFirst->origin().x / scale;
    width = width / scale;
    if ((jflags & gr_justEndInline) == 0)
    {
        while (pLast != pFirst && pLast)
        {
            Rect bbox = theGlyphBBoxTemporary(pLast->glyph());
            if (bbox.bl.x != 0.f || bbox.bl.y != 0.f || bbox.tr.x != 0.f || bbox.tr.y == 0.f)
                break;
            pLast = pLast->prev();
        }
    }

    if (pLast)
        end = pLast->nextSibling();
    if (pFirst)
        pFirst = pFirst->nextSibling();

    int icount = 0;
    int numLevels = silf()->numJustLevels();
    if (!numLevels)
    {
        for (Slot *s = pSlot; s && s != end; s = s->nextSibling())
        {
            CharInfo *c = charinfo(s->before());
            if (isWhitespace(c->unicodeChar()))
            {
                s->setJustify(this, 0, 3, 1);
                s->setJustify(this, 0, 2, 1);
                s->setJustify(this, 0, 0, -1);
                ++icount;
            }
        }
        if (!icount)
        {
            for (Slot *s = pSlot; s && s != end; s = s->nextSibling())
            {
                s->setJustify(this, 0, 3, 1);
                s->setJustify(this, 0, 2, 1);
                s->setJustify(this, 0, 0, -1);
            }
        }
        ++numLevels;
    }

    Vector<JustifyTotal> stats(numLevels);
    for (Slot *s = pFirst; s && s != end; s = s->nextSibling())
    {
        float w = s->origin().x / scale + s->advance() - base;
        if (w > currWidth) currWidth = w;
        for (int j = 0; j < numLevels; ++j)
            stats[j].accumulate(s, this, j);
        s->just(0);
    }

    for (int i = (width < 0.0f) ? -1 : numLevels - 1; i >= 0; --i)
    {
        float diff;
        float error = 0.;
        float diffpw;
        int tWeight = stats[i].weight();
        if (tWeight == 0) continue;

        do {
            error = 0.;
            diff = width - currWidth;
            diffpw = diff / tWeight;
            tWeight = 0;
            for (Slot *s = pFirst; s && s != end; s = s->nextSibling()) // don't include final glyph
            {
                int w = s->getJustify(this, i, 3);
                float pref = diffpw * w + error;
                int step = s->getJustify(this, i, 2);
                if (!step) step = 1;        // handle lazy font developers
                if (pref > 0)
                {
                    float max = uint16(s->getJustify(this, i, 0));
                    if (i == 0) max -= s->just();
                    if (pref > max) pref = max;
                    else tWeight += w;
                }
                else
                {
                    float max = uint16(s->getJustify(this, i, 1));
                    if (i == 0) max += s->just();
                    if (-pref > max) pref = -max;
                    else tWeight += w;
                }
                int actual = int(pref / step) * step;

                if (actual)
                {
                    error += diffpw * w - actual;
                    if (i == 0)
                        s->just(s->just() + actual);
                    else
                        s->setJustify(this, i, 4, actual);
                }
            }
            currWidth += diff - error;
        } while (i == 0 && int(std::abs(error)) > 0 && tWeight);
    }

    Slot *oldFirst = m_first;
    Slot *oldLast = m_last;
    if (silf()->flags() & 1)
    {
        m_first = pSlot = addLineEnd(pSlot);
        m_last = pLast = addLineEnd(end);
        if (!m_first || !m_last) return -1.0;
    }
    else
    {
        m_first = pSlot;
        m_last = pLast;
    }

    // run justification passes here
#if !defined GRAPHITE2_NTRACING
    json * const dbgout = m_face->logger();
    if (dbgout)
        *dbgout << json::object
                    << "justifies"  << objectid(this)
                    << "passes"     << json::array;
#endif

    if (m_silf->justificationPass() != m_silf->positionPass() && (width >= 0.f || (silf()->flags() & 1)))
        m_silf->runGraphite(this, m_silf->justificationPass(), m_silf->positionPass());

#if !defined GRAPHITE2_NTRACING
    if (dbgout)
    {
        *dbgout     << json::item << json::close; // Close up the passes array
        positionSlots(NULL, pSlot, pLast, m_dir);
        Slot *lEnd = pLast->nextSibling();
        *dbgout << "output" << json::array;
        for(Slot * t = pSlot; t != lEnd; t = t->next())
            *dbgout     << dslot(this, t);
        *dbgout         << json::close << json::close;
    }
#endif

    res = positionSlots(font, pSlot, pLast, m_dir);

    if (silf()->flags() & 1)
    {
        if (m_first)
            delLineEnd(m_first);
        if (m_last)
            delLineEnd(m_last);
    }
    m_first = oldFirst;
    m_last = oldLast;

    if ((m_dir & 1) != m_silf->dir() && m_silf->bidiPass() != m_silf->numPasses())
        reverseSlots();
    return res.x;
}

Slot *Segment::addLineEnd(Slot *nSlot)
{
    Slot *eSlot = newSlot();
    if (!eSlot) return NULL;
    const uint16 gid = silf()->endLineGlyphid();
    const GlyphFace * theGlyph = m_face->glyphs().glyphSafe(gid);
    eSlot->setGlyph(this, gid, theGlyph);
    if (nSlot)
    {
        eSlot->next(nSlot);
        eSlot->prev(nSlot->prev());
        nSlot->prev(eSlot);
        eSlot->before(nSlot->before());
        if (eSlot->prev())
            eSlot->after(eSlot->prev()->after());
        else
            eSlot->after(nSlot->before());
    }
    else
    {
        nSlot = m_last;
        eSlot->prev(nSlot);
        nSlot->next(eSlot);
        eSlot->after(eSlot->prev()->after());
        eSlot->before(nSlot->after());
    }
    return eSlot;
}

void Segment::delLineEnd(Slot *s)
{
    Slot *nSlot = s->next();
    if (nSlot)
    {
        nSlot->prev(s->prev());
        if (s->prev())
            s->prev()->next(nSlot);
    }
    else
        s->prev()->next(NULL);
    freeSlot(s);
}


text:
/*  GRAPHITE2 LICENSING

    Copyright 2011, SIL International
    All rights reserved.

    This library is free software; you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published
    by the Free Software Foundation; either version 2.1 of License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should also have received a copy of the GNU Lesser General Public
    License along with this library in the file named "LICENSE".
    If not, write to the Free Software Foundation, 51 Franklin Street,
    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
    internet at http://www.fsf.org/licenses/lgpl.html.

Alternatively, the contents of this file may be used under the terms of the
Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
License, as published by the Free Software Foundation, either version 2
of the License or (at your option) any later version.
*/
#include <cassert>
#include "inc/Sparse.h"
#include "inc/bits.h"

using namespace graphite2;

const sparse::chunk sparse::empty_chunk = {0,0};

sparse::~sparse() throw()
{
    if (m_array.map == &empty_chunk) return;
    free(m_array.values);
}

sparse::mapped_type sparse::operator [] (const key_type k) const throw()
{
    mapped_type         g = key_type(k/SIZEOF_CHUNK - m_nchunks) >> (sizeof k*8 - 1);
    const chunk &       c = m_array.map[g*k/SIZEOF_CHUNK];
    const mask_t        m = c.mask >> (SIZEOF_CHUNK - 1 - (k%SIZEOF_CHUNK));
    g *= m & 1;

    return g*m_array.values[g*(c.offset + bit_set_count(m >> 1))];
}

size_t sparse::capacity() const throw()
{
    size_t n = m_nchunks,
           s = 0;

    for (const chunk *ci=m_array.map; n; --n, ++ci)
        s += bit_set_count(ci->mask);

    return s;
}


text:
/*  GRAPHITE2 LICENSING

    Copyright 2011, SIL International
    All rights reserved.

    This library is free software; you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published
    by the Free Software Foundation; either version 2.1 of License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should also have received a copy of the GNU Lesser General Public
    License along with this library in the file named "LICENSE".
    If not, write to the Free Software Foundation, 51 Franklin Street,
    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
    internet at http://www.fsf.org/licenses/lgpl.html.

Alternatively, the contents of this file may be used under the terms of the
Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
License, as published by the Free Software Foundation, either version 2
of the License or (at your option) any later version.
*/
// JSON debug logging
// Author: Tim Eves

#if !defined GRAPHITE2_NTRACING

#include <cstdio>
#include <limits>
#include "inc/json.h"

#if defined(_MSC_VER)
#define FORMAT_INTMAX "%lli"
#define FORMAT_UINTMAX "%llu"
#else
#define FORMAT_INTMAX "%ji"
#define FORMAT_UINTMAX "%ju"
#endif

using namespace graphite2;

namespace
{
    enum
    {
        seq = ',',
        obj='}', member=':', empty_obj='{',
        arr=']', empty_arr='['
    };
}

const std::nullptr_t json::null = nullptr;

inline
void json::context(const char current) throw()
{
    fprintf(_stream, "%c", *_context);
    indent();
    *_context = current;
}

void json::indent(const int d) throw()
{
    if (*_context == member || (_flatten  && _flatten < _context))
        fputc(' ', _stream);
    else
        fprintf(_stream,  "\n%*s",  4*int(_context - _contexts + d), "");
}

inline
void json::push_context(const char prefix, const char suffix) throw()
{
    assert(_context - _contexts < ptrdiff_t(sizeof _contexts));

    if (_context == _contexts)
        *_context = suffix;
    else
        context(suffix);
    *++_context = prefix;
}

void json::pop_context() throw()
{
    assert(_context > _contexts);

    if (*_context == seq)   indent(-1);
    else                    fputc(*_context, _stream);

    fputc(*--_context, _stream);
    if (_context == _contexts)  fputc('\n', _stream);
    fflush(_stream);

    if (_flatten >= _context)   _flatten = 0;
    *_context = seq;
}

// These four functions cannot be inlined as pointers to these
// functions are needed for operator << (_context_t) to work.
void json::flat(json & j) throw()   { if (!j._flatten)  j._flatten = j._context; }
void json::close(json & j) throw()  { j.pop_context(); }
void json::object(json & j) throw() { j.push_context('{', '}'); }
void json::array(json & j) throw()  { j.push_context('[', ']'); }
void json::item(json & j) throw()
{
    while (j._context > j._contexts+1 && j._context[-1] != arr)
        j.pop_context();
}

json & json::operator << (json::string s) throw()
{
    const char ctxt = _context[-1] == obj ? *_context == member ? seq : member : seq;
    context(ctxt);
    fprintf(_stream, "\"%s\"", s);
    if (ctxt == member) fputc(' ', _stream);

    return *this;
}

json & json::operator << (json::number f) throw()
{
    context(seq);
    if (std::numeric_limits<json::number>::infinity() == f)
        fputs("Infinity", _stream);
    else if (-std::numeric_limits<json::number>::infinity() == f)
        fputs("-Infinity", _stream);
    else if (std::numeric_limits<json::number>::quiet_NaN() == f ||
            std::numeric_limits<json::number>::signaling_NaN() == f)
        fputs("NaN", _stream);
    else
        fprintf(_stream, "%g", f);
    return *this;
}
json & json::operator << (json::integer d) throw()  { context(seq); fprintf(_stream, FORMAT_INTMAX, intmax_t(d)); return *this; }
json & json::operator << (json::integer_u d) throw()  { context(seq); fprintf(_stream, FORMAT_UINTMAX, uintmax_t(d)); return *this; }
json & json::operator << (json::boolean b) throw()  { context(seq); fputs(b ? "true" : "false", _stream); return *this; }
json & json::operator << (std::nullptr_t)  throw()  { context(seq); fputs("null",_stream); return *this; }

#endif


text:
/*  GRAPHITE2 LICENSING

    Copyright 2010, SIL International
    All rights reserved.

    This library is free software; you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published
    by the Free Software Foundation; either version 2.1 of License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should also have received a copy of the GNU Lesser General Public
    License along with this library in the file named "LICENSE".
    If not, write to the Free Software Foundation, 51 Franklin Street,
    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
    internet at http://www.fsf.org/licenses/lgpl.html.

Alternatively, the contents of this file may be used under the terms of the
Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
License, as published by the Free Software Foundation, either version 2
of the License or (at your option) any later version.
*/
#include <algorithm>
#include <cmath>
#include <limits>

#include "inc/Intervals.h"
#include "inc/Segment.h"
#include "inc/Slot.h"
#include "inc/debug.h"
#include "inc/bits.h"

using namespace graphite2;

#include <cmath>

inline
Zones::Exclusion  Zones::Exclusion::split_at(float p) {
    Exclusion r(*this);
    r.xm = x = p;
    return r;
}

inline
void Zones::Exclusion::left_trim(float p) {
    x = p;
}

inline
Zones::Exclusion & Zones::Exclusion::operator += (Exclusion const & rhs) {
    c += rhs.c; sm += rhs.sm; smx += rhs.smx; open = false;
    return *this;
}

inline
uint8 Zones::Exclusion::outcode(float val) const {
    float p = val;
    //float d = std::numeric_limits<float>::epsilon();
    float d = 0.;
    return ((p - xm >= d) << 1) | (x - p > d);
}

void Zones::exclude_with_margins(float xmin, float xmax, int axis) {
    remove(xmin, xmax);
    weightedAxis(axis, xmin-_margin_len, xmin, 0, 0, _margin_weight, xmin-_margin_len, 0, 0, false);
    weightedAxis(axis, xmax, xmax+_margin_len, 0, 0, _margin_weight, xmax+_margin_len, 0, 0, false);
}

namespace
{

inline
bool separated(float a, float b) {
    return a != b;
    //int exp;
    //float res = frexpf(fabs(a - b), &exp);
    //return (*(unsigned int *)(&res) > 4);
    //return std::fabs(a-b) > std::numeric_limits<float>::epsilon(); // std::epsilon may not work. but 0.5 fails exising 64 bit tests
    //return std::fabs(a-b) > 0.5f;
}

}

void Zones::insert(Exclusion e)
{
#if !defined GRAPHITE2_NTRACING
    addDebug(&e);
#endif
    e.x = max(e.x, _pos);
    e.xm = min(e.xm, _posm);
    if (e.x >= e.xm) return;

    for (iterator i = _exclusions.begin(), ie = _exclusions.end(); i != ie && e.x < e.xm; ++i)
    {
        const uint8 oca = e.outcode(i->x),
                    ocb = e.outcode(i->xm);
        if ((oca & ocb) != 0) continue;

        switch (oca ^ ocb)  // What kind of overlap?
        {
        case 0:     // e completely covers i
            // split e at i.x into e1,e2
            // split e2 at i.mx into e2,e3
            // drop e1 ,i+e2, e=e3
            *i += e;
            e.left_trim(i->xm);
            break;
        case 1:     // e overlaps on the rhs of i
            // split i at e->x into i1,i2
            // split e at i.mx into e1,e2
            // trim i1, insert i2+e1, e=e2
            if (!separated(i->xm, e.x)) break;
            if (separated(i->x,e.x))   { i = _exclusions.insert(i,i->split_at(e.x)); ++i; }
            *i += e;
            e.left_trim(i->xm);
            break;
        case 2:     // e overlaps on the lhs of i
            // split e at i->x into e1,e2
            // split i at e.mx into i1,i2
            // drop e1, insert e2+i1, trim i2
            if (!separated(e.xm, i->x)) return;
            if (separated(e.xm, i->xm)) i = _exclusions.insert(i,i->split_at(e.xm));
            *i += e;
            return;
        case 3:     // i completely covers e
            // split i at e.x into i1,i2
            // split i2 at e.mx into i2,i3
            // insert i1, insert e+i2
            if (separated(e.xm, i->xm)) i = _exclusions.insert(i,i->split_at(e.xm));
            i = _exclusions.insert(i, i->split_at(e.x));
            *++i += e;
            return;
        }

        ie = _exclusions.end();
    }
}

void Zones::remove(float x, float xm)
{
#if !defined GRAPHITE2_NTRACING
    removeDebug(x, xm);
#endif
    x = max(x, _pos);
    xm = min(xm, _posm);
    if (x >= xm) return;

    for (iterator i = _exclusions.begin(), ie = _exclusions.end(); i != ie; ++i)
    {
        const uint8 oca = i->outcode(x),
                    ocb = i->outcode(xm);
        if ((oca & ocb) != 0)   continue;

        switch (oca ^ ocb)  // What kind of overlap?
        {
        case 0:     // i completely covers e
            if (separated(i->x, x))  { i = _exclusions.insert(i,i->split_at(x)); ++i; }
            GR_FALLTHROUGH;
            // no break
        case 1:     // i overlaps on the rhs of e
            i->left_trim(xm);
            return;
        case 2:     // i overlaps on the lhs of e
            i->xm = x;
            if (separated(i->x, i->xm)) break;
            GR_FALLTHROUGH;
            // no break
        case 3:     // e completely covers i
            i = _exclusions.erase(i);
            --i;
            break;
        }

        ie = _exclusions.end();
    }
}

Zones::const_iterator Zones::find_exclusion_under(float x) const
{
    size_t l = 0, h = _exclusions.size();

    while (l < h)
    {
        size_t const p = (l+h) >> 1;
        switch (_exclusions[p].outcode(x))
        {
        case 0 : return _exclusions.begin()+p;
        case 1 : h = p; break;
        case 2 :
        case 3 : l = p+1; break;
        }
    }

    return _exclusions.begin()+l;
}

float Zones::closest(float origin, float & cost) const
{
    float best_c = std::numeric_limits<float>::max(),
          best_x = 0;

    const const_iterator start = find_exclusion_under(origin);

    // Forward scan looking for lowest cost
    for (const_iterator i = start, ie = _exclusions.end(); i != ie; ++i)
        if (i->track_cost(best_c, best_x, origin)) break;

    // Backward scan looking for lowest cost
    //  We start from the exclusion to the immediate left of start since we've
    //  already tested start with the right most scan above.
    for (const_iterator i = start-1, ie = _exclusions.begin()-1; i != ie; --i)
        if (i->track_cost(best_c, best_x, origin)) break;

    cost = (best_c == std::numeric_limits<float>::max() ? -1 : best_c);
    return best_x;
}

// Cost and test position functions

bool Zones::Exclusion::track_cost(float & best_cost, float & best_pos, float origin) const {
    const float p = test_position(origin),
                localc = cost(p - origin);
    if (open && localc > best_cost) return true;

    if (localc < best_cost)
    {
        best_cost = localc;
        best_pos = p;
    }
    return false;
}

inline
float Zones::Exclusion::cost(float p) const {
    return (sm * p - 2 * smx) * p + c;
}

float Zones::Exclusion::test_position(float origin) const {
    if (sm < 0)
    {
        // sigh, test both ends and perhaps the middle too!
        float res = x;
        float cl = cost(x);
        if (x < origin && xm > origin)
        {
            float co = cost(origin);
            if (co < cl)
            {
                cl = co;
                res = origin;
            }
        }
        float cr = cost(xm);
        return cl > cr ? xm : res;
    }
    else
    {
        float zerox = smx / sm + origin;
        if (zerox < x) return x;
        else if (zerox > xm) return xm;
        else return zerox;
    }
}

#if !defined GRAPHITE2_NTRACING

void Zones::jsonDbgOut(Segment *seg) const {

    if (_dbg)
    {
        for (Zones::idebugs s = dbgs_begin(), e = dbgs_end(); s != e; ++s)
        {
            *_dbg << json::flat << json::array
                << objectid(dslot(seg, (Slot *)(s->_env[0])))
                << reinterpret_cast<ptrdiff_t>(s->_env[1]);
            if (s->_isdel)
                *_dbg << "remove" << Position(s->_excl.x, s->_excl.xm);
            else
                *_dbg << "exclude" << json::flat << json::array
                    << s->_excl.x << s->_excl.xm
                    << s->_excl.sm << s->_excl.smx << s->_excl.c
                    << json::close;
            *_dbg << json::close;
        }
    }
}

#endif


text:
/*  GRAPHITE2 LICENSING

    Copyright 2012, SIL International
    All rights reserved.

    This library is free software; you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published
    by the Free Software Foundation; either version 2.1 of License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should also have received a copy of the GNU Lesser General Public
    License along with this library in the file named "LICENSE".
    If not, write to the Free Software Foundation, 51 Franklin Street,
    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
    internet at http://www.fsf.org/licenses/lgpl.html.

Alternatively, the contents of this file may be used under the terms of the
Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
License, as published by the Free Software Foundation, either version 2
of the License or (at your option) any later version.
*/
#pragma once

#include "graphite2/Font.h"
#include "inc/Main.h"
#include "inc/Position.h"
#include "inc/GlyphFace.h"

namespace graphite2 {

class Face;
class FeatureVal;
class Segment;

struct SlantBox
{
    static const SlantBox empty;

//    SlantBox(float psi = 0., float pdi = 0., float psa = 0., float pda = 0.) : si(psi), di(pdi), sa(psa), da(pda) {};
    float width() const { return sa - si; }
    float height() const { return da - di; }
    float si; // min
    float di; // min
    float sa; // max
    float da; // max
};

struct BBox
{
    BBox(float pxi = 0, float pyi = 0., float pxa = 0., float pya = 0.) : xi(pxi), yi(pyi), xa(pxa), ya(pya) {};
    float width() const { return xa - xi; }
    float height() const { return ya - yi; }
    float xi; // min
    float yi; // min
    float xa; // max
    float ya; // max
};

class GlyphBox
{
    GlyphBox(const GlyphBox &);
    GlyphBox & operator = (const GlyphBox &);

public:
    GlyphBox(uint8 numsubs, unsigned short bitmap, Rect *slanted) : _num(numsubs), _bitmap(bitmap), _slant(*slanted) {};

    void addSubBox(int subindex, int boundary, Rect *val) { _subs[subindex * 2 + boundary] = *val; }
    Rect &subVal(int subindex, int boundary) { return _subs[subindex * 2 + boundary]; }
    const Rect &slant() const { return _slant; }
    uint8 num() const { return _num; }
    const Rect *subs() const { return _subs; }

private:
    uint8   _num;
    unsigned short  _bitmap;
    Rect    _slant;
    Rect    _subs[1];
};

class GlyphCache
{
    class Loader;

    GlyphCache(const GlyphCache&);
    GlyphCache& operator=(const GlyphCache&);

public:
    GlyphCache(const Face & face, const uint32 face_options);
    ~GlyphCache();

    unsigned short  numGlyphs() const throw();
    unsigned short  numAttrs() const throw();
    unsigned short  unitsPerEm() const throw();

    const GlyphFace *glyph(unsigned short glyphid) const;      //result may be changed by subsequent call with a different glyphid
    const GlyphFace *glyphSafe(unsigned short glyphid) const;
    float            getBoundingMetric(unsigned short glyphid, uint8 metric) const;
    uint8            numSubBounds(unsigned short glyphid) const;
    float            getSubBoundingMetric(unsigned short glyphid, uint8 subindex, uint8 metric) const;
    const Rect &     slant(unsigned short glyphid) const { return _boxes[glyphid] ? _boxes[glyphid]->slant() : _empty_slant_box; }
    const SlantBox & getBoundingSlantBox(unsigned short glyphid) const;
    const BBox &     getBoundingBBox(unsigned short glyphid) const;
    const SlantBox & getSubBoundingSlantBox(unsigned short glyphid, uint8 subindex) const;
    const BBox &     getSubBoundingBBox(unsigned short glyphid, uint8 subindex) const;
    bool             check(unsigned short glyphid) const;
    bool             hasBoxes() const { return _boxes != 0; }

    CLASS_NEW_DELETE;

private:
    const Rect            _empty_slant_box;
    const Loader        * _glyph_loader;
    const GlyphFace *   * _glyphs;
    GlyphBox        *   * _boxes;
    unsigned short        _num_glyphs,
                          _num_attrs,
                          _upem;
};

inline
unsigned short GlyphCache::numGlyphs() const throw()
{
    return _num_glyphs;
}

inline
unsigned short GlyphCache::numAttrs() const throw()
{
    return _num_attrs;
}

inline
unsigned short  GlyphCache::unitsPerEm() const throw()
{
    return _upem;
}

inline
bool GlyphCache::check(unsigned short glyphid) const
{
    return _boxes && glyphid < _num_glyphs;
}

inline
const GlyphFace *GlyphCache::glyphSafe(unsigned short glyphid) const
{
    return glyphid < _num_glyphs ? glyph(glyphid) : NULL;
}

inline
float GlyphCache::getBoundingMetric(unsigned short glyphid, uint8 metric) const
{
    if (glyphid >= _num_glyphs) return 0.;
    switch (metric) {
        case 0: return (float)(glyph(glyphid)->theBBox().bl.x);                          // x_min
        case 1: return (float)(glyph(glyphid)->theBBox().bl.y);                          // y_min
        case 2: return (float)(glyph(glyphid)->theBBox().tr.x);                          // x_max
        case 3: return (float)(glyph(glyphid)->theBBox().tr.y);                          // y_max
        case 4: return (float)(_boxes[glyphid] ? _boxes[glyphid]->slant().bl.x : 0.f);    // sum_min
        case 5: return (float)(_boxes[glyphid] ? _boxes[glyphid]->slant().bl.y : 0.f);    // diff_min
        case 6: return (float)(_boxes[glyphid] ? _boxes[glyphid]->slant().tr.x : 0.f);    // sum_max
        case 7: return (float)(_boxes[glyphid] ? _boxes[glyphid]->slant().tr.y : 0.f);    // diff_max
        default: return 0.;
    }
}

inline const SlantBox &GlyphCache::getBoundingSlantBox(unsigned short glyphid) const
{
    return _boxes[glyphid] ? *(SlantBox *)(&(_boxes[glyphid]->slant())) : SlantBox::empty;
}

inline const BBox &GlyphCache::getBoundingBBox(unsigned short glyphid) const
{
    return *(BBox *)(&(glyph(glyphid)->theBBox()));
}

inline
float GlyphCache::getSubBoundingMetric(unsigned short glyphid, uint8 subindex, uint8 metric) const
{
    GlyphBox *b = _boxes[glyphid];
    if (b == NULL || subindex >= b->num()) return 0;

    switch (metric) {
        case 0: return b->subVal(subindex, 0).bl.x;
        case 1: return b->subVal(subindex, 0).bl.y;
        case 2: return b->subVal(subindex, 0).tr.x;
        case 3: return b->subVal(subindex, 0).tr.y;
        case 4: return b->subVal(subindex, 1).bl.x;
        case 5: return b->subVal(subindex, 1).bl.y;
        case 6: return b->subVal(subindex, 1).tr.x;
        case 7: return b->subVal(subindex, 1).tr.y;
        default: return 0.;
    }
}

inline const SlantBox &GlyphCache::getSubBoundingSlantBox(unsigned short glyphid, uint8 subindex) const
{
    GlyphBox *b = _boxes[glyphid];
    return *(SlantBox *)(b->subs() + 2 * subindex + 1);
}

inline const BBox &GlyphCache::getSubBoundingBBox(unsigned short glyphid, uint8 subindex) const
{
    GlyphBox *b = _boxes[glyphid];
    return *(BBox *)(b->subs() + 2 * subindex);
}

inline
uint8 GlyphCache::numSubBounds(unsigned short glyphid) const
{
    return _boxes[glyphid] ? _boxes[glyphid]->num() : 0;
}

} // namespace graphite2


text:
/*  GRAPHITE2 LICENSING

    Copyright 2011, SIL International
    All rights reserved.

    This library is free software; you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published
    by the Free Software Foundation; either version 2.1 of License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should also have received a copy of the GNU Lesser General Public
    License along with this library in the file named "LICENSE".
    If not, write to the Free Software Foundation, 51 Franklin Street,
    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
    internet at http://www.fsf.org/licenses/lgpl.html.

Alternatively, the contents of this file may be used under the terms of the
Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
License, as published by the Free Software Foundation, either version 2
of the License or (at your option) any later version.
*/

/*
Description:
    A set of fast template based decoders for decoding values of any C integer
    type up to long int size laid out with most significant byte first or least
    significant byte first (aka big endian or little endian).  These are CPU
    byte order agnostic and will function the same regardless of the CPUs native
    byte order.

    Being template based means if the either le or be class is not used then
    template code of unused functions will not be instantiated by the compiler
    and thus shouldn't cause any overhead.
*/

#include <cstddef>

#pragma once

class be
{
    template<int S>
    inline static unsigned long int _peek(const unsigned char * p) {
        return _peek<S/2>(p) << (S/2)*8 | _peek<S/2>(p+S/2);
    }
public:
    template<typename T>
    inline static T peek(const void * p) {
        return T(_peek<sizeof(T)>(static_cast<const unsigned char *>(p)));
    }

    template<typename T>
    inline static T read(const unsigned char * &p) {
        const T r = T(_peek<sizeof(T)>(p));
        p += sizeof r;
        return r;
    }

    template<typename T>
    inline static T swap(const T x) {
        return T(_peek<sizeof(T)>(reinterpret_cast<const unsigned char *>(&x)));
    }

    template<typename T>
    inline static void skip(const unsigned char * &p, size_t n=1) {
        p += sizeof(T)*n;
    }
};

template<>
inline unsigned long int be::_peek<1>(const unsigned char * p) { return *p; }

class le
{
    template<int S>
    inline static unsigned long int _peek(const unsigned char * p) {
        return _peek<S/2>(p) | _peek<S/2>(p+S/2)  << (S/2)*8;
    }
public:
    template<typename T>
    inline static T peek(const void * p) {
        return T(_peek<sizeof(T)>(static_cast<const unsigned char *>(p)));
    }

    template<typename T>
    inline static T read(const unsigned char * &p) {
        const T r = T(_peek<sizeof(T)>(p));
        p += sizeof r;
        return r;
    }

    template<typename T>
    inline static T swap(const T x) {
        return T(_peek<sizeof(T)>(reinterpret_cast<const unsigned char *>(&x)));
    }

    template<typename T>
    inline static void skip(const unsigned char * &p, size_t n=1) {
        p += sizeof(T)*n;
    }
};

template<>
inline unsigned long int le::_peek<1>(const unsigned char * p) { return *p; }


text:
/*  GRAPHITE2 LICENSING

    Copyright 2010, SIL International
    All rights reserved.

    This library is free software; you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published
    by the Free Software Foundation; either version 2.1 of License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should also have received a copy of the GNU Lesser General Public
    License along with this library in the file named "LICENSE".
    If not, write to the Free Software Foundation, 51 Franklin Street,
    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
    internet at http://www.fsf.org/licenses/lgpl.html.

Alternatively, the contents of this file may be used under the terms of the
Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
License, as published by the Free Software Foundation, either version 2
of the License or (at your option) any later version.
*/
#pragma once
#include "inc/Main.h"
#include "inc/FeatureVal.h"

namespace graphite2 {

// Forward declarations for implmentation types
class FeatureMap;
class Face;

class FeatureSetting
{
public:
    FeatureSetting(int16 theValue, uint16 labelId) : m_label(labelId), m_value(theValue) {};
    uint16 label() const { return m_label; }
    int16 value() const { return m_value; }

    CLASS_NEW_DELETE;
private:
    FeatureSetting(const FeatureSetting & fs) : m_label(fs.m_label), m_value(fs.m_value) {};

    uint16 m_label;
    int16 m_value;
};

class FeatureRef
{
    typedef uint32      chunk_t;
    static const uint8  SIZEOF_CHUNK = sizeof(chunk_t)*8;

public:
    enum flags_t : uint16 {
        HIDDEN = 0x0800
    };
    FeatureRef() throw();
    FeatureRef(const Face & face, unsigned short & bits_offset, uint32 max_val,
               uint32 name, uint16 uiName, flags_t flags,
               FeatureSetting *settings, uint16 num_set) throw();
    ~FeatureRef() throw();

    bool applyValToFeature(uint32 val, Features& pDest) const; //defined in GrFaceImp.h
    void maskFeature(Features & pDest) const {
    if (m_index < pDest.size())                 //defensive
        pDest[m_index] |= m_mask;
    }

    uint32 getFeatureVal(const Features& feats) const; //defined in GrFaceImp.h

    uint32 getId() const { return m_id; }
    uint16 getNameId() const { return m_nameid; }
    uint16 getNumSettings() const { return m_numSet; }
    uint16 getSettingName(uint16 index) const { return m_nameValues[index].label(); }
    int16  getSettingValue(uint16 index) const { return m_nameValues[index].value(); }
    flags_t getFlags() const { return m_flags; }
    uint32 maxVal() const { return m_max; }
    const Face & getFace() const { assert(m_face); return *m_face;}
    const FeatureMap* getFeatureMap() const;// { return m_pFace;}

    CLASS_NEW_DELETE;
private:
    FeatureRef(const FeatureRef & rhs);

    const Face     * m_face;
    FeatureSetting * m_nameValues; // array of name table ids for feature values
    chunk_t m_mask,             // bit mask to get the value from the vector
            m_max;              // max value the value can take
    uint32  m_id;               // feature identifier/name
    uint16  m_nameid,           // Name table id for feature name
            m_numSet;           // number of values (number of entries in m_nameValues)
    flags_t m_flags;            // feature flags see FeatureRef::flags_t.
    byte    m_bits,             // how many bits to shift the value into place
            m_index;            // index into the array to find the ulong to mask

private:        //unimplemented
    FeatureRef& operator=(const FeatureRef&);
};

inline
FeatureRef::FeatureRef() throw()
: m_face(0),
  m_nameValues(0),
  m_mask(0), m_max(0),
  m_id(0), m_nameid(0), m_numSet(0),
  m_flags(flags_t(0)),
  m_bits(0), m_index(0)
{
}

class NameAndFeatureRef
{
  public:
    NameAndFeatureRef(uint32 name = 0) : m_name(name) , m_pFRef(NULL){}
    NameAndFeatureRef(FeatureRef const & p) : m_name(p.getId()), m_pFRef(&p) {}

    bool operator<(const NameAndFeatureRef& rhs) const //orders by m_name
        {   return m_name<rhs.m_name; }

    CLASS_NEW_DELETE

    uint32 m_name;
    const FeatureRef* m_pFRef;
};

class FeatureMap
{
public:
    FeatureMap() : m_numFeats(0), m_feats(NULL), m_pNamedFeats(NULL) {}
    ~FeatureMap() { delete[] m_feats; delete[] m_pNamedFeats; }

    bool readFeats(const Face & face);
    const FeatureRef *findFeatureRef(uint32 name) const;
    FeatureRef *feature(uint16 index) const { return m_feats + index; }
    //GrFeatureRef *featureRef(byte index) { return index < m_numFeats ? m_feats + index : NULL; }
    const FeatureRef *featureRef(byte index) const { return index < m_numFeats ? m_feats + index : NULL; }
    FeatureVal* cloneFeatures(uint32 langname/*0 means default*/) const;      //call destroy_Features when done.
    uint16 numFeats() const { return m_numFeats; };
    CLASS_NEW_DELETE
private:
friend class SillMap;
    uint16 m_numFeats;

    FeatureRef *m_feats;
    NameAndFeatureRef* m_pNamedFeats;   //owned
    FeatureVal m_defaultFeatures;        //owned

private:        //defensive on m_feats, m_pNamedFeats, and m_defaultFeatures
    FeatureMap(const FeatureMap&);
    FeatureMap& operator=(const FeatureMap&);
};

class SillMap
{
private:
    class LangFeaturePair
    {
        LangFeaturePair(const LangFeaturePair &);
        LangFeaturePair & operator = (const LangFeaturePair &);

    public:
        LangFeaturePair() :  m_lang(0), m_pFeatures(0) {}
        ~LangFeaturePair() { delete m_pFeatures; }

        uint32 m_lang;
        Features* m_pFeatures;      //owns
        CLASS_NEW_DELETE
    };
public:
    SillMap() : m_langFeats(NULL), m_numLanguages(0) {}
    ~SillMap() { delete[] m_langFeats; }
    bool readFace(const Face & face);
    bool readSill(const Face & face);
    FeatureVal* cloneFeatures(uint32 langname/*0 means default*/) const;      //call destroy_Features when done.
    uint16 numLanguages() const { return m_numLanguages; };
    uint32 getLangName(uint16 index) const { return (index < m_numLanguages)? m_langFeats[index].m_lang : 0; };

    const FeatureMap & theFeatureMap() const { return m_FeatureMap; };
private:
    FeatureMap m_FeatureMap;        //of face
    LangFeaturePair * m_langFeats;
    uint16 m_numLanguages;

private:        //defensive on m_langFeats
    SillMap(const SillMap&);
    SillMap& operator=(const SillMap&);
};

} // namespace graphite2

struct gr_feature_ref : public graphite2::FeatureRef {};


text:
/*  GRAPHITE2 LICENSING

    Copyright 2010, SIL International
    All rights reserved.

    This library is free software; you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published
    by the Free Software Foundation; either version 2.1 of License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should also have received a copy of the GNU Lesser General Public
    License along with this library in the file named "LICENSE".
    If not, write to the Free Software Foundation, 51 Franklin Street,
    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
    internet at http://www.fsf.org/licenses/lgpl.html.

Alternatively, the contents of this file may be used under the terms of the
Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
License, as published by the Free Software Foundation, either version 2
of the License or (at your option) any later version.
*/

// designed to have a limited subset of the std::vector api
#pragma once

#include <cstddef>
#include <cassert>
#include <cstring>
#include <cstdlib>
#include <new>

#include "Main.h"

namespace graphite2 {

template <typename T>
inline
ptrdiff_t distance(T* first, T* last) { return last-first; }

template <typename T>
class Vector
{
    T * m_first, *m_last, *m_end;
public:
    typedef       T &   reference;
    typedef const T &   const_reference;
    typedef       T *   iterator;
    typedef const T *   const_iterator;

    Vector() : m_first(0), m_last(0), m_end(0) {}
    Vector(size_t n, const T& value = T())      : m_first(0), m_last(0), m_end(0) { insert(begin(), n, value); }
    Vector(const Vector<T> &rhs)                : m_first(0), m_last(0), m_end(0) { insert(begin(), rhs.begin(), rhs.end()); }
    template <typename I>
    Vector(I first, const I last)               : m_first(0), m_last(0), m_end(0) { insert(begin(), first, last); }
    ~Vector() { clear(); free(m_first); }

    iterator            begin()         { return m_first; }
    const_iterator      begin() const   { return m_first; }

    iterator            end()           { return m_last; }
    const_iterator      end() const     { return m_last; }

    bool                empty() const   { return m_first == m_last; }
    size_t              size() const    { return m_last - m_first; }
    size_t              capacity() const{ return m_end - m_first; }

    void                reserve(size_t n);
    void                resize(size_t n, const T & v = T());

    reference           front()         { assert(size() > 0); return *begin(); }
    const_reference     front() const   { assert(size() > 0); return *begin(); }
    reference           back()          { assert(size() > 0); return *(end()-1); }
    const_reference     back() const    { assert(size() > 0); return *(end()-1); }

    Vector<T>         & operator = (const Vector<T> & rhs) { assign(rhs.begin(), rhs.end()); return *this; }
    reference           operator [] (size_t n)          { assert(size() > n); return m_first[n]; }
    const_reference     operator [] (size_t n) const    { assert(size() > n); return m_first[n]; }

    void                assign(size_t n, const T& u)    { clear(); insert(begin(), n, u); }
    void                assign(const_iterator first, const_iterator last)      { clear(); insert(begin(), first, last); }
    iterator            insert(iterator p, const T & x) { p = _insert_default(p, 1); new (p) T(x); return p; }
    void                insert(iterator p, size_t n, const T & x);
    void                insert(iterator p, const_iterator first, const_iterator last);
    void                pop_back()              { assert(size() > 0); --m_last; }
    void                push_back(const T &v)   { if (m_last == m_end) reserve(size()+1); new (m_last++) T(v); }

    void                clear()                 { erase(begin(), end()); }
    iterator            erase(iterator p)       { return erase(p, p+1); }
    iterator            erase(iterator first, iterator last);

private:
    iterator            _insert_default(iterator p, size_t n);
};

template <typename T>
inline
void Vector<T>::reserve(size_t n)
{
    if (n > capacity())
    {
        const ptrdiff_t sz = size();
        size_t requested;
        if (checked_mul(n,sizeof(T), requested))  std::abort();
        m_first = static_cast<T*>(realloc(m_first, requested));
        if (!m_first)   std::abort();
        m_last  = m_first + sz;
        m_end   = m_first + n;
    }
}

template <typename T>
inline
void Vector<T>::resize(size_t n, const T & v) {
    const ptrdiff_t d = n-size();
    if (d < 0)      erase(end()+d, end());
    else if (d > 0) insert(end(), d, v);
}

template<typename T>
inline
typename Vector<T>::iterator Vector<T>::_insert_default(iterator p, size_t n)
{
    assert(begin() <= p && p <= end());
    const ptrdiff_t i = p - begin();
    reserve(((size() + n + 7) >> 3) << 3);
    p = begin() + i;
    // Move tail if there is one
    if (p != end()) memmove(p + n, p, distance(p,end())*sizeof(T));
    m_last += n;
    return p;
}

template<typename T>
inline
void Vector<T>::insert(iterator p, size_t n, const T & x)
{
    p = _insert_default(p, n);
    // Copy in elements
    for (; n; --n, ++p) { new (p) T(x); }
}

template<typename T>
inline
void Vector<T>::insert(iterator p, const_iterator first, const_iterator last)
{
    p = _insert_default(p, distance(first, last));
    // Copy in elements
    for (;first != last; ++first, ++p) { new (p) T(*first); }
}

template<typename T>
inline
typename Vector<T>::iterator Vector<T>::erase(iterator first, iterator last)
{
    for (iterator e = first; e != last; ++e) e->~T();
    const size_t sz = distance(first, last);
    if (m_last != last) memmove(first, last, distance(last,end())*sizeof(T));
    m_last -= sz;
    return first;
}

} // namespace graphite2


text:
/*  GRAPHITE2 LICENSING

    Copyright 2013, SIL International
    All rights reserved.

    This library is free software; you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published
    by the Free Software Foundation; either version 2.1 of License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should also have received a copy of the GNU Lesser General Public
    License along with this library in the file named "LICENSE".
    If not, write to the Free Software Foundation, 51 Franklin Street,
    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
    internet at http://www.fsf.org/licenses/lgpl.html.

Alternatively, the contents of this file may be used under the terms of the
Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
License, as published by the Free Software Foundation, either version 2
of the License or (at your option) any later version.
*/
#pragma once

// numbers are explicitly assigned for future proofing

namespace graphite2
{

class Error
{
public:
    Error() : _e(0) {};
    operator bool() { return (_e != 0); }
    int error() { return _e; }
    void error(int e) { _e = e; }
    bool test(bool pr, int err) { return (_e = int(pr) * err); }

private:
    int _e;
};

enum errcontext {
    EC_READGLYPHS = 1,      // while reading glyphs
    EC_READSILF = 2,        // in Silf table
    EC_ASILF = 3,           // in Silf %d
    EC_APASS = 4,           // in Silf %d, pass %d
    EC_PASSCCODE = 5,       // in pass constraint code for Silf %d, pass %d
    EC_ARULE = 6,           // in Silf %d, pass %d, rule %d
    EC_ASTARTS = 7,         // in Silf %d, pass %d, start state %d
    EC_ATRANS = 8,          // in Silf %d, pass %d, fsm state %d
    EC_ARULEMAP = 9         // in Silf %d, pass %d, state %d
};

enum errors {
    E_OUTOFMEM = 1,         // Out of memory
    E_NOGLYPHS = 2,         // There are no glyphs in the font
    E_BADUPEM = 3,          // The units per em for the font is bad (0)
    E_BADCMAP = 4,          // The font does not contain any useful cmaps
    E_NOSILF = 5,           // Missing Silf table
    E_TOOOLD = 6,           // Silf table version is too old
    E_BADSIZE = 7,          // context object has the wrong structural size
// Silf Subtable Errors take a Silf subtable number * 256 in the context
    E_BADMAXGLYPH = 8,      // Silf max glyph id is too high
    E_BADNUMJUSTS = 9,      // Number of Silf justification blocks is too high
    E_BADENDJUSTS = 10,     // Silf justification blocks take too much of the Silf table space
    E_BADCRITFEATURES = 11, // Critical features section in a Silf table is too big
    E_BADSCRIPTTAGS = 12,   // Silf script tags area is too big
    E_BADAPSEUDO = 13,      // The pseudo glyph attribute number is too high
    E_BADABREAK = 14,       // The linebreak glyph attribute number is too high
    E_BADABIDI = 15,        // The bidi glyph attribute number is too high
    E_BADAMIRROR = 16,      // The mirrored glyph attribute number is too high
    E_BADNUMPASSES = 17,    // The number of passes is > 128
    E_BADPASSESSTART = 18,  // The Silf table is too small to hold any passes
    E_BADPASSBOUND = 19,    // The positioning pass number is too low or the substitution pass number is too high
    E_BADPPASS = 20,        // The positioning pass number is too high
    E_BADSPASS = 21,        // the substitution pass number is too high
    E_BADJPASSBOUND = 22,   // the justification pass must be higher than the positioning pass
    E_BADJPASS = 23,        // the justification pass is too high
    E_BADALIG = 24,         // the number of initial ligature component glyph attributes is too high
    E_BADBPASS = 25,        // the bidi pass number is specified and is either too high or too low
    E_BADNUMPSEUDO = 26,    // The number of pseudo glyphs is too high
    E_BADCLASSSIZE = 27,    // The size of the classes block is bad
    E_TOOMANYLINEAR = 28,   // The number of linear classes in the silf table is too high
    E_CLASSESTOOBIG = 29,   // There are too many classes for the space allocated in the Silf subtable
    E_MISALIGNEDCLASSES = 30,   // The class offsets in the class table don't line up with the number of classes
    E_HIGHCLASSOFFSET = 31, // The class offsets point out of the class table
    E_BADCLASSOFFSET = 32,  // A class offset is less than one following it
    E_BADCLASSLOOKUPINFO = 33,  // The search header info for a non-linear class has wrong values in it
// Pass subtable errors. Context has pass number * 65536
    E_BADPASSSTART = 34,    // The start offset for a particular pass is bad
    E_BADPASSEND = 35,      // The end offset for a particular pass is bad
    E_BADPASSLENGTH = 36,   // The length of the pass is too small
    E_BADNUMTRANS = 37,     // The number of transition states in the fsm is bad
    E_BADNUMSUCCESS = 38,   // The number of success states in the fsm is bad
    E_BADNUMSTATES = 39,    // The number of states in the fsm is bad
    E_NORANGES = 40,        // There are no columns in the fsm
    E_BADRULEMAPLEN = 41,   // The size of the success state to rule mapping is bad
    E_BADCTXTLENBOUNDS = 42,    // The precontext maximum is greater than its minimum
    E_BADCTXTLENS = 43,     // The lists of rule lengths or pre context lengths is bad
    E_BADPASSCCODEPTR = 44, // The pass constraint code position does not align with where the forward reference says it should be
    E_BADRULECCODEPTR = 45, // The rule constraint code position does not align with where the forward reference says it should be
    E_BADCCODELEN = 46,     // Bad rule/pass constraint code length
    E_BADACTIONCODEPTR = 47,    // The action code position does not align with where the forward reference says it should be
    E_MUTABLECCODE = 48,    // Constraint code edits slots. It shouldn't.
    E_BADSTATE = 49,        // Bad state transition referencing an illegal state
    E_BADRULEMAPPING = 50,  // The structure of the rule mapping is bad
    E_BADRANGE = 51,        // Bad column range structure including a glyph in more than one column
    E_BADRULENUM = 52,      // A reference to a rule is out of range (too high)
    E_BADACOLLISION = 53,   // Bad Silf table collision attribute number (too high)
    E_BADEMPTYPASS = 54,    // Can't have empty passes (no rules) except for collision passes
    E_BADSILFVERSION = 55,  // The Silf table has a bad version (probably too high)
    E_BADCOLLISIONPASS = 56,    // Collision flags set on a non positioning pass
    E_BADNUMCOLUMNS = 57,   // Arbitrarily limit number of columns in fsm
// Code errors
    E_CODEFAILURE = 60,     // Base code error. The following subcodes must align with Machine::Code::status_t in Code.h
    E_CODEALLOC = 61,       // Out of memory
    E_INVALIDOPCODE = 62,   // Invalid op code
    E_UNIMPOPCODE = 63,     // Unimplemented op code encountered
    E_OUTOFRANGECODE = 64,  // Code argument out of range
    E_BADJUMPCODE = 65,     // Code jumps past end of op codes
    E_CODEBADARGS = 66,     // Code arguments exhausted
    E_CODENORETURN = 67,    // Missing return type op code at end of code
    E_CODENESTEDCTXT = 68,   // Nested context encountered in code
// Compression errors
    E_BADSCHEME = 69,
    E_SHRINKERFAILED = 70,
};

}


text:
/*  GRAPHITE2 LICENSING

    Copyright 2010, SIL International
    All rights reserved.

    This library is free software; you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published
    by the Free Software Foundation; either version 2.1 of License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should also have received a copy of the GNU Lesser General Public
    License along with this library in the file named "LICENSE".
    If not, write to the Free Software Foundation, 51 Franklin Street,
    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
    internet at http://www.fsf.org/licenses/lgpl.html.

Alternatively, the contents of this file may be used under the terms of the
Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
License, as published by the Free Software Foundation, either version 2
of the License or (at your option) any later version.
*/
// This general interpreter interface.
// Author: Tim Eves

// Build one of direct_machine.cpp or call_machine.cpp to implement this
// interface.

#pragma once
#include <cstring>
#include <limits>
#include <graphite2/Types.h>
#include "inc/Main.h"

#if defined(__GNUC__)
#if defined(__clang__) || (__GNUC__ * 100 + __GNUC_MINOR__ * 10) < 430
#define     HOT
#if defined(__x86_64)
#define     REGPARM(n)      __attribute__((regparm(n)))
#else
#define     REGPARM(n)
#endif
#else
#define     HOT             __attribute__((hot))
#if defined(__x86_64)
#define     REGPARM(n)      __attribute__((hot, regparm(n)))
#else
#define     REGPARM(n)
#endif
#endif
#else
#define     HOT
#define     REGPARM(n)
#endif

#if defined(__MINGW32__)
// MinGW's <limits> at some point includes winnt.h which #define's a
// DELETE macro, which conflicts with enum opcode below, so we undefine
// it here.
#undef DELETE
#endif

namespace graphite2 {

// Forward declarations
class Segment;
class Slot;
class SlotMap;

namespace vm
{

typedef void * instr;
typedef Slot * slotref;

enum {VARARGS = 0xff, MAX_NAME_LEN=32};

enum opcode {
    NOP = 0,

    PUSH_BYTE,      PUSH_BYTEU,     PUSH_SHORT,     PUSH_SHORTU,    PUSH_LONG,

    ADD,            SUB,            MUL,            DIV,
    MIN_,           MAX_,
    NEG,
    TRUNC8,         TRUNC16,

    COND,

    AND,            OR,             NOT,
    EQUAL,          NOT_EQ,
    LESS,           GTR,            LESS_EQ,        GTR_EQ,

    NEXT,           NEXT_N,         COPY_NEXT,
    PUT_GLYPH_8BIT_OBS,              PUT_SUBS_8BIT_OBS,   PUT_COPY,
    INSERT,         DELETE,
    ASSOC,
    CNTXT_ITEM,

    ATTR_SET,       ATTR_ADD,       ATTR_SUB,
    ATTR_SET_SLOT,
    IATTR_SET_SLOT,
    PUSH_SLOT_ATTR,                 PUSH_GLYPH_ATTR_OBS,
    PUSH_GLYPH_METRIC,              PUSH_FEAT,
    PUSH_ATT_TO_GATTR_OBS,          PUSH_ATT_TO_GLYPH_METRIC,
    PUSH_ISLOT_ATTR,

    PUSH_IGLYPH_ATTR,    // not implemented

    POP_RET,                        RET_ZERO,           RET_TRUE,
    IATTR_SET,                      IATTR_ADD,          IATTR_SUB,
    PUSH_PROC_STATE,                PUSH_VERSION,
    PUT_SUBS,                       PUT_SUBS2,          PUT_SUBS3,
    PUT_GLYPH,                      PUSH_GLYPH_ATTR,    PUSH_ATT_TO_GLYPH_ATTR,
    BITOR,                          BITAND,             BITNOT,
    BITSET,                         SET_FEAT,
    MAX_OPCODE,
    // private opcodes for internal use only, comes after all other on disk opcodes
    TEMP_COPY = MAX_OPCODE
};

struct opcode_t
{
    instr           impl[2];
    uint8           param_sz;
    char            name[MAX_NAME_LEN];
};

class Machine
{
public:
    typedef int32  stack_t;
    static size_t const STACK_ORDER  = 10,
                        STACK_MAX    = 1 << STACK_ORDER,
                        STACK_GUARD  = 2;

    class Code;

    enum status_t {
        finished = 0,
        stack_underflow,
        stack_not_empty,
        stack_overflow,
        slot_offset_out_bounds,
        died_early
    };

    Machine(SlotMap &) throw();
    static const opcode_t *   getOpcodeTable() throw();

    CLASS_NEW_DELETE;

    SlotMap   & slotMap() const throw();
    status_t    status() const throw();
//    operator bool () const throw();

private:
    void    check_final_stack(const stack_t * const sp);
    stack_t run(const instr * program, const byte * data,
                slotref * & map) HOT;

    SlotMap       & _map;
    stack_t         _stack[STACK_MAX + 2*STACK_GUARD];
    status_t        _status;
};

inline Machine::Machine(SlotMap & map) throw()
: _map(map), _status(finished)
{
    // Initialise stack guard +1 entries as the stack pointer points to the
    //  current top of stack, hence the first push will never write entry 0.
    // Initialising the guard space like this is unnecessary and is only
    //  done to keep valgrind happy during fuzz testing.  Hopefully loop
    //  unrolling will flatten this.
    for (size_t n = STACK_GUARD + 1; n; --n)  _stack[n-1] = 0;
}

inline SlotMap& Machine::slotMap() const throw()
{
    return _map;
}

inline Machine::status_t Machine::status() const throw()
{
    return _status;
}

inline void Machine::check_final_stack(const stack_t * const sp)
{
    if (_status != finished) return;

    stack_t const * const base  = _stack + STACK_GUARD,
                  * const limit = base + STACK_MAX;
    if      (sp <  base)    _status = stack_underflow;       // This should be impossible now.
    else if (sp >= limit)   _status = stack_overflow;        // So should this.
    else if (sp != base)    _status = stack_not_empty;
}

} // namespace vm
} // namespace graphite2


text:
/*  GRAPHITE2 LICENSING

    Copyright 2010, SIL International
    All rights reserved.

    This library is free software; you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published
    by the Free Software Foundation; either version 2.1 of License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should also have received a copy of the GNU Lesser General Public
    License along with this library in the file named "LICENSE".
    If not, write to the Free Software Foundation, 51 Franklin Street,
    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
    internet at http://www.fsf.org/licenses/lgpl.html.

Alternatively, the contents of this file may be used under the terms of the
Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
License, as published by the Free Software Foundation, either version 2
of the License or (at your option) any later version.
*/
#pragma once
#include <cstring>
#include <cassert>
#include "inc/Main.h"
#include "inc/List.h"

namespace graphite2 {

class FeatureRef;
class FeatureMap;

class FeatureVal : public Vector<uint32>
{
public:
    FeatureVal() : m_pMap(0) { }
    FeatureVal(int num, const FeatureMap & pMap) : Vector<uint32>(num), m_pMap(&pMap) {}
    FeatureVal(const FeatureVal & rhs) : Vector<uint32>(rhs), m_pMap(rhs.m_pMap) {}

    FeatureVal & operator = (const FeatureVal & rhs) { Vector<uint32>::operator = (rhs); m_pMap = rhs.m_pMap; return *this; }

    bool operator ==(const FeatureVal & b) const
    {
        size_t n = size();
        if (n != b.size())      return false;

        for(const_iterator l = begin(), r = b.begin(); n && *l == *r; --n, ++l, ++r);

        return n == 0;
    }

    CLASS_NEW_DELETE
private:
    friend class FeatureRef;        //so that FeatureRefs can manipulate m_vec directly
    const FeatureMap* m_pMap;
};

typedef FeatureVal Features;

} // namespace graphite2

struct gr_feature_val : public graphite2::FeatureVal {};