xref: /plan9/sys/src/cmd/gs/src/zpacked.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1990, 1992, 1993, 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: zpacked.c,v 1.7 2004/08/19 19:33:09 stefan Exp $ */
18 /* Packed array operators */
19 #include "ghost.h"
20 #include "ialloc.h"
21 #include "idict.h"
22 #include "iname.h"
23 #include "istack.h"		/* for iparray.h */
24 #include "ipacked.h"
25 #include "iparray.h"
26 #include "ivmspace.h"
27 #include "oper.h"
28 #include "store.h"
29 #include "gxalloc.h"
30 
31 /* - currentpacking <bool> */
32 private int
zcurrentpacking(i_ctx_t * i_ctx_p)33 zcurrentpacking(i_ctx_t *i_ctx_p)
34 {
35     os_ptr op = osp;
36 
37     push(1);
38     ref_assign(op, &ref_array_packing);
39     return 0;
40 }
41 
42 /* <obj_0> ... <obj_n-1> <n> packedarray <packedarray> */
43 int
zpackedarray(i_ctx_t * i_ctx_p)44 zpackedarray(i_ctx_t *i_ctx_p)
45 {
46     os_ptr op = osp;
47     int code;
48     ref parr;
49 
50     check_type(*op, t_integer);
51     if (op->value.intval < 0 ||
52 	(op->value.intval > op - osbot &&
53 	 op->value.intval >= ref_stack_count(&o_stack))
54 	)
55 	return_error(e_rangecheck);
56     osp--;
57     code = make_packed_array(&parr, &o_stack, (uint) op->value.intval,
58 			     idmemory, "packedarray");
59     osp++;
60     if (code >= 0)
61 	*osp = parr;
62     return code;
63 }
64 
65 /* <bool> setpacking - */
66 private int
zsetpacking(i_ctx_t * i_ctx_p)67 zsetpacking(i_ctx_t *i_ctx_p)
68 {
69     os_ptr op = osp;
70     ref cont;
71 
72     check_type(*op, t_boolean);
73     make_struct(&cont, avm_local, ref_array_packing_container);
74     ref_assign_old(&cont, &ref_array_packing, op, "setpacking");
75     pop(1);
76     return 0;
77 }
78 
79 /* ------ Non-operator routines ------ */
80 
81 /* Make a packed array.  See the comment in packed.h about */
82 /* ensuring that refs in mixed arrays are properly aligned. */
83 #undef idmemory			/****** NOTA BENE ******/
84 int
make_packed_array(ref * parr,ref_stack_t * pstack,uint size,gs_dual_memory_t * idmemory,client_name_t cname)85 make_packed_array(ref * parr, ref_stack_t * pstack, uint size,
86 		  gs_dual_memory_t *idmemory, client_name_t cname)
87 {
88     uint i;
89     const ref *pref;
90     uint idest = 0, ishort = 0;
91     ref_packed *pbody;
92     ref_packed *pdest;
93     ref_packed *pshort;		/* points to start of */
94 				/* last run of short elements */
95     gs_ref_memory_t *imem = idmemory->current;
96     uint space = imemory_space(imem);
97     int skip = 0, pad;
98     ref rtemp;
99     int code;
100 
101     /* Do a first pass to calculate the size of the array, */
102     /* and to detect local-into-global stores. */
103 
104     for (i = size; i != 0; i--) {
105 	pref = ref_stack_index(pstack, i - 1);
106 	switch (r_btype(pref)) {	/* not r_type, opers are special */
107 	    case t_name:
108 	      if (name_index(imem, pref) >= packed_name_max_index)
109 		    break;	/* can't pack */
110 		idest++;
111 		continue;
112 	    case t_integer:
113 		if (pref->value.intval < packed_min_intval ||
114 		    pref->value.intval > packed_max_intval
115 		    )
116 		    break;
117 		idest++;
118 		continue;
119 	    case t_oparray:
120 		/* Check for local-into-global store. */
121 		store_check_space(space, pref);
122 		/* falls through */
123 	    case t_operator:
124 		{
125 		    uint oidx;
126 
127 		    if (!r_has_attr(pref, a_executable))
128 			break;
129 		    oidx = op_index(pref);
130 		    if (oidx == 0 || oidx > packed_int_mask)
131 			break;
132 		}
133 		idest++;
134 		continue;
135 	    default:
136 		/* Check for local-into-global store. */
137 		store_check_space(space, pref);
138 	}
139 	/* Can't pack this element, use a full ref. */
140 	/* We may have to unpack up to align_packed_per_ref - 1 */
141 	/* preceding short elements. */
142 	/* If we are at the beginning of the array, however, */
143 	/* we can just move the elements up. */
144 	{
145 	    int i = (idest - ishort) & (align_packed_per_ref - 1);
146 
147 	    if (ishort == 0)	/* first time */
148 		idest += skip = -i & (align_packed_per_ref - 1);
149 	    else
150 		idest += (packed_per_ref - 1) * i;
151 	}
152 	ishort = idest += packed_per_ref;
153     }
154     pad = -(int)idest & (packed_per_ref - 1);	/* padding at end */
155 
156     /* Now we can allocate the array. */
157 
158     code = gs_alloc_ref_array(imem, &rtemp, 0, (idest + pad) / packed_per_ref,
159 			      cname);
160     if (code < 0)
161 	return code;
162     pbody = (ref_packed *) rtemp.value.refs;
163 
164     /* Make sure any initial skipped elements contain legal packed */
165     /* refs, so that the garbage collector can scan storage. */
166 
167     pshort = pbody;
168     for (; skip; skip--)
169 	*pbody++ = pt_tag(pt_integer);
170     pdest = pbody;
171 
172     for (i = size; i != 0; i--) {
173 	pref = ref_stack_index(pstack, i - 1);
174 	switch (r_btype(pref)) {	/* not r_type, opers are special */
175 	    case t_name:
176 		{
177 		    uint nidx = name_index(imem, pref);
178 
179 		    if (nidx >= packed_name_max_index)
180 			break;	/* can't pack */
181 		    *pdest++ = nidx +
182 			(r_has_attr(pref, a_executable) ?
183 			 pt_tag(pt_executable_name) :
184 			 pt_tag(pt_literal_name));
185 		}
186 		continue;
187 	    case t_integer:
188 		if (pref->value.intval < packed_min_intval ||
189 		    pref->value.intval > packed_max_intval
190 		    )
191 		    break;
192 		*pdest++ = pt_tag(pt_integer) +
193 		    ((short)pref->value.intval - packed_min_intval);
194 		continue;
195 	    case t_oparray:
196 	    case t_operator:
197 		{
198 		    uint oidx;
199 
200 		    if (!r_has_attr(pref, a_executable))
201 			break;
202 		    oidx = op_index(pref);
203 		    if (oidx == 0 || oidx > packed_int_mask)
204 			break;
205 		    *pdest++ = pt_tag(pt_executable_operator) + oidx;
206 		}
207 		continue;
208 	}
209 	/* Can't pack this element, use a full ref. */
210 	/* We may have to unpack up to align_packed_per_ref - 1 */
211 	/* preceding short elements. */
212 	/* Note that if we are at the beginning of the array, */
213 	/* 'skip' already ensures that we don't need to do this. */
214 	{
215 	    int i = (pdest - pshort) & (align_packed_per_ref - 1);
216 	    const ref_packed *psrc = pdest;
217 	    ref *pmove =
218 	    (ref *) (pdest += (packed_per_ref - 1) * i);
219 
220 	    ref_assign_new(pmove, pref);
221 	    while (--i >= 0) {
222 		--psrc;
223 		--pmove;
224 		packed_get(imem->non_gc_memory, psrc, pmove);
225 	    }
226 	}
227 	pshort = pdest += packed_per_ref;
228     }
229 
230     {
231 	int atype =
232 	(pdest == pbody + size ? t_shortarray : t_mixedarray);
233 
234 	/* Pad with legal packed refs so that the garbage collector */
235 	/* can scan storage. */
236 
237 	for (; pad; pad--)
238 	    *pdest++ = pt_tag(pt_integer);
239 
240 	/* Finally, make the array. */
241 
242 	ref_stack_pop(pstack, size);
243 	make_tasv_new(parr, atype, a_readonly | space, size,
244 		      packed, pbody + skip);
245     }
246     return 0;
247 }
248 
249 /* ------ Initialization procedure ------ */
250 
251 const op_def zpacked_op_defs[] =
252 {
253     {"0currentpacking", zcurrentpacking},
254     {"1packedarray", zpackedarray},
255     {"1setpacking", zsetpacking},
256     op_def_end(0)
257 };
258