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