summaryrefslogtreecommitdiff
path: root/base/gsparams.c
blob: 897ca1e0fe6aec89c7a781661be753b80a68a934 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
/* Copyright (C) 2001-2023 Artifex Software, Inc.
   All Rights Reserved.

   This software is provided AS-IS with no warranty, either express or
   implied.

   This software is distributed under license and may not be copied,
   modified or distributed except as expressly authorized under the terms
   of the license contained in the file LICENSE in this distribution.

   Refer to licensing information at http://www.artifex.com or contact
   Artifex Software, Inc.,  39 Mesa Street, Suite 108A, San Francisco,
   CA 94129, USA, for further information.
*/


/* Generic parameter list serializer & expander */

/* Initial version 2/1/98 by John Desrosiers (soho@crl.com) */
/* 11/16/98 L. Peter Deutsch (ghost@aladdin.com) edited to remove names
   put_bytes, put_word which conflicted with other modules */

#include "gx.h"
#include "memory_.h"
#include "gserrors.h"
#include "gsparams.h"

/* ----------- Local Type Decl's ------------ */
typedef struct {
    byte *buf;			/* current buffer ptr */
    byte *buf_end;		/* end of buffer */
    unsigned total_sizeof;	/* current # bytes in buf */
} WriteBuffer;

/* ---------- Forward refs ----------- */
static void
ptr_align_to(
            const byte ** src,	/* pointer to align */
            unsigned alignment	/* alignment, must be power of 2 */
            );
static void
wb_put_word(
            unsigned source,	/* number to put to buffer */
            WriteBuffer * dest	/* destination descriptor */
            );
static void
wb_put_bytes(
             const byte * source,	/* bytes to put to buffer */
             unsigned source_sizeof,	/* # bytes to put */
             WriteBuffer * dest		/* destination descriptor */
             );
static void
wb_put_alignment(
                 unsigned alignment,	/* alignment to match, must be power 2 */
                 WriteBuffer * dest	/* destination descriptor */
                 );

/* Get word compressed with wb_put_word */
static unsigned		/* decompressed word */
buf_get_word(
            const byte ** src	/* UPDATES: ptr to src buf ptr */
            );

/* ------------ Serializer ------------ */
/* Serialize the contents of a gs_param_list (including sub-dicts) */
int				/* ret -ve err, else # bytes needed to represent param list, whether */

/* or not it actually fit into buffer. List was successully */

