xref: /plan9/sys/src/cmd/gs/src/gsparams.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1998, 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: gsparams.c,v 1.5 2002/06/16 05:48:55 lpd Exp $ */
18 /* Generic parameter list serializer & expander */
19 
20 /* Initial version 2/1/98 by John Desrosiers (soho@crl.com) */
21 /* 11/16/98 L. Peter Deutsch (ghost@aladdin.com) edited to remove names
22    put_bytes, put_word which conflicted with other modules */
23 
24 #include "gx.h"
25 #include "memory_.h"
26 #include "gserrors.h"
27 #include "gsparams.h"
28 
29 /* ----------- Local Type Decl's ------------ */
30 typedef struct {
31     byte *buf;			/* current buffer ptr */
32     byte *buf_end;		/* end of buffer */
33     unsigned total_sizeof;	/* current # bytes in buf */
34 } WriteBuffer;
35 
36 /* ---------- Forward refs ----------- */
37 private void
38 ptr_align_to(
39 	    const byte ** src,	/* pointer to align */
40 	    unsigned alignment	/* alignment, must be power of 2 */
41 	    );
42 private void
43 wb_put_word(
44 	    unsigned source,	/* number to put to buffer */
45 	    WriteBuffer * dest	/* destination descriptor */
46 	    );
47 private void
48 wb_put_bytes(
49 	     const byte * source,	/* bytes to put to buffer */
50 	     unsigned source_sizeof,	/* # bytes to put */
51 	     WriteBuffer * dest		/* destination descriptor */
52 	     );
53 private void
54 wb_put_alignment(
55 		 unsigned alignment,	/* alignment to match, must be power 2 */
56 		 WriteBuffer * dest	/* destination descriptor */
57 		 );
58 
59 /* Get word compressed with wb_put_word */
60 private unsigned		/* decompressed word */
61 buf_get_word(
62 	    const byte ** src	/* UPDATES: ptr to src buf ptr */
63 	    );
64 
65 
66 /* ------------ Serializer ------------ */
67 /* Serialize the contents of a gs_param_list (including sub-dicts) */
68 int				/* ret -ve err, else # bytes needed to represent param list, whether */
69 
70 /* or not it actually fit into buffer. List was successully */
71 
72 /* serialized only if if this # is <= supplied buf size. */
gs_param_list_serialize(gs_param_list * list,byte * buf,int buf_sizeof)73 gs_param_list_serialize(
74 			   gs_param_list * list,	/* root of list to serialize */
75 					/* list MUST BE IN READ MODE */
76 			   byte * buf,	/* destination buffer (can be 0) */
77 			   int buf_sizeof	/* # bytes available in buf (can be 0) */
78 )
79 {
80     int code = 0;
81     int temp_code;
82     gs_param_enumerator_t key_enum;
83     gs_param_key_t key;
84     WriteBuffer write_buf;
85 
86     write_buf.buf = buf;
87     write_buf.buf_end = buf + (buf ? buf_sizeof : 0);
88     write_buf.total_sizeof = 0;
89     param_init_enumerator(&key_enum);
90 
91     /* Each item is serialized as ("word" means compressed word):
92      *  word: key sizeof + 1, or 0 if end of list/dict
93      *  word: data type(gs_param_type_xxx)
94      *  byte[]: key, including trailing \0
95      *  (if simple type)
96      *   byte[]: unpacked representation of data
97      *  (if simple array or string)
98      *   byte[]: unpacked mem image of gs_param_xxx_array structure
99      *   pad: to array alignment
100      *   byte[]: data associated with array contents
101      *  (if string/name array)
102      *   byte[]: unpacked mem image of gs_param_string_array structure
103      *   pad: to void *
104      *   { gs_param_string structure mem image;
105      *     data associated with string;
106      *   } for each string in array
107      *  (if dict/dict_int_keys)
108      *   word: # of entries in dict,
109      *   pad: to void *
110      *   dict entries follow immediately until end-of-dict
111      *
112      * NB that this format is designed to allow using an input buffer
113      * as the direct source of data when expanding a gs_c_param_list
114      */
115     /* Enumerate all the keys; use keys to get their typed values */
116     while ((code = param_get_next_key(list, &key_enum, &key)) == 0) {
117 	int value_top_sizeof;
118 	int value_base_sizeof;
119 
120 	/* Get next datum & put its type & key to buffer */
121 	gs_param_typed_value value;
122 	char string_key[256];
123 
124 	if (sizeof(string_key) < key.size + 1) {
125 	    code = gs_note_error(gs_error_rangecheck);
126 	    break;
127 	}
128 	memcpy(string_key, key.data, key.size);
129 	string_key[key.size] = 0;
130 	if ((code = param_read_typed(list, string_key, &value)) != 0) {
131 	    code = code > 0 ? gs_note_error(gs_error_unknownerror) : code;
132 	    break;
133 	}
134 	wb_put_word((unsigned)key.size + 1, &write_buf);
135 	wb_put_word((unsigned)value.type, &write_buf);
136 	wb_put_bytes((byte *) string_key, key.size + 1, &write_buf);
137 
138 	/* Put value & its size to buffer */
139 	value_top_sizeof = gs_param_type_sizes[value.type];
140 	value_base_sizeof = gs_param_type_base_sizes[value.type];
141 	switch (value.type) {
142 	    case gs_param_type_null:
143 	    case gs_param_type_bool:
144 	    case gs_param_type_int:
145 	    case gs_param_type_long:
146 	    case gs_param_type_float:
147 		wb_put_bytes((byte *) & value.value, value_top_sizeof, &write_buf);
148 		break;
149 
150 	    case gs_param_type_string:
151 	    case gs_param_type_name:
152 	    case gs_param_type_int_array:
153 	    case gs_param_type_float_array:
154 		wb_put_bytes((byte *) & value.value, value_top_sizeof, &write_buf);
155 		wb_put_alignment(value_base_sizeof, &write_buf);
156 		value_base_sizeof *= value.value.s.size;
157 		wb_put_bytes(value.value.s.data, value_base_sizeof, &write_buf);
158 		break;
159 
160 	    case gs_param_type_string_array:
161 	    case gs_param_type_name_array:
162 		value_base_sizeof *= value.value.sa.size;
163 		wb_put_bytes((const byte *)&value.value, value_top_sizeof, &write_buf);
164 		wb_put_alignment(sizeof(void *), &write_buf);
165 
166 		wb_put_bytes((const byte *)value.value.sa.data, value_base_sizeof,
167 			  &write_buf);
168 		{
169 		    int str_count;
170 		    const gs_param_string *sa;
171 
172 		    for (str_count = value.value.sa.size,
173 			 sa = value.value.sa.data; str_count-- > 0; ++sa)
174 			wb_put_bytes(sa->data, sa->size, &write_buf);
175 		}
176 		break;
177 
178 	    case gs_param_type_dict:
179 	    case gs_param_type_dict_int_keys:
180 		wb_put_word(value.value.d.size, &write_buf);
181 		wb_put_alignment(sizeof(void *), &write_buf);
182 
183 		{
184 		    int bytes_written =
185 		    gs_param_list_serialize(value.value.d.list,
186 					    write_buf.buf,
187 		     write_buf.buf ? write_buf.buf_end - write_buf.buf : 0);
188 
189 		    temp_code = param_end_read_dict(list,
190 						    (const char *)key.data,
191 						    &value.value.d);
192 		    if (bytes_written < 0)
193 			code = bytes_written;
194 		    else {
195 			code = temp_code;
196 			if (bytes_written)
197 			    wb_put_bytes(write_buf.buf, bytes_written, &write_buf);
198 		    }
199 		}
200 		break;
201 
202 	    default:
203 		code = gs_note_error(gs_error_unknownerror);
204 		break;
205 	}
206 	if (code < 0)
207 	    break;
208     }
209 
210     /* Write end marker, which is an (illegal) 0 key length */
211     if (code >= 0) {
212 	wb_put_word(0, &write_buf);
213 	code = write_buf.total_sizeof;
214     }
215     return code;
216 }
217 
218 
219 /* ------------ Expander --------------- */
220 /* Expand a buffer into a gs_param_list (including sub-dicts) */
221 int				/* ret -ve err, +ve # of chars read from buffer */
gs_param_list_unserialize(gs_param_list * list,const byte * buf)222 gs_param_list_unserialize(
223 			     gs_param_list * list,	/* root of list to expand to */
224 					/* list MUST BE IN WRITE MODE */
225 			     const byte * buf	/* source buffer */
226 )
227 {
228     int code = 0;
229     const byte *orig_buf = buf;
230 
231     do {
232 	gs_param_typed_value typed;
233 	gs_param_name key;
234 	unsigned key_sizeof;
235 	int value_top_sizeof;
236 	int value_base_sizeof;
237 	int temp_code;
238 	gs_param_type type;
239 
240 	/* key length, 0 indicates end of data */
241 	key_sizeof = buf_get_word(&buf);
242 	if (key_sizeof == 0)	/* end of data */
243 	    break;
244 
245 	/* data type */
246 	type = (gs_param_type) buf_get_word(&buf);
247 
248 	/* key */
249 	key = (gs_param_name) buf;
250 	buf += key_sizeof;
251 
252 	/* Data values */
253 	value_top_sizeof = gs_param_type_sizes[type];
254 	value_base_sizeof = gs_param_type_base_sizes[type];
255 	typed.type = type;
256 	if (type != gs_param_type_dict && type != gs_param_type_dict_int_keys) {
257 	    memcpy(&typed.value, buf, value_top_sizeof);
258 	    buf += value_top_sizeof;
259 	}
260 	switch (type) {
261 	    case gs_param_type_null:
262 	    case gs_param_type_bool:
263 	    case gs_param_type_int:
264 	    case gs_param_type_long:
265 	    case gs_param_type_float:
266 		break;
267 
268 	    case gs_param_type_string:
269 	    case gs_param_type_name:
270 	    case gs_param_type_int_array:
271 	    case gs_param_type_float_array:
272 		ptr_align_to(&buf, value_base_sizeof);
273 		typed.value.s.data = buf;
274 		typed.value.s.persistent = false;
275 		buf += typed.value.s.size * value_base_sizeof;
276 		break;
277 
278 	    case gs_param_type_string_array:
279 	    case gs_param_type_name_array:
280 		ptr_align_to(&buf, sizeof(void *));
281 
282 		typed.value.sa.data = (const gs_param_string *)buf;
283 		typed.value.sa.persistent = false;
284 		buf += typed.value.s.size * value_base_sizeof;
285 		{
286 		    int str_count;
287 		    gs_param_string *sa;
288 
289 		    for (str_count = typed.value.sa.size,
290 			 sa = (gs_param_string *) typed.value.sa.data;
291 			 str_count-- > 0; ++sa) {
292 			sa->data = buf;
293 			sa->persistent = false;
294 			buf += sa->size;
295 		    }
296 		}
297 		break;
298 
299 	    case gs_param_type_dict:
300 	    case gs_param_type_dict_int_keys:
301 		typed.value.d.size = buf_get_word(&buf);
302 		code = param_begin_write_dict
303 		    (list, key, &typed.value.d, type == gs_param_type_dict_int_keys);
304 		if (code < 0)
305 		    break;
306 		ptr_align_to(&buf, sizeof(void *));
307 
308 		code = gs_param_list_unserialize(typed.value.d.list, buf);
309 		temp_code = param_end_write_dict(list, key, &typed.value.d);
310 		if (code >= 0) {
311 		    buf += code;
312 		    code = temp_code;
313 		}
314 		break;
315 
316 	    default:
317 		code = gs_note_error(gs_error_unknownerror);
318 		break;
319 	}
320 	if (code < 0)
321 	    break;
322 	if (typed.type != gs_param_type_dict && typed.type != gs_param_type_dict_int_keys)
323 	    code = param_write_typed(list, key, &typed);
324     }
325     while (code >= 0);
326 
327     return code >= 0 ? buf - orig_buf : code;
328 }
329 
330 
331 /* ---------- Utility functions -------- */
332 
333 /* Align a byte pointer on the next Nth byte */
334 private void
ptr_align_to(const byte ** src,unsigned alignment)335 ptr_align_to(
336 	    const byte ** src,	/* pointer to align */
337 	    unsigned alignment	/* alignment, must be power of 2 */
338 )
339 {
340     *src += -(int)ALIGNMENT_MOD(*src, alignment) & (alignment - 1);
341 }
342 
343 /* Put compressed word repr to a buffer */
344 private void
wb_put_word(unsigned source,WriteBuffer * dest)345 wb_put_word(
346 	    unsigned source,	/* number to put to buffer */
347 	    WriteBuffer * dest	/* destination descriptor */
348 )
349 {
350     do {
351 	byte chunk = source & 0x7f;
352 
353 	if (source >= 0x80)
354 	    chunk |= 0x80;
355 	source >>= 7;
356 	++dest->total_sizeof;
357 	if (dest->buf && dest->buf < dest->buf_end)
358 	    *dest->buf++ = chunk;
359     }
360     while (source != 0);
361 }
362 
363 /* Put array of bytes to buffer */
364 private void
wb_put_bytes(const byte * source,unsigned source_sizeof,WriteBuffer * dest)365 wb_put_bytes(
366 	     const byte * source,	/* bytes to put to buffer */
367 	     unsigned source_sizeof,	/* # bytes to put */
368 	     WriteBuffer * dest	/* destination descriptor */
369 )
370 {
371     dest->total_sizeof += source_sizeof;
372     if (dest->buf && dest->buf + source_sizeof <= dest->buf_end) {
373 	if (dest->buf != source)
374 	    memcpy(dest->buf, source, source_sizeof);
375 	dest->buf += source_sizeof;
376     }
377 }
378 
379 /* Pad destination out to req'd alignment w/zeros */
380 private void
wb_put_alignment(unsigned alignment,WriteBuffer * dest)381 wb_put_alignment(
382 		 unsigned alignment,	/* alignment to match, must be power 2 */
383 		 WriteBuffer * dest	/* destination descriptor */
384 )
385 {
386     static const byte zero =
387     {0};
388 
389     while ((dest->total_sizeof & (alignment - 1)) != 0)
390 	wb_put_bytes(&zero, 1, dest);
391 }
392 
393 /* Get word compressed with wb_put_word */
394 private unsigned		/* decompressed word */
buf_get_word(const byte ** src)395 buf_get_word(
396 	    const byte ** src	/* UPDATES: ptr to src buf ptr */
397 )
398 {
399     unsigned dest = 0;
400     byte chunk;
401     unsigned shift = 0;
402 
403     do {
404 	chunk = *(*src)++;
405 	dest |= (chunk & 0x7f) << shift;
406 	shift += 7;
407     }
408     while (chunk & 0x80);
409 
410     return dest;
411 }
412