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