/* serialized only if if this # is <= supplied buf size. */
gs_param_list_serialize(
                           gs_param_list * list,	/* root of list to serialize */
                                        /* list MUST BE IN READ MODE */
                           byte * buf,	/* destination buffer (can be 0) */
                           int buf_sizeof	/* # bytes available in buf (can be 0) */
)
{
    int code = 0;
    int temp_code;
    gs_param_enumerator_t key_enum;
    gs_param_key_t key;
    WriteBuffer write_buf;

    write_buf.buf = buf;
    write_buf.buf_end = buf + (buf ? buf_sizeof : 0);
    write_buf.total_sizeof = 0;
    param_init_enumerator(&key_enum);

    /* Each item is serialized as ("word" means compressed word):
     *  word: key sizeof + 1, or 0 if end of list/dict
     *  word: data type(gs_param_type_xxx)
     *  byte[]: key, including trailing \0
     *  (if simple type)
     *   byte[]: unpacked representation of data
     *  (if simple array or string)
     *   byte[]: unpacked mem image of gs_param_xxx_array structure
     *   pad: to array alignment
     *   byte[]: data associated with array contents
     *  (if string/name array)
     *   byte[]: unpacked mem image of gs_param_string_array structure
     *   pad: to void *
     *   { gs_param_string structure mem image;
     *     data associated with string;
     *   } for each string in array
     *  (if dict/dict_int_keys)
     *   word: # of entries in dict,
     *   pad: to void *
     *   dict entries follow immediately until end-of-dict
     *
     * NB that this format is designed to allow using an input buffer
     * as the direct source of data when expanding a gs_c_param_list
     */
    /* Enumerate all the keys; use keys to get their typed values */
    while ((code = param_get_next_key(list, &key_enum, &key)) == 0) {
        int value_top_sizeof;
        int value_base_sizeof;

        /* Get next datum & put its type & key to buffer */
        gs_param_typed_value value;
        char string_key[256];

        if (sizeof(string_key) < key.size + 1) {
            code = gs_note_error(gs_error_rangecheck);
            break;
        }
        memcpy(string_key, key.data, key.size);
        string_key[key.size] = 0;
        if ((code = param_read_typed(list, string_key, &value)) != 0) {
            code = code > 0 ? gs_note_error(gs_error_unknownerror) : code;
            break;
        }
        wb_put_word((unsigned)key.size + 1, &write_buf);
        wb_put_word((unsigned)value.type, &write_buf);
        wb_put_bytes((byte *) string_key, key.size + 1, &write_buf);

        /* Put value & its size to buffer */
        value_top_sizeof = gs_param_type_sizes[value.type];
        value_base_sizeof = gs_param_type_base_sizes[value.type];
        switch (value.type) {
            case gs_param_type_null:
            case gs_param_type_bool:
            case gs_param_type_int:
            case gs_param_type_long:
            case gs_param_type_size_t:
            case gs_param_type_i64:
            case gs_param_type_float:
                wb_put_bytes((byte *) & value.value, value_top_sizeof, &write_buf);
                break;

            case gs_param_type_string:
            case gs_param_type_name:
            case gs_param_type_int_array:
            case gs_param_type_float_array:
                wb_put_bytes((byte *) & value.value, value_top_sizeof, &write_buf);
                wb_put_alignment(value_base_sizeof, &write_buf);
                value_base_sizeof *= value.value.s.size;
                wb_put_bytes(value.value.s.data, value_base_sizeof, &write_buf);
                break;

            case gs_param_type_string_array:
            case gs_param_type_name_array:
                value_base_sizeof *= value.value.sa.size;
                wb_put_bytes((const byte *)&value.value, value_top_sizeof, &write_buf);
                wb_put_alignment(sizeof(void *), &write_buf);

                wb_put_bytes((const byte *)value.value.sa.data, value_base_sizeof,
                          &write_buf);
                {
                    int str_count;
                    const gs_param_string *sa;

                    for (str_count = value.value.sa.size,
                         sa = value.value.sa.data; str_count-- > 0; ++sa)
                        wb_put_bytes(sa->data, sa->size, &write_buf);
                }
                break;

            case gs_param_type_dict:
            case gs_param_type_dict_int_keys:
                wb_put_word(value.value.d.size, &write_buf);
                wb_put_alignment(sizeof(void *), &write_buf);

                {
                    int bytes_written =
                    gs_param_list_serialize(value.value.d.list,
                                            write_buf.buf,
                     write_buf.buf ? write_buf.buf_end - write_buf.buf : 0);

                    temp_code = param_end_read_dict(list,
                                                    (const char *)key.data,
                                                    &value.value.d);
                    if (bytes_written < 0)
                        code = bytes_written;
                    else {
                        code = temp_code;
                        if (bytes_written)
                            wb_put_bytes(write_buf.buf, bytes_written, &write_buf);
                    }
                }
                break;

            default:
                code = gs_note_error(gs_error_unknownerror);
                break;
        }
        if (code < 0)
            break;
    }

    /* Write end marker, which is an (illegal) 0 key length */
    if (code >= 0) {
        wb_put_word(0, &write_buf);
        code = write_buf.total_sizeof;
    }
    return code;
}

