/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 | 163k | { |
29 | 163k | os_ptr op = osp; |
30 | | |
31 | 163k | check_op(1); |
32 | 163k | pop(1); |
33 | 163k | return 0; |
34 | 163k | } |
35 | | |
36 | | /* <obj1> <obj2> exch <obj2> <obj1> */ |
37 | | int |
38 | | zexch(i_ctx_t *i_ctx_p) |
39 | 7.59k | { |
40 | 7.59k | os_ptr op = osp; |
41 | 7.59k | ref next; |
42 | | |
43 | 7.59k | check_op(2); |
44 | 7.59k | ref_assign_inline(&next, op - 1); |
45 | 7.59k | ref_assign_inline(op - 1, op); |
46 | 7.59k | ref_assign_inline(op, &next); |
47 | 7.59k | return 0; |
48 | 7.59k | } |
49 | | |
50 | | /* <obj> dup <obj> <obj> */ |
51 | | int |
52 | | zdup(i_ctx_t *i_ctx_p) |
53 | 26 | { |
54 | 26 | os_ptr op = osp; |
55 | | |
56 | 26 | check_op(1); |
57 | 26 | push(1); |
58 | 26 | ref_assign_inline(op, op - 1); |
59 | 26 | return 0; |
60 | 26 | } |
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 | 3.17G | { |
66 | 3.17G | os_ptr op = osp; |
67 | 3.17G | register os_ptr opn; |
68 | | |
69 | 3.17G | check_op(1); |
70 | 3.17G | check_type(*op, t_integer); |
71 | 3.17G | if ((ulong)op->value.intval >= (ulong)(op - osbot)) { |
72 | | /* Might be in an older stack block. */ |
73 | 21.6k | ref *elt; |
74 | | |
75 | 21.6k | if (op->value.intval < 0) |
76 | 12 | return_error(gs_error_rangecheck); |
77 | 21.5k | elt = ref_stack_index(&o_stack, op->value.intval + 1); |
78 | 21.5k | if (elt == 0) |
79 | 51 | return_error(gs_error_stackunderflow); |
80 | 21.5k | ref_assign(op, elt); |
81 | 21.5k | return 0; |
82 | 21.5k | } |
83 | 3.17G | opn = op + ~(int)op->value.intval; |
84 | 3.17G | ref_assign_inline(op, opn); |
85 | 3.17G | return 0; |
86 | 3.17G | } |
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 | 270M | { |
92 | 270M | 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 | 270M | if (code == gs_error_rangecheck && osp->value.intval >= 0) |
101 | 0 | code = gs_note_error(gs_error_stackunderflow); |
102 | 270M | return code; |
103 | 270M | } |
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 | 1.92G | { |
110 | 1.92G | os_ptr op = osp; |
111 | 1.92G | os_ptr op1 = op - 1; |
112 | 1.92G | int count, mod; |
113 | 1.92G | register os_ptr from, to; |
114 | 1.92G | register int n; |
115 | | |
116 | 1.92G | check_type(*op1, t_integer); |
117 | 1.92G | check_type(*op, t_integer); |
118 | 1.92G | 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 | 238 | int left, i; |
126 | | |
127 | 238 | if (op1->value.intval < 0) |
128 | 30 | return_error(gs_error_rangecheck); |
129 | 208 | if (op1->value.intval + 2 > (int)ref_stack_count(&o_stack)) |
130 | 78 | return_error(gs_error_stackunderflow); |
131 | 130 | count = op1->value.intval; |
132 | 130 | if (count <= 1) { |
133 | 88 | pop(2); |
134 | 88 | return 0; |
135 | 88 | } |
136 | 42 | mod = op->value.intval; |
137 | 42 | if (mod >= count) |
138 | 0 | mod %= count; |
139 | 42 | else if (mod < 0) { |
140 | 5 | mod %= count; |
141 | 5 | if (mod < 0) |
142 | 5 | mod += count; /* can't assume % means mod! */ |
143 | 5 | } |
144 | | /* Use the chain rotation algorithm mentioned below. */ |
145 | 89 | for (i = 0, left = count; left; i++) { |
146 | 47 | ref *elt = ref_stack_index(&o_stack, i + 2); |
147 | 47 | ref save; |
148 | 47 | int j, k; |
149 | 47 | ref *next; |
150 | | |
151 | 47 | if (elt == NULL) |
152 | 0 | return_error(gs_error_stackunderflow); |
153 | 47 | save = *elt; |
154 | 4.08k | for (j = i, left--;; j = k, elt = next, left--) { |
155 | 4.08k | k = (j + mod) % count; |
156 | 4.08k | if (k == i) |
157 | 47 | break; |
158 | 4.04k | next = ref_stack_index(&o_stack, k + 2); |
159 | 4.04k | if (next == NULL) |
160 | 0 | return_error(gs_error_stackunderflow); |
161 | 4.04k | ref_assign(elt, next); |
162 | 4.04k | } |
163 | 47 | *elt = save; |
164 | 47 | } |
165 | 42 | pop(2); |
166 | 42 | return 0; |
167 | 42 | } |
168 | 1.92G | count = op1->value.intval; |
169 | 1.92G | if (count <= 1) { |
170 | 18.5k | pop(2); |
171 | 18.5k | return 0; |
172 | 18.5k | } |
173 | 1.92G | 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 | 1.92G | switch (mod) { |
183 | 947M | case 1: /* common special case */ |
184 | 947M | pop(2); |
185 | 947M | op -= 2; |
186 | 947M | { |
187 | 947M | ref top; |
188 | | |
189 | 947M | ref_assign_inline(&top, op); |
190 | 2.94G | for (from = op, n = count; --n; from--) |
191 | 2.00G | ref_assign_inline(from, from - 1); |
192 | 947M | ref_assign_inline(from, &top); |
193 | 947M | } |
194 | 947M | return 0; |
195 | 876M | case -1: /* common special case */ |
196 | 876M | pop(2); |
197 | 876M | op -= 2; |
198 | 876M | { |
199 | 876M | ref bot; |
200 | | |
201 | 876M | to = op - count + 1; |
202 | 876M | ref_assign_inline(&bot, to); |
203 | 3.32G | for (n = count; --n; to++) |
204 | 2.44G | ref_assign(to, to + 1); |
205 | 876M | ref_assign_inline(to, &bot); |
206 | 876M | } |
207 | 876M | return 0; |
208 | 1.92G | } |
209 | 97.7M | if (mod < 0) { |
210 | 14.1M | mod += count; |
211 | 14.1M | if (mod < 0) { |
212 | 146 | mod %= count; |
213 | 146 | if (mod < 0) |
214 | 88 | mod += count; /* can't assume % means mod! */ |
215 | 146 | } |
216 | 83.6M | } else if (mod >= count) |
217 | 11 | mod %= count; |
218 | 97.7M | if (mod <= count >> 1) { |
219 | | /* Move everything up, then top elements down. */ |
220 | 86.1M | if (mod >= ostop - op) { |
221 | 33 | o_stack.requested = mod; |
222 | 33 | return_error(gs_error_stackoverflow); |
223 | 33 | } |
224 | 86.1M | pop(2); |
225 | 86.1M | op -= 2; |
226 | 571M | for (to = op + mod, from = op, n = count; n--; to--, from--) |
227 | 485M | ref_assign(to, from); |
228 | 86.1M | memcpy((char *)(from + 1), (char *)(op + 1), mod * sizeof(ref)); |
229 | 86.1M | } else { |
230 | | /* Move bottom elements up, then everything down. */ |
231 | 11.5M | mod = count - mod; |
232 | 11.5M | if (mod >= ostop - op) { |
233 | 5 | o_stack.requested = mod; |
234 | 5 | return_error(gs_error_stackoverflow); |
235 | 5 | } |
236 | 11.5M | pop(2); |
237 | 11.5M | op -= 2; |
238 | 11.5M | to = op - count + 1; |
239 | 11.5M | memcpy((char *)(op + 1), (char *)to, mod * sizeof(ref)); |
240 | 52.0M | for (from = to + mod, n = count; n--; to++, from++) |
241 | 40.4M | ref_assign(to, from); |
242 | 11.5M | } |
243 | 97.7M | return 0; |
244 | 97.7M | } |
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 | 1.83k | { |
252 | 1.83k | ref_stack_clear(&o_stack); |
253 | 1.83k | return 0; |
254 | 1.83k | } |
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 | 6.02M | { |
260 | 6.02M | os_ptr op = osp; |
261 | | |
262 | 6.02M | push(1); |
263 | 6.02M | make_int(op, ref_stack_count(&o_stack) - 1); |
264 | 6.02M | return 0; |
265 | 6.02M | } |
266 | | |
267 | | /* - mark <mark> */ |
268 | | static int |
269 | | zmark(i_ctx_t *i_ctx_p) |
270 | 968M | { |
271 | 968M | os_ptr op = osp; |
272 | | |
273 | 968M | push(1); |
274 | 968M | make_mark(op); |
275 | 968M | return 0; |
276 | 968M | } |
277 | | |
278 | | /* <mark> ... cleartomark */ |
279 | | int |
280 | | zcleartomark(i_ctx_t *i_ctx_p) |
281 | 195M | { |
282 | 195M | uint count = ref_stack_counttomark(&o_stack); |
283 | | |
284 | 195M | if (count == 0) |
285 | 14 | return_error(gs_error_unmatchedmark); |
286 | 195M | ref_stack_pop(&o_stack, count); |
287 | 195M | return 0; |
288 | 195M | } |
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 | 928M | { |
295 | 928M | os_ptr op = osp; |
296 | 928M | uint count = ref_stack_counttomark(&o_stack); |
297 | | |
298 | 928M | if (count == 0) |
299 | 204 | return_error(gs_error_unmatchedmark); |
300 | 928M | push(1); |
301 | 928M | make_int(op, count - 1); |
302 | 928M | return 0; |
303 | 928M | } |
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 | | }; |