cppcheck/test/bug-hunting/cve/CVE-2019-10023/Function.cc

1568 lines
35 KiB
C++
Raw Permalink Normal View History

//========================================================================
//
// Function.cc
//
// Copyright 2001-2003 Glyph & Cog, LLC
//
//========================================================================
#include <aconf.h>
#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include "gmem.h"
#include "gmempp.h"
#include "GList.h"
#include "Object.h"
#include "Dict.h"
#include "Stream.h"
#include "Error.h"
#include "Function.h"
//------------------------------------------------------------------------
// Max depth of nested functions. This is used to catch infinite
// loops in the function object structure.
#define recursionLimit 8
//------------------------------------------------------------------------
// Function
//------------------------------------------------------------------------
Function::Function() {
}
Function::~Function() {
}
Function *Function::parse(Object *funcObj, int recursion) {
Function *func;
Dict *dict;
int funcType;
Object obj1;
if (recursion > recursionLimit) {
error(errSyntaxError, -1, "Loop detected in function objects");
return NULL;
}
if (funcObj->isStream()) {
dict = funcObj->streamGetDict();
} else if (funcObj->isDict()) {
dict = funcObj->getDict();
} else if (funcObj->isName("Identity")) {
return new IdentityFunction();
} else {
error(errSyntaxError, -1, "Expected function dictionary or stream");
return NULL;
}
if (!dict->lookup("FunctionType", &obj1)->isInt()) {
error(errSyntaxError, -1, "Function type is missing or wrong type");
obj1.free();
return NULL;
}
funcType = obj1.getInt();
obj1.free();
if (funcType == 0) {
func = new SampledFunction(funcObj, dict);
} else if (funcType == 2) {
func = new ExponentialFunction(funcObj, dict);
} else if (funcType == 3) {
func = new StitchingFunction(funcObj, dict, recursion);
} else if (funcType == 4) {
func = new PostScriptFunction(funcObj, dict);
} else {
error(errSyntaxError, -1, "Unimplemented function type ({0:d})", funcType);
return NULL;
}
if (!func->isOk()) {
delete func;
return NULL;
}
return func;
}
GBool Function::init(Dict *dict) {
Object obj1, obj2;
int i;
//----- Domain
if (!dict->lookup("Domain", &obj1)->isArray()) {
error(errSyntaxError, -1, "Function is missing domain");
goto err2;
}
m = obj1.arrayGetLength() / 2;
if (m > funcMaxInputs) {
error(errSyntaxError, -1,
"Functions with more than {0:d} inputs are unsupported",
funcMaxInputs);
goto err2;
}
for (i = 0; i < m; ++i) {
obj1.arrayGet(2*i, &obj2);
if (!obj2.isNum()) {
error(errSyntaxError, -1, "Illegal value in function domain array");
goto err1;
}
domain[i][0] = obj2.getNum();
obj2.free();
obj1.arrayGet(2*i+1, &obj2);
if (!obj2.isNum()) {
error(errSyntaxError, -1, "Illegal value in function domain array");
goto err1;
}
domain[i][1] = obj2.getNum();
obj2.free();
}
obj1.free();
//----- Range
hasRange = gFalse;
n = 0;
if (dict->lookup("Range", &obj1)->isArray()) {
hasRange = gTrue;
n = obj1.arrayGetLength() / 2;
if (n > funcMaxOutputs) {
error(errSyntaxError, -1,
"Functions with more than {0:d} outputs are unsupported",
funcMaxOutputs);
goto err2;
}
for (i = 0; i < n; ++i) {
obj1.arrayGet(2*i, &obj2);
if (!obj2.isNum()) {
error(errSyntaxError, -1, "Illegal value in function range array");
goto err1;
}
range[i][0] = obj2.getNum();
obj2.free();
obj1.arrayGet(2*i+1, &obj2);
if (!obj2.isNum()) {
error(errSyntaxError, -1, "Illegal value in function range array");
goto err1;
}
range[i][1] = obj2.getNum();
obj2.free();
}
}
obj1.free();
return gTrue;
err1:
obj2.free();
err2:
obj1.free();
return gFalse;
}
//------------------------------------------------------------------------
// IdentityFunction
//------------------------------------------------------------------------
IdentityFunction::IdentityFunction() {
int i;
// fill these in with arbitrary values just in case they get used
// somewhere
m = funcMaxInputs;
n = funcMaxOutputs;
for (i = 0; i < funcMaxInputs; ++i) {
domain[i][0] = 0;
domain[i][1] = 1;
}
hasRange = gFalse;
}
IdentityFunction::~IdentityFunction() {
}
void IdentityFunction::transform(double *in, double *out) {
int i;
for (i = 0; i < funcMaxOutputs; ++i) {
out[i] = in[i];
}
}
//------------------------------------------------------------------------
// SampledFunction
//------------------------------------------------------------------------
SampledFunction::SampledFunction(Object *funcObj, Dict *dict) {
Stream *str;
int sampleBits;
double sampleMul;
Object obj1, obj2;
Guint buf, bitMask;
int bits;
Guint s;
double in[funcMaxInputs];
int i, j, t, bit, idx;
idxOffset = NULL;
samples = NULL;
sBuf = NULL;
ok = gFalse;
//----- initialize the generic stuff
if (!init(dict)) {
goto err1;
}
if (!hasRange) {
error(errSyntaxError, -1, "Type 0 function is missing range");
goto err1;
}
if (m > sampledFuncMaxInputs) {
error(errSyntaxError, -1,
"Sampled functions with more than {0:d} inputs are unsupported",
sampledFuncMaxInputs);
goto err1;
}
//----- buffer
sBuf = (double *)gmallocn(1 << m, sizeof(double));
//----- get the stream
if (!funcObj->isStream()) {
error(errSyntaxError, -1, "Type 0 function isn't a stream");
goto err1;
}
str = funcObj->getStream();
//----- Size
if (!dict->lookup("Size", &obj1)->isArray() ||
obj1.arrayGetLength() != m) {
error(errSyntaxError, -1, "Function has missing or invalid size array");
goto err2;
}
for (i = 0; i < m; ++i) {
obj1.arrayGet(i, &obj2);
if (!obj2.isInt()) {
error(errSyntaxError, -1, "Illegal value in function size array");
goto err3;
}
sampleSize[i] = obj2.getInt();
if (sampleSize[i] <= 0) {
error(errSyntaxError, -1, "Illegal non-positive value in function size array");
goto err3;
}
obj2.free();
}
obj1.free();
idxOffset = (int *)gmallocn(1 << m, sizeof(int));
for (i = 0; i < (1<<m); ++i) {
idx = 0;
for (j = m - 1, t = i; j >= 1; --j, t <<= 1) {
if (sampleSize[j] == 1) {
bit = 0;
} else {
bit = (t >> (m - 1)) & 1;
}
idx = (idx + bit) * sampleSize[j-1];
}
if (sampleSize[0] == 1) {
bit = 0;
} else {
bit = (t >> (m - 1)) & 1;
}
idxOffset[i] = (idx + bit) * n;
}
//----- BitsPerSample
if (!dict->lookup("BitsPerSample", &obj1)->isInt()) {
error(errSyntaxError, -1, "Function has missing or invalid BitsPerSample");
goto err2;
}
sampleBits = obj1.getInt();
sampleMul = 1.0 / (pow(2.0, (double)sampleBits) - 1);
obj1.free();
//----- Encode
if (dict->lookup("Encode", &obj1)->isArray() &&
obj1.arrayGetLength() == 2*m) {
for (i = 0; i < m; ++i) {
obj1.arrayGet(2*i, &obj2);
if (!obj2.isNum()) {
error(errSyntaxError, -1, "Illegal value in function encode array");
goto err3;
}
encode[i][0] = obj2.getNum();
obj2.free();
obj1.arrayGet(2*i+1, &obj2);
if (!obj2.isNum()) {
error(errSyntaxError, -1, "Illegal value in function encode array");
goto err3;
}
encode[i][1] = obj2.getNum();
obj2.free();
}
} else {
for (i = 0; i < m; ++i) {
encode[i][0] = 0;
encode[i][1] = sampleSize[i] - 1;
}
}
obj1.free();
for (i = 0; i < m; ++i) {
inputMul[i] = (encode[i][1] - encode[i][0]) /
(domain[i][1] - domain[i][0]);
}
//----- Decode
if (dict->lookup("Decode", &obj1)->isArray() &&
obj1.arrayGetLength() == 2*n) {
for (i = 0; i < n; ++i) {
obj1.arrayGet(2*i, &obj2);
if (!obj2.isNum()) {
error(errSyntaxError, -1, "Illegal value in function decode array");
goto err3;
}
decode[i][0] = obj2.getNum();
obj2.free();
obj1.arrayGet(2*i+1, &obj2);
if (!obj2.isNum()) {
error(errSyntaxError, -1, "Illegal value in function decode array");
goto err3;
}
decode[i][1] = obj2.getNum();
obj2.free();
}
} else {
for (i = 0; i < n; ++i) {
decode[i][0] = range[i][0];
decode[i][1] = range[i][1];
}
}
obj1.free();
//----- samples
nSamples = n;
for (i = 0; i < m; ++i)
nSamples *= sampleSize[i];
samples = (double *)gmallocn(nSamples, sizeof(double));
buf = 0;
bits = 0;
bitMask = (sampleBits < 32) ? ((1 << sampleBits) - 1) : 0xffffffffU;
str->reset();
for (i = 0; i < nSamples; ++i) {
if (sampleBits == 8) {
s = str->getChar();
} else if (sampleBits == 16) {
s = str->getChar();
s = (s << 8) + str->getChar();
} else if (sampleBits == 32) {
s = str->getChar();
s = (s << 8) + str->getChar();
s = (s << 8) + str->getChar();
s = (s << 8) + str->getChar();
} else {
while (bits < sampleBits) {
buf = (buf << 8) | (str->getChar() & 0xff);
bits += 8;
}
s = (buf >> (bits - sampleBits)) & bitMask;
bits -= sampleBits;
}
samples[i] = (double)s * sampleMul;
}
str->close();
// set up the cache
for (i = 0; i < m; ++i) {
in[i] = domain[i][0];
cacheIn[i] = in[i] - 1;
}
transform(in, cacheOut);
ok = gTrue;
return;
err3:
obj2.free();
err2:
obj1.free();
err1:
return;
}
SampledFunction::~SampledFunction() {
if (idxOffset) {
gfree(idxOffset);
}
if (samples) {
gfree(samples);
}
if (sBuf) {
gfree(sBuf);
}
}
SampledFunction::SampledFunction(SampledFunction *func) {
memcpy((void *)this, (void *)func, sizeof(SampledFunction));
idxOffset = (int *)gmallocn(1 << m, sizeof(int));
memcpy(idxOffset, func->idxOffset, (1 << m) * (int)sizeof(int));
samples = (double *)gmallocn(nSamples, sizeof(double));
memcpy(samples, func->samples, nSamples * sizeof(double));
sBuf = (double *)gmallocn(1 << m, sizeof(double));
}
void SampledFunction::transform(double *in, double *out) {
double x;
int e[funcMaxInputs];
double efrac0[funcMaxInputs];
double efrac1[funcMaxInputs];
int i, j, k, idx0, t;
// check the cache
for (i = 0; i < m; ++i) {
if (in[i] != cacheIn[i]) {
break;
}
}
if (i == m) {
for (i = 0; i < n; ++i) {
out[i] = cacheOut[i];
}
return;
}
// map input values into sample array
for (i = 0; i < m; ++i) {
x = (in[i] - domain[i][0]) * inputMul[i] + encode[i][0];
if (x < 0 || x != x) { // x!=x is a more portable version of isnan(x)
x = 0;
} else if (x > sampleSize[i] - 1) {
x = sampleSize[i] - 1;
}
e[i] = (int)x;
if (e[i] == sampleSize[i] - 1 && sampleSize[i] > 1) {
// this happens if in[i] = domain[i][1]
e[i] = sampleSize[i] - 2;
}
efrac1[i] = x - e[i];
efrac0[i] = 1 - efrac1[i];
}
// compute index for the first sample to be used
idx0 = 0;
for (k = m - 1; k >= 1; --k) {
idx0 = (idx0 + e[k]) * sampleSize[k-1];
}
idx0 = (idx0 + e[0]) * n;
// for each output, do m-linear interpolation
for (i = 0; i < n; ++i) {
// pull 2^m values out of the sample array
for (j = 0; j < (1<<m); ++j) {
sBuf[j] = samples[idx0 + idxOffset[j] + i];
}
// do m sets of interpolations
for (j = 0, t = (1<<m); j < m; ++j, t >>= 1) {
for (k = 0; k < t; k += 2) {
sBuf[k >> 1] = efrac0[j] * sBuf[k] + efrac1[j] * sBuf[k+1];
}
}
// map output value to range
out[i] = sBuf[0] * (decode[i][1] - decode[i][0]) + decode[i][0];
if (out[i] < range[i][0]) {
out[i] = range[i][0];
} else if (out[i] > range[i][1]) {
out[i] = range[i][1];
}
}
// save current result in the cache
for (i = 0; i < m; ++i) {
cacheIn[i] = in[i];
}
for (i = 0; i < n; ++i) {
cacheOut[i] = out[i];
}
}
//------------------------------------------------------------------------
// ExponentialFunction
//------------------------------------------------------------------------
ExponentialFunction::ExponentialFunction(Object *funcObj, Dict *dict) {
Object obj1, obj2;
int i;
ok = gFalse;
//----- initialize the generic stuff
if (!init(dict)) {
goto err1;
}
if (m != 1) {
error(errSyntaxError, -1, "Exponential function with more than one input");
goto err1;
}
//----- C0
if (dict->lookup("C0", &obj1)->isArray()) {
if (hasRange && obj1.arrayGetLength() != n) {
error(errSyntaxError, -1, "Function's C0 array is wrong length");
goto err2;
}
n = obj1.arrayGetLength();
if (n > funcMaxOutputs) {
error(errSyntaxError, -1,
"Functions with more than {0:d} outputs are unsupported",
funcMaxOutputs);
goto err2;
}
for (i = 0; i < n; ++i) {
obj1.arrayGet(i, &obj2);
if (!obj2.isNum()) {
error(errSyntaxError, -1, "Illegal value in function C0 array");
goto err3;
}
c0[i] = obj2.getNum();
obj2.free();
}
} else {
if (hasRange && n != 1) {
error(errSyntaxError, -1, "Function's C0 array is wrong length");
goto err2;
}
n = 1;
c0[0] = 0;
}
obj1.free();
//----- C1
if (dict->lookup("C1", &obj1)->isArray()) {
if (obj1.arrayGetLength() != n) {
error(errSyntaxError, -1, "Function's C1 array is wrong length");
goto err2;
}
for (i = 0; i < n; ++i) {
obj1.arrayGet(i, &obj2);
if (!obj2.isNum()) {
error(errSyntaxError, -1, "Illegal value in function C1 array");
goto err3;
}
c1[i] = obj2.getNum();
obj2.free();
}
} else {
if (n != 1) {
error(errSyntaxError, -1, "Function's C1 array is wrong length");
goto err2;
}
c1[0] = 1;
}
obj1.free();
//----- N (exponent)
if (!dict->lookup("N", &obj1)->isNum()) {
error(errSyntaxError, -1, "Function has missing or invalid N");
goto err2;
}
e = obj1.getNum();
obj1.free();
ok = gTrue;
return;
err3:
obj2.free();
err2:
obj1.free();
err1:
return;
}
ExponentialFunction::~ExponentialFunction() {
}
ExponentialFunction::ExponentialFunction(ExponentialFunction *func) {
memcpy((void *)this, (void *)func, sizeof(ExponentialFunction));
}
void ExponentialFunction::transform(double *in, double *out) {
double x;
int i;
if (in[0] < domain[0][0]) {
x = domain[0][0];
} else if (in[0] > domain[0][1]) {
x = domain[0][1];
} else {
x = in[0];
}
for (i = 0; i < n; ++i) {
out[i] = c0[i] + pow(x, e) * (c1[i] - c0[i]);
if (hasRange) {
if (out[i] < range[i][0]) {
out[i] = range[i][0];
} else if (out[i] > range[i][1]) {
out[i] = range[i][1];
}
}
}
return;
}
//------------------------------------------------------------------------
// StitchingFunction
//------------------------------------------------------------------------
StitchingFunction::StitchingFunction(Object *funcObj, Dict *dict,
int recursion) {
Object obj1, obj2;
int i;
ok = gFalse;
funcs = NULL;
bounds = NULL;
encode = NULL;
scale = NULL;
//----- initialize the generic stuff
if (!init(dict)) {
goto err1;
}
if (m != 1) {
error(errSyntaxError, -1, "Stitching function with more than one input");
goto err1;
}
//----- Functions
if (!dict->lookup("Functions", &obj1)->isArray()) {
error(errSyntaxError, -1,
"Missing 'Functions' entry in stitching function");
goto err1;
}
k = obj1.arrayGetLength();
funcs = (Function **)gmallocn(k, sizeof(Function *));
bounds = (double *)gmallocn(k + 1, sizeof(double));
encode = (double *)gmallocn(2 * k, sizeof(double));
scale = (double *)gmallocn(k, sizeof(double));
for (i = 0; i < k; ++i) {
funcs[i] = NULL;
}
for (i = 0; i < k; ++i) {
if (!(funcs[i] = Function::parse(obj1.arrayGet(i, &obj2),
recursion + 1))) {
goto err2;
}
if (funcs[i]->getInputSize() != 1 ||
(i > 0 && funcs[i]->getOutputSize() != funcs[0]->getOutputSize())) {
error(errSyntaxError, -1,
"Incompatible subfunctions in stitching function");
goto err2;
}
obj2.free();
}
obj1.free();
//----- Bounds
if (!dict->lookup("Bounds", &obj1)->isArray() ||
obj1.arrayGetLength() != k - 1) {
error(errSyntaxError, -1,
"Missing or invalid 'Bounds' entry in stitching function");
goto err1;
}
bounds[0] = domain[0][0];
for (i = 1; i < k; ++i) {
if (!obj1.arrayGet(i - 1, &obj2)->isNum()) {
error(errSyntaxError, -1,
"Invalid type in 'Bounds' array in stitching function");
goto err2;
}
bounds[i] = obj2.getNum();
obj2.free();
}
bounds[k] = domain[0][1];
obj1.free();
//----- Encode
if (!dict->lookup("Encode", &obj1)->isArray() ||
obj1.arrayGetLength() != 2 * k) {
error(errSyntaxError, -1,
"Missing or invalid 'Encode' entry in stitching function");
goto err1;
}
for (i = 0; i < 2 * k; ++i) {
if (!obj1.arrayGet(i, &obj2)->isNum()) {
error(errSyntaxError, -1,
"Invalid type in 'Encode' array in stitching function");
goto err2;
}
encode[i] = obj2.getNum();
obj2.free();
}
obj1.free();
//----- pre-compute the scale factors
for (i = 0; i < k; ++i) {
if (bounds[i] == bounds[i+1]) {
// avoid a divide-by-zero -- in this situation, function i will
// never be used anyway
scale[i] = 0;
} else {
scale[i] = (encode[2*i+1] - encode[2*i]) / (bounds[i+1] - bounds[i]);
}
}
ok = gTrue;
return;
err2:
obj2.free();
err1:
obj1.free();
}
StitchingFunction::StitchingFunction(StitchingFunction *func) {
int i;
memcpy((void *)this, (void *)func, sizeof(StitchingFunction));
funcs = (Function **)gmallocn(k, sizeof(Function *));
for (i = 0; i < k; ++i) {
funcs[i] = func->funcs[i]->copy();
}
bounds = (double *)gmallocn(k + 1, sizeof(double));
memcpy(bounds, func->bounds, (k + 1) * sizeof(double));
encode = (double *)gmallocn(2 * k, sizeof(double));
memcpy(encode, func->encode, 2 * k * sizeof(double));
scale = (double *)gmallocn(k, sizeof(double));
memcpy(scale, func->scale, k * sizeof(double));
ok = gTrue;
}
StitchingFunction::~StitchingFunction() {
int i;
if (funcs) {
for (i = 0; i < k; ++i) {
if (funcs[i]) {
delete funcs[i];
}
}
}
gfree(funcs);
gfree(bounds);
gfree(encode);
gfree(scale);
}
void StitchingFunction::transform(double *in, double *out) {
double x;
int i;
if (in[0] < domain[0][0]) {
x = domain[0][0];
} else if (in[0] > domain[0][1]) {
x = domain[0][1];
} else {
x = in[0];
}
for (i = 0; i < k - 1; ++i) {
if (x < bounds[i+1]) {
break;
}
}
x = encode[2*i] + (x - bounds[i]) * scale[i];
funcs[i]->transform(&x, out);
}
//------------------------------------------------------------------------
// PostScriptFunction
//------------------------------------------------------------------------
// This is not an enum, because we can't foreward-declare the enum
// type in Function.h
//
// NB: This must be kept in sync with psOpNames[] below.
#define psOpAbs 0
#define psOpAdd 1
#define psOpAnd 2
#define psOpAtan 3
#define psOpBitshift 4
#define psOpCeiling 5
#define psOpCopy 6
#define psOpCos 7
#define psOpCvi 8
#define psOpCvr 9
#define psOpDiv 10
#define psOpDup 11
#define psOpEq 12
#define psOpExch 13
#define psOpExp 14
#define psOpFalse 15
#define psOpFloor 16
#define psOpGe 17
#define psOpGt 18
#define psOpIdiv 19
#define psOpIndex 20
#define psOpLe 21
#define psOpLn 22
#define psOpLog 23
#define psOpLt 24
#define psOpMod 25
#define psOpMul 26
#define psOpNe 27
#define psOpNeg 28
#define psOpNot 29
#define psOpOr 30
#define psOpPop 31
#define psOpRoll 32
#define psOpRound 33
#define psOpSin 34
#define psOpSqrt 35
#define psOpSub 36
#define psOpTrue 37
#define psOpTruncate 38
#define psOpXor 39
// the push/j/jz ops are used internally (and are not listed in psOpNames[])
#define psOpPush 40
#define psOpJ 41
#define psOpJz 42
#define nPSOps (sizeof(psOpNames) / sizeof(const char *))
// Note: 'if' and 'ifelse' are parsed separately.
// The rest are listed here in alphabetical order.
//
// NB: This must be kept in sync with the psOpXXX defines above.
static const char *psOpNames[] = {
"abs",
"add",
"and",
"atan",
"bitshift",
"ceiling",
"copy",
"cos",
"cvi",
"cvr",
"div",
"dup",
"eq",
"exch",
"exp",
"false",
"floor",
"ge",
"gt",
"idiv",
"index",
"le",
"ln",
"log",
"lt",
"mod",
"mul",
"ne",
"neg",
"not",
"or",
"pop",
"roll",
"round",
"sin",
"sqrt",
"sub",
"true",
"truncate",
"xor"
};
struct PSCode {
int op;
union {
double d;
int i;
} val;
};
#define psStackSize 100
PostScriptFunction::PostScriptFunction(Object *funcObj, Dict *dict) {
Stream *str;
GList *tokens;
GString *tok;
double in[funcMaxInputs];
int tokPtr, codePtr, i;
codeString = NULL;
code = NULL;
codeSize = 0;
ok = gFalse;
//----- initialize the generic stuff
if (!init(dict)) {
goto err1;
}
if (!hasRange) {
error(errSyntaxError, -1, "Type 4 function is missing range");
goto err1;
}
//----- get the stream
if (!funcObj->isStream()) {
error(errSyntaxError, -1, "Type 4 function isn't a stream");
goto err1;
}
str = funcObj->getStream();
//----- tokenize the function
codeString = new GString();
tokens = new GList();
str->reset();
while ((tok = getToken(str))) {
tokens->append(tok);
}
str->close();
//----- parse the function
if (tokens->getLength() < 1 ||
((GString *)tokens->get(0))->cmp("{")) {
error(errSyntaxError, -1, "Expected '{{' at start of PostScript function");
goto err2;
}
tokPtr = 1;
codePtr = 0;
if (!parseCode(tokens, &tokPtr, &codePtr)) {
goto err2;
}
codeLen = codePtr;
//----- set up the cache
for (i = 0; i < m; ++i) {
in[i] = domain[i][0];
cacheIn[i] = in[i] - 1;
}
transform(in, cacheOut);
ok = gTrue;
err2:
deleteGList(tokens, GString);
err1:
return;
}
PostScriptFunction::PostScriptFunction(PostScriptFunction *func) {
memcpy((void *)this, (void *)func, sizeof(PostScriptFunction));
codeString = func->codeString->copy();
code = (PSCode *)gmallocn(codeSize, sizeof(PSCode));
memcpy(code, func->code, codeSize * sizeof(PSCode));
}
PostScriptFunction::~PostScriptFunction() {
gfree(code);
if (codeString) {
delete codeString;
}
}
void PostScriptFunction::transform(double *in, double *out) {
double stack[psStackSize];
double x;
int sp, i;
// check the cache
for (i = 0; i < m; ++i) {
if (in[i] != cacheIn[i]) {
break;
}
}
if (i == m) {
for (i = 0; i < n; ++i) {
out[i] = cacheOut[i];
}
return;
}
for (i = 0; i < m; ++i) {
stack[psStackSize - 1 - i] = in[i];
}
sp = exec(stack, psStackSize - m);
// if (sp < psStackSize - n) {
// error(errSyntaxWarning, -1,
// "Extra values on stack at end of PostScript function");
// }
if (sp > psStackSize - n) {
error(errSyntaxError, -1, "Stack underflow in PostScript function");
sp = psStackSize - n;
}
for (i = 0; i < n; ++i) {
x = stack[sp + n - 1 - i];
if (x < range[i][0]) {
out[i] = range[i][0];
} else if (x > range[i][1]) {
out[i] = range[i][1];
} else {
out[i] = x;
}
}
// save current result in the cache
for (i = 0; i < m; ++i) {
cacheIn[i] = in[i];
}
for (i = 0; i < n; ++i) {
cacheOut[i] = out[i];
}
}
GBool PostScriptFunction::parseCode(GList *tokens, int *tokPtr, int *codePtr) {
GString *tok;
char *p;
int a, b, mid, cmp;
int codePtr0, codePtr1;
while (1) {
if (*tokPtr >= tokens->getLength()) {
error(errSyntaxError, -1,
"Unexpected end of PostScript function stream");
return gFalse;
}
tok = (GString *)tokens->get((*tokPtr)++);
p = tok->getCString();
if (isdigit(*p) || *p == '.' || *p == '-') {
addCodeD(codePtr, psOpPush, atof(tok->getCString()));
} else if (!tok->cmp("{")) {
codePtr0 = *codePtr;
addCodeI(codePtr, psOpJz, 0);
if (!parseCode(tokens, tokPtr, codePtr)) {
return gFalse;
}
if (*tokPtr >= tokens->getLength()) {
error(errSyntaxError, -1,
"Unexpected end of PostScript function stream");
return gFalse;
}
tok = (GString *)tokens->get((*tokPtr)++);
if (!tok->cmp("if")) {
code[codePtr0].val.i = *codePtr;
} else if (!tok->cmp("{")) {
codePtr1 = *codePtr;
addCodeI(codePtr, psOpJ, 0);
code[codePtr0].val.i = *codePtr;
if (!parseCode(tokens, tokPtr, codePtr)) {
return gFalse;
}
if (*tokPtr >= tokens->getLength()) {
error(errSyntaxError, -1,
"Unexpected end of PostScript function stream");
return gFalse;
}
tok = (GString *)tokens->get((*tokPtr)++);
if (!tok->cmp("ifelse")) {
code[codePtr1].val.i = *codePtr;
} else {
error(errSyntaxError, -1,
"Expected 'ifelse' in PostScript function stream");
return gFalse;
}
} else {
error(errSyntaxError, -1,
"Expected 'if' in PostScript function stream");
return gFalse;
}
} else if (!tok->cmp("}")) {
break;
} else if (!tok->cmp("if")) {
error(errSyntaxError, -1,
"Unexpected 'if' in PostScript function stream");
return gFalse;
} else if (!tok->cmp("ifelse")) {
error(errSyntaxError, -1,
"Unexpected 'ifelse' in PostScript function stream");
return gFalse;
} else {
a = -1;
b = nPSOps;
cmp = 0; // make gcc happy
// invariant: psOpNames[a] < tok < psOpNames[b]
while (b - a > 1) {
mid = (a + b) / 2;
cmp = tok->cmp(psOpNames[mid]);
if (cmp > 0) {
a = mid;
} else if (cmp < 0) {
b = mid;
} else {
a = b = mid;
}
}
if (cmp != 0) {
error(errSyntaxError, -1,
"Unknown operator '{0:t}' in PostScript function",
tok);
return gFalse;
}
addCode(codePtr, a);
}
}
return gTrue;
}
void PostScriptFunction::addCode(int *codePtr, int op) {
if (*codePtr >= codeSize) {
if (codeSize) {
codeSize *= 2;
} else {
codeSize = 16;
}
code = (PSCode *)greallocn(code, codeSize, sizeof(PSCode));
}
code[*codePtr].op = op;
++(*codePtr);
}
void PostScriptFunction::addCodeI(int *codePtr, int op, int x) {
if (*codePtr >= codeSize) {
if (codeSize) {
codeSize *= 2;
} else {
codeSize = 16;
}
code = (PSCode *)greallocn(code, codeSize, sizeof(PSCode));
}
code[*codePtr].op = op;
code[*codePtr].val.i = x;
++(*codePtr);
}
void PostScriptFunction::addCodeD(int *codePtr, int op, double x) {
if (*codePtr >= codeSize) {
if (codeSize) {
codeSize *= 2;
} else {
codeSize = 16;
}
code = (PSCode *)greallocn(code, codeSize, sizeof(PSCode));
}
code[*codePtr].op = op;
code[*codePtr].val.d = x;
++(*codePtr);
}
GString *PostScriptFunction::getToken(Stream *str) {
GString *s;
int c;
GBool comment;
s = new GString();
comment = gFalse;
while (1) {
if ((c = str->getChar()) == EOF) {
delete s;
return NULL;
}
codeString->append((char)c);
if (comment) {
if (c == '\x0a' || c == '\x0d') {
comment = gFalse;
}
} else if (c == '%') {
comment = gTrue;
} else if (!isspace(c)) {
break;
}
}
if (c == '{' || c == '}') {
s->append((char)c);
} else if (isdigit(c) || c == '.' || c == '-') {
while (1) {
s->append((char)c);
c = str->lookChar();
if (c == EOF || !(isdigit(c) || c == '.' || c == '-')) {
break;
}
str->getChar();
codeString->append((char)c);
}
} else {
while (1) {
s->append((char)c);
c = str->lookChar();
if (c == EOF || !isalnum(c)) {
break;
}
str->getChar();
codeString->append((char)c);
}
}
return s;
}
int PostScriptFunction::exec(double *stack, int sp0) {
PSCode *c;
double tmp[psStackSize];
double t;
int sp, ip, nn, k, i;
sp = sp0;
ip = 0;
while (ip < codeLen) {
c = &code[ip++];
switch(c->op) {
case psOpAbs:
if (sp >= psStackSize) {
goto underflow;
}
stack[sp] = fabs(stack[sp]);
break;
case psOpAdd:
if (sp + 1 >= psStackSize) {
goto underflow;
}
stack[sp + 1] = stack[sp + 1] + stack[sp];
++sp;
break;
case psOpAnd:
if (sp + 1 >= psStackSize) {
goto underflow;
}
stack[sp + 1] = (int)stack[sp + 1] & (int)stack[sp];
++sp;
break;
case psOpAtan:
if (sp + 1 >= psStackSize) {
goto underflow;
}
stack[sp + 1] = atan2(stack[sp + 1], stack[sp]);
++sp;
break;
case psOpBitshift:
if (sp + 1 >= psStackSize) {
goto underflow;
}
k = (int)stack[sp + 1];
nn = (int)stack[sp];
if (nn > 0) {
stack[sp + 1] = k << nn;
} else if (nn < 0) {
stack[sp + 1] = k >> -nn;
} else {
stack[sp + 1] = k;
}
++sp;
break;
case psOpCeiling:
if (sp >= psStackSize) {
goto underflow;
}
stack[sp] = ceil(stack[sp]);
break;
case psOpCopy:
if (sp >= psStackSize) {
goto underflow;
}
nn = (int)stack[sp++];
if (nn < 0) {
goto invalidArg;
}
if (sp + nn > psStackSize) {
goto underflow;
}
if (sp - nn < 0) {
goto overflow;
}
for (i = 0; i < nn; ++i) {
stack[sp - nn + i] = stack[sp + i];
}
sp -= nn;
break;
case psOpCos:
if (sp >= psStackSize) {
goto underflow;
}
stack[sp] = cos(stack[sp]);
break;
case psOpCvi:
if (sp >= psStackSize) {
goto underflow;
}
stack[sp] = (int)stack[sp];
break;
case psOpCvr:
if (sp >= psStackSize) {
goto underflow;
}
break;
case psOpDiv:
if (sp + 1 >= psStackSize) {
goto underflow;
}
stack[sp + 1] = stack[sp + 1] / stack[sp];
++sp;
break;
case psOpDup:
if (sp >= psStackSize) {
goto underflow;
}
if (sp < 1) {
goto overflow;
}
stack[sp - 1] = stack[sp];
--sp;
break;
case psOpEq:
if (sp + 1 >= psStackSize) {
goto underflow;
}
stack[sp + 1] = stack[sp + 1] == stack[sp] ? 1 : 0;
++sp;
break;
case psOpExch:
if (sp + 1 >= psStackSize) {
goto underflow;
}
t = stack[sp];
stack[sp] = stack[sp + 1];
stack[sp + 1] = t;
break;
case psOpExp:
if (sp + 1 >= psStackSize) {
goto underflow;
}
stack[sp + 1] = pow(stack[sp + 1], stack[sp]);
++sp;
break;
case psOpFalse:
if (sp < 1) {
goto overflow;
}
stack[sp - 1] = 0;
--sp;
break;
case psOpFloor:
if (sp >= psStackSize) {
goto underflow;
}
stack[sp] = floor(stack[sp]);
break;
case psOpGe:
if (sp + 1 >= psStackSize) {
goto underflow;
}
stack[sp + 1] = stack[sp + 1] >= stack[sp] ? 1 : 0;
++sp;
break;
case psOpGt:
if (sp + 1 >= psStackSize) {
goto underflow;
}
stack[sp + 1] = stack[sp + 1] > stack[sp] ? 1 : 0;
++sp;
break;
case psOpIdiv:
if (sp + 1 >= psStackSize) {
goto underflow;
}
stack[sp + 1] = (int)stack[sp + 1] / (int)stack[sp];
++sp;
break;
case psOpIndex:
if (sp >= psStackSize) {
goto underflow;
}
k = (int)stack[sp];
if (k < 0) {
goto invalidArg;
}
if (sp + 1 + k >= psStackSize) {
goto underflow;
}
stack[sp] = stack[sp + 1 + k];
break;
case psOpLe:
if (sp + 1 >= psStackSize) {
goto underflow;
}
stack[sp + 1] = stack[sp + 1] <= stack[sp] ? 1 : 0;
++sp;
break;
case psOpLn:
if (sp >= psStackSize) {
goto underflow;
}
stack[sp] = log(stack[sp]);
break;
case psOpLog:
if (sp >= psStackSize) {
goto underflow;
}
stack[sp] = log10(stack[sp]);
break;
case psOpLt:
if (sp + 1 >= psStackSize) {
goto underflow;
}
stack[sp + 1] = stack[sp + 1] < stack[sp] ? 1 : 0;
++sp;
break;
case psOpMod:
if (sp + 1 >= psStackSize) {
goto underflow;
}
stack[sp + 1] = (int)stack[sp + 1] % (int)stack[sp];
++sp;
break;
case psOpMul:
if (sp + 1 >= psStackSize) {
goto underflow;
}
stack[sp + 1] = stack[sp + 1] * stack[sp];
++sp;
break;
case psOpNe:
if (sp + 1 >= psStackSize) {
goto underflow;
}
stack[sp + 1] = stack[sp + 1] != stack[sp] ? 1 : 0;
++sp;
break;
case psOpNeg:
if (sp >= psStackSize) {
goto underflow;
}
stack[sp] = -stack[sp];
break;
case psOpNot:
if (sp >= psStackSize) {
goto underflow;
}
stack[sp] = stack[sp] == 0 ? 1 : 0;
break;
case psOpOr:
if (sp + 1 >= psStackSize) {
goto underflow;
}
stack[sp + 1] = (int)stack[sp + 1] | (int)stack[sp];
++sp;
break;
case psOpPop:
if (sp >= psStackSize) {
goto underflow;
}
++sp;
break;
case psOpRoll:
if (sp + 1 >= psStackSize) {
goto underflow;
}
k = (int)stack[sp++];
nn = (int)stack[sp++];
if (nn < 0) {
goto invalidArg;
}
if (sp + nn > psStackSize) {
goto underflow;
}
if (k >= 0) {
k %= nn;
} else {
k = -k % nn;
if (k) {
k = nn - k;
}
}
for (i = 0; i < nn; ++i) {
tmp[i] = stack[sp + i];
}
for (i = 0; i < nn; ++i) {
stack[sp + i] = tmp[(i + k) % nn];
}
break;
case psOpRound:
if (sp >= psStackSize) {
goto underflow;
}
t = stack[sp];
stack[sp] = (t >= 0) ? floor(t + 0.5) : ceil(t - 0.5);
break;
case psOpSin:
if (sp >= psStackSize) {
goto underflow;
}
stack[sp] = sin(stack[sp]);
break;
case psOpSqrt:
if (sp >= psStackSize) {
goto underflow;
}
stack[sp] = sqrt(stack[sp]);
break;
case psOpSub:
if (sp + 1 >= psStackSize) {
goto underflow;
}
stack[sp + 1] = stack[sp + 1] - stack[sp];
++sp;
break;
case psOpTrue:
if (sp < 1) {
goto overflow;
}
stack[sp - 1] = 1;
--sp;
break;
case psOpTruncate:
if (sp >= psStackSize) {
goto underflow;
}
t = stack[sp];
stack[sp] = (t >= 0) ? floor(t) : ceil(t);
break;
case psOpXor:
if (sp + 1 >= psStackSize) {
goto underflow;
}
stack[sp + 1] = (int)stack[sp + 1] ^ (int)stack[sp];
++sp;
break;
case psOpPush:
if (sp < 1) {
goto overflow;
}
stack[--sp] = c->val.d;
break;
case psOpJ:
ip = c->val.i;
break;
case psOpJz:
if (sp >= psStackSize) {
goto underflow;
}
k = (int)stack[sp++];
if (k == 0) {
ip = c->val.i;
}
break;
}
}
return sp;
underflow:
error(errSyntaxError, -1, "Stack underflow in PostScript function");
return sp;
overflow:
error(errSyntaxError, -1, "Stack overflow in PostScript function");
return sp;
invalidArg:
error(errSyntaxError, -1, "Invalid arg in PostScript function");
return sp;
}