/* ------------ Expander --------------- */
/* Expand a buffer into a gs_param_list (including sub-dicts) */
int				/* ret -ve err, +ve # of chars read from buffer */
gs_param_list_unserialize(
                             gs_param_list * list,	/* root of list to expand to */
                                        /* list MUST BE IN WRITE MODE */
                             const byte * buf	/* source buffer */
)
{
    int code = 0;
    const byte *orig_buf = buf;

    do {
        gs_param_typed_value typed;
        gs_param_name key;
        unsigned key_sizeof;
        int value_top_sizeof;
        int value_base_sizeof;
        int temp_code;
        gs_param_type type;

        /* key length, 0 indicates end of data */
        key_sizeof = buf_get_word(&buf);
        if (key_sizeof == 0)	/* end of data */
            break;

        /* data type */
        type = (gs_param_type) buf_get_word(&buf);

        /* key */
        key = (gs_param_name) buf;
        buf += key_sizeof;

        /* Data values */
        value_top_sizeof = gs_param_type_sizes[type];
        value_base_sizeof = gs_param_type_base_sizes[type];
        typed.type = type;
        if (type != gs_param_type_dict && type != gs_param_type_dict_int_keys) {
            memcpy(&typed.value, buf, value_top_sizeof);
            buf += value_top_sizeof;
        }
        switch (type) {
            case gs_param_type_null:
            case gs_param_type_bool:
            case gs_param_type_int:
            case gs_param_type_long:
            case gs_param_type_size_t:
            case gs_param_type_i64:
            case gs_param_type_float:
                break;

            case gs_param_type_string:
            case gs_param_type_name:
            case gs_param_type_int_array:
            case gs_param_type_float_array:
                ptr_align_to(&buf, value_base_sizeof);
                typed.value.s.data = buf;
                typed.value.s.persistent = false;
                buf += typed.value.s.size * value_base_sizeof;
                break;

            case gs_param_type_string_array:
            case gs_param_type_name_array:
                ptr_align_to(&buf, sizeof(void *));

                typed.value.sa.data = (const gs_param_string *)buf;
                typed.value.sa.persistent = false;
                buf += typed.value.s.size * value_base_sizeof;
                {
                    int str_count;
                    gs_param_string *sa;

                    for (str_count = typed.value.sa.size,
                         sa = (gs_param_string *) typed.value.sa.data;
                         str_count-- > 0; ++sa) {
                        sa->data = buf;
                        sa->persistent = false;
                        buf += sa->size;
                    }
                }
                break;

            case gs_param_type_dict:
            case gs_param_type_dict_int_keys:
                typed.value.d.size = buf_get_word(&buf);
                code = param_begin_write_dict
                    (list, key, &typed.value.d, type == gs_param_type_dict_int_keys);
                if (code < 0)
                    break;
                ptr_align_to(&buf, sizeof(void *));

                code = gs_param_list_unserialize(typed.value.d.list, buf);
                temp_code = param_end_write_dict(list, key, &typed.value.d);
                if (code >= 0) {
                    buf += code;
                    code = temp_code;
                }
                break;

            default:
                code = gs_note_error(gs_error_unknownerror);
                break;
        }
        if (code < 0)
            break;
        if (typed.type != gs_param_type_dict && typed.type != gs_param_type_dict_int_keys)
            code = param_write_typed(list, key, &typed);
    }
    while (code >= 0);

    return code >= 0 ? buf - orig_buf : code;
}

/* ---------- Utility functions -------- */

/* Align a byte pointer on the next Nth byte */
static void
ptr_align_to(
            const byte ** src,	/* pointer to align */
            unsigned alignment	/* alignment, must be power of 2 */
)
{
    *src += -(int)ALIGNMENT_MOD(*src, alignment) & (alignment - 1);
}

/* Put compressed word repr to a buffer */
static void
wb_put_word(
            unsigned source,	/* number to put to buffer */
            WriteBuffer * dest	/* destination descriptor */
)
{
    do {
        byte chunk = source & 0x7f;

        if (source >= 0x80)
            chunk |= 0x80;
        source >>= 7;
        ++dest->total_sizeof;
        if (dest->buf && dest->buf < dest->buf_end)
            *dest->buf++ = chunk;
    }
    while (source != 0);
}

/* Put array of bytes to buffer */
static void
wb_put_bytes(
             const byte * source,	/* bytes to put to buffer */
             unsigned source_sizeof,	/* # bytes to put */
             WriteBuffer * dest	/* destination descriptor */
)
{
    dest->total_sizeof += source_sizeof;
    if (dest->buf && dest->buf + source_sizeof <= dest->buf_end) {
        if (dest->buf != source)
            memcpy(dest->buf, source, source_sizeof);
        dest->buf += source_sizeof;
    }
}

/* Pad destination out to req'd alignment w/zeros */
static void
wb_put_alignment(
                 unsigned alignment,	/* alignment to match, must be power 2 */
                 WriteBuffer * dest	/* destination descriptor */
)
{
    static const byte zero =
    {0};

    while ((dest->total_sizeof & (alignment - 1)) != 0)
        wb_put_bytes(&zero, 1, dest);
}

/* Get word compressed with wb_put_word */
static unsigned		/* decompressed word */
buf_get_word(
            const byte ** src	/* UPDATES: ptr to src buf ptr */
)
{
    unsigned dest = 0;
    byte chunk;
    unsigned shift = 0;

    do {
        chunk = *(*src)++;
        dest |= (chunk & 0x7f) << shift;
        shift += 7;
    }
    while (chunk & 0x80);

    return dest;
}