Coverage Report

Created: 2025-06-24 07:01

/src/ghostpdl/psi/zfunc.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2023 Artifex Software, Inc.
2
   All Rights Reserved.
3
4
   This software is provided AS-IS with no warranty, either express or
5
   implied.
6
7
   This software is distributed under license and may not be copied,
8
   modified or distributed except as expressly authorized under the terms
9
   of the license contained in the file LICENSE in this distribution.
10
11
   Refer to licensing information at http://www.artifex.com or contact
12
   Artifex Software, Inc.,  39 Mesa Street, Suite 108A, San Francisco,
13
   CA 94129, USA, for further information.
14
*/
15
16
17
/* Generic PostScript language interface to Functions */
18
#include "memory_.h"
19
#include "ghost.h"
20
#include "oper.h"
21
#include "gscdefs.h"
22
#include "gsfunc.h"
23
#include "gsstruct.h"
24
#include "ialloc.h"
25
#include "idict.h"
26
#include "idparam.h"
27
#include "ifunc.h"
28
#include "store.h"
29
#include "zfunc.h"
30
31
/* Define the maximum depth of nesting of subsidiary functions. */
32
0
#define MAX_SUB_FUNCTION_DEPTH 3
33
34
/* ------ Operators ------ */
35
36
/* Create a function procedure from a function structure. */
37
static int
38
make_function_proc(i_ctx_t *i_ctx_p, ref *op, gs_function_t *pfn)
39
5.63k
{
40
5.63k
    ref cref;     /* closure */
41
5.63k
    int code;
42
43
5.63k
    code = ialloc_ref_array(&cref, a_executable | a_execute, 2,
44
5.63k
                            ".buildfunction");
45
5.63k
    if (code < 0)
46
0
        return code;
47
5.63k
    make_istruct_new(cref.value.refs, a_executable | a_execute, pfn);
48
5.63k
    make_oper_new(cref.value.refs + 1, 0, zexecfunction);
49
5.63k
    ref_assign(op, &cref);
50
5.63k
    return 0;
51
5.63k
}
52
53
/* <dict> .buildfunction <function_proc> */
54
static int
55
zbuildfunction(i_ctx_t *i_ctx_p)
56
0
{
57
0
    os_ptr op = osp;
58
0
    gs_function_t *pfn;
59
0
    int code;
60
61
0
    check_op(1);
62
0
    code = fn_build_function(i_ctx_p, op, &pfn, imemory, 0, 0);
63
64
0
    if (code < 0)
65
0
        return code;
66
0
    code = make_function_proc(i_ctx_p, op, pfn);
67
0
    if (code < 0)
68
0
        gs_function_free(pfn, true, imemory);
69
0
    return 0;
70
0
}
71
72
int buildfunction(i_ctx_t * i_ctx_p, ref *arr, ref *pproc, int type)
73
6.12k
{
74
6.12k
    os_ptr op = osp;
75
6.12k
    gs_function_t *pfn=NULL;
76
6.12k
    int code=0;
77
78
6.12k
    switch(type) {
79
245
        case 0:
80
245
            code = make_sampled_function(i_ctx_p, arr, pproc, &pfn);
81
245
            break;
82
5.88k
        case 4:
83
5.88k
            code = make_type4_function(i_ctx_p, arr, pproc, &pfn);
84
5.88k
            if (code == 0) {
85
5.63k
                code = make_function_proc(i_ctx_p, op, pfn);
86
5.63k
                if (code < 0) {
87
0
                    gs_function_free(pfn, true, imemory);
88
0
                }
89
5.63k
            }
90
5.88k
            break;
91
6.12k
    }
92
6.12k
    return code;
93
6.12k
}
94
95
/* <in1> ... <function_struct> %execfunction <out1> ... */
96
int
97
zexecfunction(i_ctx_t *i_ctx_p)
98
0
{
99
0
    os_ptr op = osp;
100
101
        /*
102
         * Since this operator's name begins with %, the name is not defined
103
         * in systemdict.  The only place this operator can ever appear is
104
         * in the execute-only closure created by .buildfunction.
105
         * Therefore, in principle it is unnecessary to check the argument.
106
         * However, we do a little checking anyway just on general
107
         * principles.  Note that since the argument may be an instance of
108
         * any subclass of gs_function_t, we currently have no way to check
109
         * its type.
110
         */
111
0
    if (!r_is_struct(op) ||
112
0
        !r_has_masked_attrs(op, a_executable | a_execute, a_executable | a_all)
113
0
        )
114
0
        return_error(gs_error_typecheck);
115
0
    {
116
0
        gs_function_t *pfn = (gs_function_t *) op->value.pstruct;
117
0
        int m = pfn->params.m, n = pfn->params.n;
118
0
        int diff = n - (m + 1);
119
120
0
        if (diff > 0)
121
0
            check_ostack(diff);
122
0
        {
123
0
            float params[20]; /* arbitrary size, just to avoid allocs */
124
0
            float *in;
125
0
            float *out;
126
0
            int code = 0;
127
128
0
            if (m + n <= countof(params)) {
129
0
                in = params;
130
0
            } else {
131
0
                in = (float *)ialloc_byte_array(m + n, sizeof(float),
132
0
                                                "%execfunction(in/out)");
133
0
                if (in == 0)
134
0
                    code = gs_note_error(gs_error_VMerror);
135
0
            }
136
0
            out = in + m;
137
0
            if (code < 0 ||
138
0
                (code = float_params(op - 1, m, in)) < 0 ||
139
0
                (code = gs_function_evaluate(pfn, in, out)) < 0
140
0
                )
141
0
                DO_NOTHING;
142
0
            else {
143
0
                if (diff > 0)
144
0
                    push(diff); /* can't fail */
145
0
                else if (diff < 0) {
146
0
                    ref_stack_pop(&o_stack, -diff);
147
0
                    op = osp;
148
0
                }
149
0
                code = make_floats(op + 1 - n, out, n);
150
0
            }
151
0
            if (in != params)
152
0
                ifree_object(in, "%execfunction(in)");
153
0
            return code;
154
0
        }
155
0
    }
156
0
}
157
158
/*
159
 * <proc> .isencapfunction <bool>
160
 *
161
 * This routine checks if a given Postscript procedure is an "encapsulated"
162
 * function of the type made by .buildfunction.  These functions can then
163
 * be executed without executing the interpreter.  These functions can be
164
 * executed directly from within C code inside the graphics library.
165
 */
