/src/ghostpdl/psi/zstack.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 | | /* Operand stack operators */ |
18 | | #include "memory_.h" |
19 | | #include "ghost.h" |
20 | | #include "ialloc.h" |
21 | | #include "istack.h" |
22 | | #include "oper.h" |
23 | | #include "store.h" |
24 | | |
25 | | /* <obj> pop - */ |
26 | | int |
27 | | zpop(i_ctx_t *i_ctx_p) |
28 | 9.76k | { |
29 | 9.76k | os_ptr op = osp; |
30 | | |
31 | 9.76k | check_op(1); |
32 | 9.76k | pop(1); |
33 | 9.76k | return 0; |
34 | 9.76k | } |
35 | | |
36 | | /* <obj1> <obj2> exch <obj2> <obj1> */ |
37 | | int |
38 | | zexch(i_ctx_t *i_ctx_p) |
39 | 167 | { |
40 | 167 | os_ptr op = osp; |
41 | 167 | ref next; |
42 | | |
43 | 167 | check_op(2); |
44 | 167 | ref_assign_inline(&next, op - 1); |
45 | 167 | ref_assign_inline(op - 1, op); |
46 | 167 | ref_assign_inline(op, &next); |
47 | 167 | return 0; |
48 | 167 | } |
49 | | |
50 | | /* <obj> dup <obj> <obj> */ |
51 | | int |
52 | | zdup(i_ctx_t *i_ctx_p) |
53 | 4 | { |
54 | 4 | os_ptr op = osp; |
55 | | |
56 | 4 | check_op(1); |
57 | 4 | push(1); |
58 | 4 | ref_assign_inline(op, op - 1); |
59 | 4 | return 0; |
60 | 4 | } |
61 | | |
62 | | /* <obj_n> ... <obj_0> <n> index <obj_n> ... <obj_0> <obj_n> */ |
63 | | int |
64 | | zindex(i_ctx_t *i_ctx_p) |
65 | 174M | { |
66 | 174M | os_ptr op = osp; |
67 | 174M | register os_ptr opn; |
68 | | |
69 | 174M | check_op(1); |
70 | 174M | check_type(*op, t_integer); |
71 | 174M | if ((ulong)op->value.intval >= (ulong)(op - osbot)) { |
72 | | /* Might be in an older stack block. */ |
73 | 10 | ref *elt; |
74 | | |
75 | 10 | if (op->value.intval < 0) |
76 | 1 | return_error(gs_error_rangecheck); |
77 | 9 | elt = ref_stack_index(&o_stack, op->value.intval + 1); |
78 | 9 | if (elt == 0) |
79 | 7 | return_error(gs_error_stackunderflow); |
80 | 2 | ref_assign(op, elt); |
81 | 2 | return 0; |
82 | 9 | } |
83 | 174M | opn = op + ~(int)op->value.intval; |
84 | 174M | ref_assign_inline(op, opn); |
85 | 174M | return 0; |
86 | 174M | } |
87 | | |
88 | | /* <obj_n> ... <obj_0> <n> .argindex <obj_n> ... <obj_0> <obj_n> */ |
89 | | static int |
90 | | zargindex(i_ctx_t *i_ctx_p) |
91 | 16.3M | { |
92 | 16.3M | int code = zindex(i_ctx_p); |
93 | | |
94 | | /* |
95 | | * Pseudo-operators should use .argindex rather than index to access |
96 | | * their arguments on the stack, so that if there aren't enough, the |
97 | | * result will be a stackunderflow rather than a rangecheck. (This is, |
98 | | * in fact, the only reason this operator exists.) |
99 | | */ |
100 | 16.3M | if (code == gs_error_rangecheck && osp->value.intval >= 0) |
101 | 0 | code = gs_note_error(gs_error_stackunderflow); |
102 | 16.3M | return code; |
103 | 16.3M | } |
104 | | |
105 | | /* <obj_n-1> ... <obj_0> <n> <i> roll */ |
106 | | /* <obj_(i-1)_mod_ n> ... <obj_0> <obj_n-1> ... <obj_i_mod_n> */ |
107 | | int |
108 | | zroll(i_ctx_t *i_ctx_p) |
109 | 113M | { |
110 | 113M | os_ptr op = osp; |
111 | 113M | os_ptr op1 = op - 1; |
112 | 113M | int count, mod; |
113 | 113M | register os_ptr from, to; |
114 | 113M | register int n; |
115 | | |
116 | 113M | check_type(*op1, t_integer); |
117 | 113M | check_type(*op, t_integer); |
118 | 113M | if ((uint) op1->value.intval > (uint)(op1 - osbot)) { |
119 | | /* |
120 | | * The data might span multiple stack blocks. |
121 | | * There are efficient ways to handle this situation, |
122 | | * but they're more complicated than seems worth implementing; |
123 | | * for now, do something very simple and inefficient. |
124 | | */ |
125 | 14 | int left, i; |
126 | | |
127 | 14 | if (op1->value.intval < 0) |
128 | 2 | return_error(gs_error_rangecheck); |
129 | 12 | if (op1->value.intval + 2 > (int)ref_stack_count(&o_stack)) |
130 | 8 | return_error(gs_error_stackunderflow); |
131 | 4 | count = op1->value.intval; |
132 | 4 | if (count <= 1) { |
133 | 0 | pop(2); |
134 | 0 | return 0; |
135 | 0 | } |
136 | 4 | mod = op->value.intval; |
137 | 4 | if (mod >= count) |
138 | 0 | mod %= count; |
139 | 4 | else if (mod < 0) { |
140 | 0 | mod %= count; |
141 | 0 | if (mod < 0) |
142 | 0 | mod += count; /* can't assume % means mod! */ |
143 | 0 | } |
144 | | /* Use the chain rotation algorithm mentioned below. */ |
145 | 8 | for (i = 0, left = count; left; i++) { |
146 | 4 | ref *elt = ref_stack_index(&o_stack, i + 2); |
147 | 4 | ref save; |
148 | 4 | int j, k; |
149 | 4 | ref *next; |
150 | | |
151 | 4 | if (elt == NULL) |
152 | 0 | return_error(gs_error_stackunderflow); |
153 | 4 | save = *elt; |
154 | 15 | for (j = i, left--;; j = k, elt = next, left--) { |
155 | 15 | k = (j + mod) % count; |
156 | 15 | if (k == i) |
157 | 4 | break; |
158 | 11 | next = ref_stack_index(&o_stack, k + 2); |
159 | 11 | if (next == NULL) |
160 | 0 | return_error(gs_error_stackunderflow); |
161 | 11 | ref_assign(elt, next); |
162 | 11 | } |
163 | 4 | *elt = save; |
164 | 4 | } |
165 | 4 | pop(2); |
166 | 4 | return 0; |
167 | 4 | } |
168 | 113M | count = op1->value.intval; |
169 | 113M | if (count <= 1) { |
170 | 5 | pop(2); |
171 | 5 | return 0; |
172 | 5 | } |
173 | 113M | mod = op->value.intval; |
174 | | /* |
175 | | * The elegant approach, requiring no extra space, would be to |
176 | | * rotate the elements in chains separated by mod elements. |
177 | | * Instead, we simply check to make sure there is enough space |
178 | | * above op to do the roll in two block moves. |
179 | | * Unfortunately, we can't count on memcpy doing the right thing |
180 | | * in *either* direction. |
181 | | */ |
182 | 113M | switch (mod) { |
183 | 56.4M | case 1: /* common special case */ |
184 | 56.4M | pop(2); |
185 | 56.4M | op -= 2; |
186 | 56.4M | { |
187 | 56.4M | ref top; |
188 | | |
189 | 56.4M | ref_assign_inline(&top, op); |
190 | 175M | for (from = op, n = count; --n; from--) |
191 | 118M | ref_assign_inline(from, from - 1); |
192 | 56.4M | ref_assign_inline(from, &top); |
193 | 56.4M | } |
194 | 56.4M | return 0; |
195 | 52.3M | case -1: /* common special case */ |
196 | 52.3M | pop(2); |
197 | 52.3M | op -= 2; |
198 | 52.3M | { |
199 | 52.3M | ref bot; |
200 | | |
201 | 52.3M | to = op - count + 1; |
202 | 52.3M | ref_assign_inline(&bot, to); |
203 | 198M | for (n = count; --n; to++) |
204 | 146M | ref_assign(to, to + 1); |
205 | 52.3M | ref_assign_inline(to, &bot); |
206 | 52.3M | } |
207 | 52.3M | return 0; |
208 | 113M | } |
209 | 5.18M | if (mod < 0) { |
210 | 814k | mod += count; |
211 | 814k | if (mod < 0) { |
212 | 1 | mod %= count; |
213 | 1 | if (mod < 0) |
214 | 1 | mod += count; /* can't assume % means mod! */ |
215 | 1 | } |
216 | 4.37M | } else if (mod >= count) |
217 | 0 | mod %= count; |
218 | 5.18M | if (mod <= count >> 1) { |
219 | | /* Move everything up, then top elements down. */ |
220 | 4.42M | if (mod >= ostop - op) { |
221 | 0 | o_stack.requested = mod; |
222 | 0 | return_error(gs_error_stackoverflow); |
223 | 0 | } |
224 | 4.42M | pop(2); |
225 | 4.42M | op -= 2; |
226 | 31.0M | for (to = op + mod, from = op, n = count; n--; to--, from--) |
227 | 26.6M | ref_assign(to, from); |
228 | 4.42M | memcpy((char *)(from + 1), (char *)(op + 1), mod * sizeof(ref)); |
229 | 4.42M | } else { |
230 | | /* Move bottom elements up, then everything down. */ |
231 | 763k | mod = count - mod; |
232 | 763k | if (mod >= ostop - op) { |
233 | 1 | o_stack.requested = mod; |
234 | 1 | return_error(gs_error_stackoverflow); |
235 | 1 | } |
236 | 763k | pop(2); |
237 | 763k | op -= 2; |
238 | 763k | to = op - count + 1; |
239 | 763k | memcpy((char *)(op + 1), (char *)to, mod * sizeof(ref)); |
240 | 3.32M | for (from = to + mod, n = count; n--; to++, from++) |
241 | 2.55M | ref_assign(to, from); |
242 | 763k | } |
243 | 5.18M | return 0; |
244 | 5.18M | } |
245 | | |
246 | | /* |- ... clear |- */ |
247 | | /* The function name is changed, because the IRIS library has */ |
248 | | /* a function called zclear. */ |
249 | | static int |
250 | | zclear_stack(i_ctx_t *i_ctx_p) |
251 | 51 | { |
252 | 51 | ref_stack_clear(&o_stack); |
253 | 51 | return 0; |
254 | 51 | } |
255 | | |
256 | | /* |- <obj_n-1> ... <obj_0> count <obj_n-1> ... <obj_0> <n> */ |
257 | | static int |
258 | | zcount(i_ctx_t *i_ctx_p) |
259 | 334k | { |
260 | 334k | os_ptr op = osp; |
261 | | |
262 | 334k | push(1); |
263 | 334k | make_int(op, ref_stack_count(&o_stack) - 1); |
264 | 334k | return 0; |
265 | 334k | } |
266 | | |
267 | | /* - mark <mark> */ |
268 | | static int |
269 | | zmark(i_ctx_t *i_ctx_p) |
270 | 57.5M | { |
271 | 57.5M | os_ptr op = osp; |
272 | | |
273 | 57.5M | push(1); |
274 | 57.5M | make_mark(op); |
275 | 57.5M | return 0; |
276 | 57.5M | } |
277 | | |
278 | | /* <mark> ... cleartomark */ |
279 | | int |
280 | | zcleartomark(i_ctx_t *i_ctx_p) |
281 | 11.6M | { |
282 | 11.6M | uint count = ref_stack_counttomark(&o_stack); |
283 | | |
284 | 11.6M | if (count == 0) |
285 | 0 | return_error(gs_error_unmatchedmark); |
286 | 11.6M | ref_stack_pop(&o_stack, count); |
287 | 11.6M | return 0; |
288 | 11.6M | } |
289 | | |
290 | | /* <mark> <obj_n-1> ... <obj_0> counttomark */ |
291 | | /* <mark> <obj_n-1> ... <obj_0> <n> */ |
292 | | static int |
293 | | zcounttomark(i_ctx_t *i_ctx_p) |
294 | 55.0M | { |
295 | 55.0M | os_ptr op = osp; |
296 | 55.0M | uint count = ref_stack_counttomark(&o_stack); |
297 | | |
298 | 55.0M | if (count == 0) |
299 | 11 | return_error(gs_error_unmatchedmark); |
300 | 55.0M | push(1); |
301 | 55.0M | make_int(op, count - 1); |
302 | 55.0M | return 0; |
303 | 55.0M | } |
304 | | |
305 | | /* ------ Initialization procedure ------ */ |
306 | | |
307 | | const op_def zstack_op_defs[] = |
308 | | { |
309 | | {"2.argindex", zargindex}, |
310 | | {"0clear", zclear_stack}, |
311 | | {"0cleartomark", zcleartomark}, |
312 | | {"0count", zcount}, |
313 | | {"0counttomark", zcounttomark}, |
314 | | {"1dup", zdup}, |
315 | | {"2exch", zexch}, |
316 | | {"2index", zindex}, |
317 | | {"0mark", zmark}, |
318 | | {"1pop", zpop}, |
319 | | {"2roll", zroll}, |
320 | | op_def_end(0) |
321 | | }; |