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