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