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