166
static int
167
zisencapfunction(i_ctx_t *i_ctx_p)
168
0
{
169
0
    os_ptr op = osp;
170
0
    gs_function_t *pfn;
171
172
0
    check_op(1);
173
0
    check_proc(*op);
174
0
    pfn = ref_function(op);
175
0
    make_bool(op, pfn != NULL);
176
0
    return 0;
177
0
}
178
179
/* ------ Procedures ------ */
180
181
/* Build a function structure from a PostScript dictionary. */
182
int
183
fn_build_function(i_ctx_t *i_ctx_p, const ref * op, gs_function_t ** ppfn, gs_memory_t *mem,
184
    const float *shading_domain, const int num_inputs)
185
0
{
186
0
    return fn_build_sub_function(i_ctx_p, op, ppfn, 0, mem, shading_domain, num_inputs);
187
0
}
188
int
189
fn_build_sub_function(i_ctx_t *i_ctx_p, const ref * op, gs_function_t ** ppfn,
190
    int depth, gs_memory_t *mem, const float *shading_domain, const int num_inputs)
191
0
{
192
0
    int j, code, type;
193
0
    uint i;
194
0
    gs_function_params_t params;
195
196
0
    if (depth > MAX_SUB_FUNCTION_DEPTH)
197
0
        return_error(gs_error_limitcheck);
198
0
    check_type(*op, t_dictionary);
199
0
    code = dict_int_param(op, "FunctionType", 0, max_int, -1, &type);
200
0
    if (code < 0)
201
0
        return code;
202
0
    for (i = 0; i < build_function_type_table_count; ++i)
203
0
        if (build_function_type_table[i].type == type)
204
0
            break;
205
0
    if (i == build_function_type_table_count)
206
0
        return_error(gs_error_rangecheck);
207
    /* Collect parameters common to all function types. */
208
0
    params.Domain = 0;
209
0
    params.Range = 0;
210
0
    code = fn_build_float_array(op, "Domain", true, true, &params.Domain, mem);
211
0
    if (code < 0) {
212
0
        gs_errorinfo_put_pair_from_dict(i_ctx_p, op, "Domain");
213
0
        goto fail;
214
0
    }
215
0
    params.m = code >> 1;
216
0
    for (j = 0; j < params.m << 1; j += 2) {
217
0
        if (params.Domain[j] > params.Domain[j + 1]) {
218
0
          code = gs_note_error(gs_error_rangecheck);
219
0
          gs_errorinfo_put_pair_from_dict(i_ctx_p, op, "Domain");
220
0
          goto fail;
221
0
        }
222
0
    }
223
0
    if (shading_domain) {
224
        /* Each function dictionary's domain must be a superset of that of
225
         * the shading dictionary. PLRM3 p.265. CET 12-14c. We do this check
226
         * here because Adobe checks Domain before checking other parameters.
227
         */
228
0
        if (num_inputs != params.m)
229
0
            code = gs_note_error(gs_error_rangecheck);
230
0
        for (j = 0; j < 2*num_inputs && code >= 0; j += 2) {
231
0
            if (params.Domain[j] > shading_domain[j] ||
232
0
                params.Domain[j+1] < shading_domain[j+1]
233
0
               ) {
234
0
                code = gs_note_error(gs_error_rangecheck);
235
0
            }
236
0
        }
237
0
        if (code < 0) {
238
0
            gs_errorinfo_put_pair_from_dict(i_ctx_p, op, "Domain");
239
0
            goto fail;
240
0
        }
241
0
    }
242
0
    code = fn_build_float_array(op, "Range", false, true, &params.Range, mem);
243
0
    if (code < 0)
244
0
        goto fail;
245
0
    params.n = code >> 1;
246
    /* Finish building the function. */
247
    /* If this fails, it will free all the parameters. */
248
0
    return (*build_function_type_table[i].proc)
249
0
        (i_ctx_p, op, &params, depth + 1, ppfn, mem);
250
0
fail:
251
0
    gs_free_const_object(mem, params.Range, "Range");
252
0
    gs_free_const_object(mem, params.Domain, "Domain");
253
0
    return code;
254
0
}
255
256
/*
257
 * Collect a heap-allocated array of floats.  If the key is missing, set
258
 * *pparray = 0 and return 0; otherwise set *pparray and return the number
259
 * of elements.  Note that 0-length arrays are acceptable, so if the value
260
 * returned is 0, the caller must check whether *pparray == 0.
261
 */
