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