/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 | 683 | { |
29 | 683 | os_ptr op = osp; |
30 | | |
31 | 683 | check_op(1); |
32 | 683 | pop(1); |
33 | 683 | return 0; |
34 | 683 | } |
35 | | |
36 | | /* <obj1> <obj2> exch <obj2> <obj1> */ |
37 | | int |
38 | | zexch(i_ctx_t *i_ctx_p) |
39 | 0 | { |
40 | 0 | os_ptr op = osp; |
41 | 0 | ref next; |
42 | |
|
43 | 0 | check_op(2); |
44 | 0 | ref_assign_inline(&next, op - 1); |
45 | 0 | ref_assign_inline(op - 1, op); |
46 | 0 | ref_assign_inline(op, &next); |
47 | 0 | return 0; |
48 | 0 | } |
49 | | |
50 | | /* <obj> dup <obj> <obj> */ |
51 | | int |
52 | | zdup(i_ctx_t *i_ctx_p) |
53 | 0 | { |
54 | 0 | os_ptr op = osp; |
55 | |
|
56 | 0 | check_op(1); |
57 | 0 | push(1); |
58 | 0 | ref_assign_inline(op, op - 1); |
59 | 0 | return 0; |
60 | 0 | } |
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 | 12.0M | { |
66 | 12.0M | os_ptr op = osp; |
67 | 12.0M | register os_ptr opn; |
68 | | |
69 | 12.0M | check_type(*op, t_integer); |
70 | 12.0M | if ((ulong)op->value.intval >= (ulong)(op - osbot)) { |
71 | | /* Might be in an older stack block. */ |
72 | 0 | ref *elt; |
73 | |
|
74 | 0 | if (op->value.intval < 0) |
75 | 0 | return_error(gs_error_rangecheck); |
76 | 0 | elt = ref_stack_index(&o_stack, op->value.intval + 1); |
77 | 0 | if (elt == 0) |
78 | 0 | return_error(gs_error_stackunderflow); |
79 | 0 | ref_assign(op, elt); |
80 | 0 | return 0; |
81 | 0 | } |
82 | 12.0M | opn = op + ~(int)op->value.intval; |
83 | 12.0M | ref_assign_inline(op, opn); |
84 | 12.0M | return 0; |
85 | 12.0M | } |
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 | 1.13M | { |
91 | 1.13M | 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 | 1.13M | if (code == gs_error_rangecheck && osp->value.intval >= 0) |
100 | 0 | code = gs_note_error(gs_error_stackunderflow); |
101 | 1.13M | return code; |
102 | 1.13M | } |
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 | 8.52M | { |
109 | 8.52M | os_ptr op = osp; |
110 | 8.52M | os_ptr op1 = op - 1; |
111 | 8.52M | int count, mod; |
112 | 8.52M | register os_ptr from, to; |
113 | 8.52M | register int n; |
114 | | |
115 | 8.52M | check_type(*op1, t_integer); |
116 | 8.52M | check_type(*op, t_integer); |
117 | 8.52M | 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 | 0 | int left, i; |
125 | |
|
126 | 0 | if (op1->value.intval < 0) |
127 | 0 | return_error(gs_error_rangecheck); |
128 | 0 | if (op1->value.intval + 2 > (int)ref_stack_count(&o_stack)) |
129 | 0 | return_error(gs_error_stackunderflow); |
130 | 0 | count = op1->value.intval; |
131 | 0 | if (count <= 1) { |
132 | 0 | pop(2); |
133 | 0 | return 0; |
134 | 0 | } |
135 | 0 | mod = op->value.intval; |
136 | 0 | if (mod >= count) |
137 | 0 | mod %= count; |
138 | 0 | else if (mod < 0) { |
139 | 0 | mod %= count; |
140 | 0 | if (mod < 0) |
141 | 0 | mod += count; /* can't assume % means mod! */ |
142 | 0 | } |
143 | | /* Use the chain rotation algorithm mentioned below. */ |
144 | 0 | for (i = 0, left = count; left; i++) { |
145 | 0 | ref *elt = ref_stack_index(&o_stack, i + 2); |
146 | 0 | ref save; |
147 | 0 | int j, k; |
148 | 0 | ref *next; |
149 | |
|
150 | 0 | save = *elt; |
151 | 0 | for (j = i, left--;; j = k, elt = next, left--) { |
152 | 0 | k = (j + mod) % count; |
153 | 0 | if (k == i) |
154 | 0 | break; |
155 | 0 | next = ref_stack_index(&o_stack, k + 2); |
156 | 0 | ref_assign(elt, next); |
157 | 0 | } |
158 | 0 | *elt = save; |
159 | 0 | } |
160 | 0 | pop(2); |
161 | 0 | return 0; |
162 | 0 | } |
163 | 8.52M | count = op1->value.intval; |
164 | 8.52M | if (count <= 1) { |
165 | 0 | pop(2); |
166 | 0 | return 0; |
167 | 0 | } |
168 | 8.52M | 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 | 8.52M | switch (mod) { |
178 | 4.29M | case 1: /* common special case */ |
179 | 4.29M | pop(2); |
180 | 4.29M | op -= 2; |
181 | 4.29M | { |
182 | 4.29M | ref top; |
183 | | |
184 | 4.29M | ref_assign_inline(&top, op); |
185 | 13.5M | for (from = op, n = count; --n; from--) |
186 | 9.23M | ref_assign_inline(from, from - 1); |
187 | 4.29M | ref_assign_inline(from, &top); |
188 | 4.29M | } |
189 | 4.29M | return 0; |
190 | 3.89M | case -1: /* common special case */ |
191 | 3.89M | pop(2); |
192 | 3.89M | op -= 2; |
193 | 3.89M | { |
194 | 3.89M | ref bot; |
195 | | |
196 | 3.89M | to = op - count + 1; |
197 | 3.89M | ref_assign_inline(&bot, to); |
198 | 14.6M | for (n = count; --n; to++) |
199 | 10.7M | ref_assign(to, to + 1); |
200 | 3.89M | ref_assign_inline(to, &bot); |
201 | 3.89M | } |
202 | 3.89M | return 0; |
203 | 8.52M | } |
204 | 339k | if (mod < 0) { |
205 | 70.7k | mod += count; |
206 | 70.7k | if (mod < 0) { |
207 | 0 | mod %= count; |
208 | 0 | if (mod < 0) |
209 | 0 | mod += count; /* can't assume % means mod! */ |
210 | 0 | } |
211 | 269k | } else if (mod >= count) |
212 | 0 | mod %= count; |
213 | 339k | if (mod <= count >> 1) { |
214 | | /* Move everything up, then top elements down. */ |
215 | 300k | if (mod >= ostop - op) { |
216 | 0 | o_stack.requested = mod; |
217 | 0 | return_error(gs_error_stackoverflow); |
218 | 0 | } |
219 | 300k | pop(2); |
220 | 300k | op -= 2; |
221 | 2.38M | for (to = op + mod, from = op, n = count; n--; to--, from--) |
222 | 2.08M | ref_assign(to, from); |
223 | 300k | memcpy((char *)(from + 1), (char *)(op + 1), mod * sizeof(ref)); |
224 | 300k | } else { |
225 | | /* Move bottom elements up, then everything down. */ |
226 | 39.0k | mod = count - mod; |
227 | 39.0k | if (mod >= ostop - op) { |
228 | 0 | o_stack.requested = mod; |
229 | 0 | return_error(gs_error_stackoverflow); |
230 | 0 | } |
231 | 39.0k | pop(2); |
232 | 39.0k | op -= 2; |
233 | 39.0k | to = op - count + 1; |
234 | 39.0k | memcpy((char *)(op + 1), (char *)to, mod * sizeof(ref)); |
235 | 169k | for (from = to + mod, n = count; n--; to++, from++) |
236 | 130k | ref_assign(to, from); |
237 | 39.0k | } |
238 | 339k | return 0; |
239 | 339k | } |
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 | 1 | { |
247 | 1 | ref_stack_clear(&o_stack); |
248 | 1 | return 0; |
249 | 1 | } |
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 | 24.4k | { |
255 | 24.4k | os_ptr op = osp; |
256 | | |
257 | 24.4k | push(1); |
258 | 24.4k | make_int(op, ref_stack_count(&o_stack) - 1); |
259 | 24.4k | return 0; |
260 | 24.4k | } |
261 | | |
262 | | /* - mark <mark> */ |
263 | | static int |
264 | | zmark(i_ctx_t *i_ctx_p) |
265 | 4.53M | { |
266 | 4.53M | os_ptr op = osp; |
267 | | |
268 | 4.53M | push(1); |
269 | 4.53M | make_mark(op); |
270 | 4.53M | return 0; |
271 | 4.53M | } |
272 | | |
273 | | /* <mark> ... cleartomark */ |
274 | | int |
275 | | zcleartomark(i_ctx_t *i_ctx_p) |
276 | 820k | { |
277 | 820k | uint count = ref_stack_counttomark(&o_stack); |
278 | | |
279 | 820k | if (count == 0) |
280 | 0 | return_error(gs_error_unmatchedmark); |
281 | 820k | ref_stack_pop(&o_stack, count); |
282 | 820k | return 0; |
283 | 820k | } |
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 | 3.85M | { |
290 | 3.85M | os_ptr op = osp; |
291 | 3.85M | uint count = ref_stack_counttomark(&o_stack); |
292 | | |
293 | 3.85M | if (count == 0) |
294 | 0 | return_error(gs_error_unmatchedmark); |
295 | 3.85M | push(1); |
296 | 3.85M | make_int(op, count - 1); |
297 | 3.85M | return 0; |
298 | 3.85M | } |
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 | | }; |