262
int
263
fn_build_float_array(const ref * op, const char *kstr, bool required,
264
                     bool even, const float **pparray, gs_memory_t *mem)
265
0
{
266
0
    ref *par;
267
0
    int code;
268
269
0
    *pparray = 0;
270
0
    if (dict_find_string(op, kstr, &par) <= 0)
271
0
        return (required ? gs_note_error(gs_error_rangecheck) : 0);
272
0
    if (!r_is_array(par))
273
0
        return_error(gs_error_typecheck);
274
0
    {
275
0
        uint size = r_size(par);
276
0
        float *ptr = (float *)
277
0
            gs_alloc_byte_array(mem, size, sizeof(float), kstr);
278
279
0
        if (ptr == 0)
280
0
            return_error(gs_error_VMerror);
281
0
        code = dict_float_array_check_param(mem, op, kstr, size,
282
0
                                            ptr, NULL,
283
0
                                            0, gs_error_rangecheck);
284
0
        if (code < 0 || (even && (code & 1) != 0)) {
285
0
            gs_free_object(mem, ptr, kstr);
286
0
            return(code < 0 ? code : gs_note_error(gs_error_rangecheck));
287
0
        }
288
0
        *pparray = ptr;
289
0
    }
290
0
    return code;
291
0
}
292
293
/*
294
 * Similar to fn_build_float_array() except
295
 * - numeric parameter is accepted and converted to 1-element array
296
 * - number of elements is not checked for even/odd
297
 */
298
int
299
fn_build_float_array_forced(const ref * op, const char *kstr, bool required,
300
                     const float **pparray, gs_memory_t *mem)
301
0
{
302
0
    ref *par;
303
0
    int code;
304
0
    uint size;
305
0
    float *ptr;
306
307
0
    *pparray = 0;
308
0
    if (dict_find_string(op, kstr, &par) <= 0)
309
0
        return (required ? gs_note_error(gs_error_rangecheck) : 0);
310
311
0
    if( r_is_array(par) )
312
0
        size = r_size(par);
313
0
    else if(r_is_number(par))
314
0
        size = 1;
315
0
    else
316
0
        return_error(gs_error_typecheck);
317
0
    ptr = (float *)gs_alloc_byte_array(mem, size, sizeof(float), kstr);
318
319
0
    if (ptr == 0)
320
0
        return_error(gs_error_VMerror);
321
0
    if(r_is_array(par) )
322
0
        code = dict_float_array_check_param(mem, op, kstr,
323
0
                                            size, ptr, NULL,
324
0
                                            0, gs_error_rangecheck);
325
0
    else {
326
0
        code = dict_float_param(op, kstr, 0., ptr); /* defailt cannot happen */
327
0
        if( code == 0 )
328
0
            code = 1;
329
0
    }
330
331
0
    if (code < 0 ) {
332
0
        gs_free_object(mem, ptr, kstr);
333
0
        return code;
334
0
    }
335
0
    *pparray = ptr;
336
0
    return code;
337
0
}
338
339
/*
340
 * If a PostScript object is a Function procedure, return the function
341
 * object, otherwise return 0.
342
 */
343
gs_function_t *
344
ref_function(const ref *op)
345
11.6k
{
346
11.6k
    if (r_has_type(op, t_array) &&
347
11.6k
        r_has_masked_attrs(op, a_executable | a_execute,
348
11.6k
                           a_executable | a_all) &&
349
11.6k
        r_size(op) == 2 &&
350
11.6k
        r_has_type_attrs(op->value.refs + 1, t_operator, a_executable) &&
351
11.6k
        op->value.refs[1].value.opproc == zexecfunction &&
352
11.6k
        r_is_struct(op->value.refs) &&
353
11.6k
        r_has_masked_attrs(op->value.refs, a_executable | a_execute,
354
11.6k
                           a_executable | a_all)
355
11.6k
        )
356
5.80k
        return (gs_function_t *)op->value.refs->value.pstruct;
357
5.88k
    return 0;
358
11.6k
}
359
360
/* ------ Initialization procedure ------ */
361
362
const op_def zfunc_op_defs[] =
363
{
364
    {"1.buildfunction", zbuildfunction},
365
    {"1%execfunction", zexecfunction},
366
    {"1.isencapfunction", zisencapfunction},
367
    op_def_end(0)
368
};