Source code

Revision control

Copy as Markdown

Other Tools

/* 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
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
// This file will be pulled into and integrated into a machine implmentation
// DO NOT build directly and under no circumstances ever #include headers in
// here or you will break the direct_machine.
//
// Implementers' notes
// ==================
// You have access to a few primitives and the full C++ code:
// declare_params(n) Tells the interpreter how many bytes of parameter
// space to claim for this instruction uses and
// initialises the param pointer. You *must* before the
// first use of param.
// use_params(n) Claim n extra bytes of param space beyond what was
// claimed using delcare_param.
// param A const byte pointer for the parameter space claimed by
// this instruction.
// binop(op) Implement a binary operation on the stack using the
// specified C++ operator.
// NOT_IMPLEMENTED Any instruction body containing this will exit the
// program with an assertion error. Instructions that are
// not implemented should also be marked NILOP in the
// opcodes tables this will cause the code class to spot
// them in a live code stream and throw a runtime_error
// instead.
// push(n) Push the value n onto the stack.
// pop() Pop the top most value and return it.
//
// You have access to the following named fast 'registers':
// sp = The pointer to the current top of stack, the last value
// pushed.
// seg = A reference to the Segment this code is running over.
// is = The current slot index
// isb = The original base slot index at the start of this rule
// isf = The first positioned slot
// isl = The last positioned slot
// ip = The current instruction pointer
// endPos = Position of advance of last cluster
// dir = writing system directionality of the font
// #define NOT_IMPLEMENTED assert(false)
// #define NOT_IMPLEMENTED
#define binop(op) const uint32 a = pop(); *sp = uint32(*sp) op a
#define sbinop(op) const int32 a = pop(); *sp = int32(*sp) op a
#define use_params(n) dp += n
#define declare_params(n) const byte * param = dp; \
use_params(n);
#define push(n) { *++sp = n; }
#define pop() (*sp--)
#define slotat(x) (map[(x)])
#define DIE { is=seg.last(); status = Machine::died_early; EXIT(1); }
#define POSITIONED 1
STARTOP(nop)
do {} while (0);
ENDOP
STARTOP(push_byte)
declare_params(1);
push(int8(*param));
ENDOP
STARTOP(push_byte_u)
declare_params(1);
push(uint8(*param));
ENDOP
STARTOP(push_short)
declare_params(2);
const int16 r = int16(param[0]) << 8
| uint8(param[1]);
push(r);
ENDOP
STARTOP(push_short_u)
declare_params(2);
const uint16 r = uint16(param[0]) << 8
| uint8(param[1]);
push(r);
ENDOP
STARTOP(push_long)
declare_params(4);
const int32 r = int32(param[0]) << 24
| uint32(param[1]) << 16
| uint32(param[2]) << 8
| uint8(param[3]);
push(r);
ENDOP
STARTOP(add)
binop(+);
ENDOP
STARTOP(sub)
binop(-);
ENDOP
STARTOP(mul)
binop(*);
ENDOP
STARTOP(div_)
const int32 b = pop();
const int32 a = int32(*sp);
if (b == 0 || (a == std::numeric_limits<int32>::min() && b == -1)) DIE;
*sp = int32(*sp) / b;
ENDOP
STARTOP(min_)
const int32 a = pop(), b = *sp;
if (a < b) *sp = a;
ENDOP
STARTOP(max_)
const int32 a = pop(), b = *sp;
if (a > b) *sp = a;
ENDOP
STARTOP(neg)
*sp = uint32(-int32(*sp));
ENDOP
STARTOP(trunc8)
*sp = uint8(*sp);
ENDOP
STARTOP(trunc16)
*sp = uint16(*sp);
ENDOP
STARTOP(cond)
const uint32 f = pop(), t = pop(), c = pop();
push(c ? t : f);
ENDOP
STARTOP(and_)
binop(&&);
ENDOP
STARTOP(or_)
binop(||);
ENDOP
STARTOP(not_)
*sp = !*sp;
ENDOP
STARTOP(equal)
binop(==);
ENDOP
STARTOP(not_eq_)
binop(!=);
ENDOP
STARTOP(less)
sbinop(<);
ENDOP
STARTOP(gtr)
sbinop(>);
ENDOP
STARTOP(less_eq)
sbinop(<=);
ENDOP
STARTOP(gtr_eq)
sbinop(>=);
ENDOP
STARTOP(next)
if (map - &smap[0] >= int(smap.size())) DIE
if (is)
{
if (is == smap.highwater())
smap.highpassed(true);
is = is->next();
}
++map;
ENDOP
//STARTOP(next_n)
// use_params(1);
// NOT_IMPLEMENTED;
//declare_params(1);
//const size_t num = uint8(*param);
//ENDOP
//STARTOP(copy_next)
// if (is) is = is->next();
// ++map;
// ENDOP
STARTOP(put_glyph_8bit_obs)
declare_params(1);
const unsigned int output_class = uint8(*param);
is->setGlyph(&seg, seg.getClassGlyph(output_class, 0));
ENDOP
STARTOP(put_subs_8bit_obs)
declare_params(3);
const int slot_ref = int8(param[0]);
const unsigned int input_class = uint8(param[1]),
output_class = uint8(param[2]);
uint16 index;
slotref slot = slotat(slot_ref);
if (slot)
{
index = seg.findClassIndex(input_class, slot->gid());
is->setGlyph(&seg, seg.getClassGlyph(output_class, index));
}
ENDOP
STARTOP(put_copy)
declare_params(1);
const int slot_ref = int8(*param);
if (is && !is->isDeleted())
{
slotref ref = slotat(slot_ref);
if (ref && ref != is)
{
int16 *tempUserAttrs = is->userAttrs();
if (is->attachedTo() || is->firstChild()) DIE
Slot *prev = is->prev();
Slot *next = is->next();
memcpy(tempUserAttrs, ref->userAttrs(), seg.numAttrs() * sizeof(uint16));
memcpy(is, ref, sizeof(Slot));
is->firstChild(NULL);
is->nextSibling(NULL);
is->userAttrs(tempUserAttrs);
is->next(next);
is->prev(prev);
if (is->attachedTo())
is->attachedTo()->child(is);
}
is->markCopied(false);
is->markDeleted(false);
}
ENDOP
STARTOP(insert)
if (smap.decMax() <= 0) DIE;
Slot *newSlot = seg.newSlot();
if (!newSlot) DIE;
Slot *iss = is;
while (iss && iss->isDeleted()) iss = iss->next();
if (!iss)
{
if (seg.last())
{
seg.last()->next(newSlot);
newSlot->prev(seg.last());
newSlot->before(seg.last()->before());
seg.last(newSlot);
}
else
{
seg.first(newSlot);
seg.last(newSlot);
}
}
else if (iss->prev())
{
iss->prev()->next(newSlot);
newSlot->prev(iss->prev());
newSlot->before(iss->prev()->after());
}
else
{
newSlot->prev(NULL);
newSlot->before(iss->before());
seg.first(newSlot);
}
newSlot->next(iss);
if (iss)
{
iss->prev(newSlot);
newSlot->originate(iss->original());
newSlot->after(iss->before());
}
else if (newSlot->prev())
{
newSlot->originate(newSlot->prev()->original());
newSlot->after(newSlot->prev()->after());
}
else
{
newSlot->originate(seg.defaultOriginal());
}
if (is == smap.highwater())
smap.highpassed(false);
is = newSlot;
seg.extendLength(1);
if (map != &smap[-1])
--map;
ENDOP
STARTOP(delete_)
if (!is || is->isDeleted()) DIE
is->markDeleted(true);
if (is->prev())
is->prev()->next(is->next());
else
seg.first(is->next());
if (is->next())
is->next()->prev(is->prev());
else
seg.last(is->prev());
if (is == smap.highwater())
smap.highwater(is->next());
if (is->prev())
is = is->prev();
seg.extendLength(-1);
ENDOP
STARTOP(assoc)
declare_params(1);
unsigned int num = uint8(*param);
const int8 * assocs = reinterpret_cast<const int8 *>(param+1);
use_params(num);
int max = -1;
int min = -1;
while (num-- > 0)
{
int sr = *assocs++;
slotref ts = slotat(sr);
if (ts && (min == -1 || ts->before() < min)) min = ts->before();
if (ts && ts->after() > max) max = ts->after();
}
if (min > -1) // implies max > -1
{
is->before(min);
is->after(max);
}
ENDOP
STARTOP(cntxt_item)
// It turns out this is a cunningly disguised condition forward jump.
declare_params(3);
const int is_arg = int8(param[0]);
const size_t iskip = uint8(param[1]),
dskip = uint8(param[2]);
if (mapb + is_arg != map)
{
ip += iskip;
dp += dskip;
push(true);
}
ENDOP
STARTOP(attr_set)
declare_params(1);
const attrCode slat = attrCode(uint8(*param));
const int val = pop();
is->setAttr(&seg, slat, 0, val, smap);
ENDOP
STARTOP(attr_add)
declare_params(1);
const attrCode slat = attrCode(uint8(*param));
const uint32_t val = pop();
if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0)
{
seg.positionSlots(0, *smap.begin(), *(smap.end()-1), seg.currdir());
flags |= POSITIONED;
}
uint32_t res = uint32_t(is->getAttr(&seg, slat, 0));
is->setAttr(&seg, slat, 0, int32_t(val + res), smap);
ENDOP
STARTOP(attr_sub)
declare_params(1);
const attrCode slat = attrCode(uint8(*param));
const uint32_t val = pop();
if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0)
{
seg.positionSlots(0, *smap.begin(), *(smap.end()-1), seg.currdir());
flags |= POSITIONED;
}
uint32_t res = uint32_t(is->getAttr(&seg, slat, 0));
is->setAttr(&seg, slat, 0, int32_t(res - val), smap);
ENDOP
STARTOP(attr_set_slot)
declare_params(1);
const attrCode slat = attrCode(uint8(*param));
const int offset = int(map - smap.begin())*int(slat == gr_slatAttTo);
const int val = pop() + offset;
is->setAttr(&seg, slat, offset, val, smap);
ENDOP
STARTOP(iattr_set_slot)
declare_params(2);
const attrCode slat = attrCode(uint8(param[0]));
const uint8 idx = uint8(param[1]);
const int val = int(pop() + (map - smap.begin())*int(slat == gr_slatAttTo));
is->setAttr(&seg, slat, idx, val, smap);
ENDOP
STARTOP(push_slot_attr)
declare_params(2);
const attrCode slat = attrCode(uint8(param[0]));
const int slot_ref = int8(param[1]);
if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0)
{
seg.positionSlots(0, *smap.begin(), *(smap.end()-1), seg.currdir());
flags |= POSITIONED;
}
slotref slot = slotat(slot_ref);
if (slot)
{
int res = slot->getAttr(&seg, slat, 0);
push(res);
}
ENDOP
STARTOP(push_glyph_attr_obs)
declare_params(2);
const unsigned int glyph_attr = uint8(param[0]);
const int slot_ref = int8(param[1]);
slotref slot = slotat(slot_ref);
if (slot)
push(int32(seg.glyphAttr(slot->gid(), glyph_attr)));
ENDOP
STARTOP(push_glyph_metric)
declare_params(3);
const unsigned int glyph_attr = uint8(param[0]);
const int slot_ref = int8(param[1]);
const signed int attr_level = uint8(param[2]);
slotref slot = slotat(slot_ref);
if (slot)
push(seg.getGlyphMetric(slot, glyph_attr, attr_level, dir));
ENDOP
STARTOP(push_feat)
declare_params(2);
const unsigned int feat = uint8(param[0]);
const int slot_ref = int8(param[1]);
slotref slot = slotat(slot_ref);
if (slot)
{
uint8 fid = seg.charinfo(slot->original())->fid();
push(seg.getFeature(fid, feat));
}
ENDOP
STARTOP(push_att_to_gattr_obs)
declare_params(2);
const unsigned int glyph_attr = uint8(param[0]);
const int slot_ref = int8(param[1]);
slotref slot = slotat(slot_ref);
if (slot)
{
slotref att = slot->attachedTo();
if (att) slot = att;
push(int32(seg.glyphAttr(slot->gid(), glyph_attr)));
}
ENDOP
STARTOP(push_att_to_glyph_metric)
declare_params(3);
const unsigned int glyph_attr = uint8(param[0]);
const int slot_ref = int8(param[1]);
const signed int attr_level = uint8(param[2]);
slotref slot = slotat(slot_ref);
if (slot)
{
slotref att = slot->attachedTo();
if (att) slot = att;
push(int32(seg.getGlyphMetric(slot, glyph_attr, attr_level, dir)));
}
ENDOP
STARTOP(push_islot_attr)
declare_params(3);
const attrCode slat = attrCode(uint8(param[0]));
const int slot_ref = int8(param[1]),
idx = uint8(param[2]);
if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0)
{
seg.positionSlots(0, *smap.begin(), *(smap.end()-1), seg.currdir());
flags |= POSITIONED;
}
slotref slot = slotat(slot_ref);
if (slot)
{
int res = slot->getAttr(&seg, slat, idx);
push(res);
}
ENDOP
#if 0
STARTOP(push_iglyph_attr) // not implemented
NOT_IMPLEMENTED;
ENDOP
#endif
STARTOP(pop_ret)
const uint32 ret = pop();
EXIT(ret);
ENDOP
STARTOP(ret_zero)
EXIT(0);
ENDOP
STARTOP(ret_true)
EXIT(1);
ENDOP
STARTOP(iattr_set)
declare_params(2);
const attrCode slat = attrCode(uint8(param[0]));
const uint8 idx = uint8(param[1]);
const int val = pop();
is->setAttr(&seg, slat, idx, val, smap);
ENDOP
STARTOP(iattr_add)
declare_params(2);
const attrCode slat = attrCode(uint8(param[0]));
const uint8 idx = uint8(param[1]);
const uint32_t val = pop();
if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0)
{
seg.positionSlots(0, *smap.begin(), *(smap.end()-1), seg.currdir());
flags |= POSITIONED;
}
uint32_t res = uint32_t(is->getAttr(&seg, slat, idx));
is->setAttr(&seg, slat, idx, int32_t(val + res), smap);
ENDOP
STARTOP(iattr_sub)
declare_params(2);
const attrCode slat = attrCode(uint8(param[0]));
const uint8 idx = uint8(param[1]);
const uint32_t val = pop();
if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0)
{
seg.positionSlots(0, *smap.begin(), *(smap.end()-1), seg.currdir());
flags |= POSITIONED;
}
uint32_t res = uint32_t(is->getAttr(&seg, slat, idx));
is->setAttr(&seg, slat, idx, int32_t(res - val), smap);
ENDOP
STARTOP(push_proc_state)
use_params(1);
push(1);
ENDOP
STARTOP(push_version)
push(0x00030000);
ENDOP
STARTOP(put_subs)
declare_params(5);
const int slot_ref = int8(param[0]);
const unsigned int input_class = uint8(param[1]) << 8
| uint8(param[2]);
const unsigned int output_class = uint8(param[3]) << 8
| uint8(param[4]);
slotref slot = slotat(slot_ref);
if (slot)
{
int index = seg.findClassIndex(input_class, slot->gid());
is->setGlyph(&seg, seg.getClassGlyph(output_class, index));
}
ENDOP
#if 0
STARTOP(put_subs2) // not implemented
NOT_IMPLEMENTED;
ENDOP
STARTOP(put_subs3) // not implemented
NOT_IMPLEMENTED;
ENDOP
#endif
STARTOP(put_glyph)
declare_params(2);
const unsigned int output_class = uint8(param[0]) << 8
| uint8(param[1]);
is->setGlyph(&seg, seg.getClassGlyph(output_class, 0));
ENDOP
STARTOP(push_glyph_attr)
declare_params(3);
const unsigned int glyph_attr = uint8(param[0]) << 8
| uint8(param[1]);
const int slot_ref = int8(param[2]);
slotref slot = slotat(slot_ref);
if (slot)
push(int32(seg.glyphAttr(slot->gid(), glyph_attr)));
ENDOP
STARTOP(push_att_to_glyph_attr)
declare_params(3);
const unsigned int glyph_attr = uint8(param[0]) << 8
| uint8(param[1]);
const int slot_ref = int8(param[2]);
slotref slot = slotat(slot_ref);
if (slot)
{
slotref att = slot->attachedTo();
if (att) slot = att;
push(int32(seg.glyphAttr(slot->gid(), glyph_attr)));
}
ENDOP
STARTOP(temp_copy)
slotref newSlot = seg.newSlot();
if (!newSlot || !is) DIE;
int16 *tempUserAttrs = newSlot->userAttrs();
memcpy(newSlot, is, sizeof(Slot));
memcpy(tempUserAttrs, is->userAttrs(), seg.numAttrs() * sizeof(uint16));
newSlot->userAttrs(tempUserAttrs);
newSlot->markCopied(true);
*map = newSlot;
ENDOP
STARTOP(band)
binop(&);
ENDOP
STARTOP(bor)
binop(|);
ENDOP
STARTOP(bnot)
*sp = ~*sp;
ENDOP
STARTOP(setbits)
declare_params(4);
const uint16 m = uint16(param[0]) << 8
| uint8(param[1]);
const uint16 v = uint16(param[2]) << 8
| uint8(param[3]);
*sp = ((*sp) & ~m) | v;
ENDOP
STARTOP(set_feat)
declare_params(2);
const unsigned int feat = uint8(param[0]);
const int slot_ref = int8(param[1]);
slotref slot = slotat(slot_ref);
if (slot)
{
uint8 fid = seg.charinfo(slot->original())->fid();
seg.setFeature(fid, feat, pop());
}
ENDOP