xref: /plan9/sys/src/cmd/gs/src/zstack.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
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