1 /*
2 * Copyright © 2008 Chris Wilson <chris@chris-wilson.co.uk>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it either under the terms of the GNU Lesser General Public
6 * License version 2.1 as published by the Free Software Foundation
7 * (the "LGPL") or, at your option, under the terms of the Mozilla
8 * Public License Version 1.1 (the "MPL"). If you do not alter this
9 * notice, a recipient may use your version of this file under either
10 * the MPL or the LGPL.
11 *
12 * You should have received a copy of the LGPL along with this library
13 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
15 * You should have received a copy of the MPL along with this library
16 * in the file COPYING-MPL-1.1
17 *
18 * The contents of this file are subject to the Mozilla Public License
19 * Version 1.1 (the "License"); you may not use this file except in
20 * compliance with the License. You may obtain a copy of the License at
21 * http://www.mozilla.org/MPL/
22 *
23 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
24 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
25 * the specific language governing rights and limitations.
26 *
27 * The Original Code is the cairo graphics library.
28 *
29 * The Initial Developer of the Original Code is Chris Wilson.
30 *
31 * Contributor(s):
32 * Chris Wilson <chris@chris-wilson.co.uk>
33 */
34
35 #include "config.h"
36
37 #include "cairo-script-private.h"
38
39 #include <limits.h> /* INT_MAX */
40 #include <string.h>
41
42 csi_status_t
csi_array_new(csi_t * ctx,csi_integer_t initial_size,csi_object_t * obj)43 csi_array_new (csi_t *ctx,
44 csi_integer_t initial_size,
45 csi_object_t *obj)
46
47 {
48 csi_array_t *array;
49
50 if (ctx->free_array == NULL ||
51 ctx->free_array->stack.size <= initial_size)
52 {
53 csi_status_t status;
54
55 array = _csi_slab_alloc (ctx, sizeof (csi_array_t));
56 if (_csi_unlikely (array == NULL))
57 return _csi_error (CSI_STATUS_NO_MEMORY);
58
59 status = _csi_stack_init (ctx, &array->stack,
60 initial_size ? initial_size : 32);
61 if (_csi_unlikely (status)) {
62 _csi_slab_free (ctx, array, sizeof (csi_array_t));
63 return status;
64 }
65 } else {
66 array = ctx->free_array;
67 ctx->free_array = NULL;
68 }
69
70 array->base.type = CSI_OBJECT_TYPE_ARRAY;
71 array->base.ref = 1;
72
73 obj->type = CSI_OBJECT_TYPE_ARRAY;
74 obj->datum.array = array;
75
76 return CSI_STATUS_SUCCESS;
77 }
78
79 csi_status_t
csi_array_put(csi_t * ctx,csi_array_t * array,csi_integer_t elem,csi_object_t * value)80 csi_array_put (csi_t *ctx,
81 csi_array_t *array,
82 csi_integer_t elem,
83 csi_object_t *value)
84 {
85 if (_csi_unlikely (elem < 0))
86 return _csi_error (CSI_STATUS_INVALID_SCRIPT);
87
88 if (_csi_unlikely (elem >= array->stack.len)) {
89 csi_status_t status;
90
91 status = _csi_stack_grow (ctx, &array->stack, elem + 1);
92 if (_csi_unlikely (status))
93 return status;
94
95 memset (array->stack.objects + array->stack.len,
96 0, (elem - array->stack.len + 1) * sizeof (csi_object_t));
97 array->stack.len = elem + 1;
98 }
99
100 csi_object_free (ctx, &array->stack.objects[elem]);
101 array->stack.objects[elem] = *csi_object_reference (value);
102
103 return CSI_STATUS_SUCCESS;
104 }
105
106 csi_status_t
csi_array_get(csi_t * ctx,csi_array_t * array,csi_integer_t elem,csi_object_t * value)107 csi_array_get (csi_t *ctx,
108 csi_array_t *array,
109 csi_integer_t elem,
110 csi_object_t *value)
111 {
112 if (_csi_unlikely (elem < 0))
113 return _csi_error (CSI_STATUS_INVALID_SCRIPT);
114
115 if (_csi_unlikely (elem > array->stack.len))
116 return _csi_error (CSI_STATUS_INVALID_SCRIPT);
117
118 *value = array->stack.objects[elem];
119 return CSI_STATUS_SUCCESS;
120 }
121
122 csi_status_t
csi_array_append(csi_t * ctx,csi_array_t * array,csi_object_t * obj)123 csi_array_append (csi_t *ctx,
124 csi_array_t *array,
125 csi_object_t *obj)
126 {
127 return _csi_stack_push (ctx, &array->stack, csi_object_reference (obj));
128 }
129
130 inline csi_status_t
_csi_array_execute(csi_t * ctx,csi_array_t * array)131 _csi_array_execute (csi_t *ctx, csi_array_t *array)
132 {
133 csi_integer_t i;
134 csi_status_t status;
135
136 if (_csi_unlikely (array->stack.len == 0))
137 return CSI_STATUS_SUCCESS;
138
139 for (i = 0; i < array->stack.len; i++) {
140 csi_object_t *obj = &array->stack.objects[i];
141
142 if (obj->type & CSI_OBJECT_ATTR_EXECUTABLE) {
143 if (obj->type == (CSI_OBJECT_TYPE_ARRAY |
144 CSI_OBJECT_ATTR_EXECUTABLE))
145 {
146 status = _csi_push_ostack_copy (ctx, &array->stack.objects[i]);
147 }
148 else
149 status = csi_object_execute (ctx, &array->stack.objects[i]);
150 } else
151 status = _csi_push_ostack_copy (ctx, &array->stack.objects[i]);
152 if (_csi_unlikely (status))
153 return status;
154 }
155
156 return CSI_STATUS_SUCCESS;
157 }
158
159 void
csi_array_free(csi_t * ctx,csi_array_t * array)160 csi_array_free (csi_t *ctx, csi_array_t *array)
161 {
162 #if CSI_DEBUG_MALLOC
163 _csi_stack_fini (ctx, &array->stack);
164 _csi_slab_free (ctx, array, sizeof (csi_array_t));
165 #else
166 csi_integer_t n;
167
168 for (n = 0; n < array->stack.len; n++)
169 csi_object_free (ctx, &array->stack.objects[n]);
170 array->stack.len = 0;
171
172 if (ctx->free_array != NULL) {
173 if (array->stack.size > ctx->free_array->stack.size) {
174 csi_array_t *tmp = ctx->free_array;
175 ctx->free_array = array;
176 array = tmp;
177 }
178
179 _csi_stack_fini (ctx, &array->stack);
180 _csi_slab_free (ctx, array, sizeof (csi_array_t));
181 } else
182 ctx->free_array = array;
183 #endif
184 }
185
186 static cairo_bool_t
_dictionary_name_equal(const void * _a,const void * _b)187 _dictionary_name_equal (const void *_a, const void *_b)
188 {
189 return TRUE;
190 }
191
192 csi_status_t
csi_dictionary_new(csi_t * ctx,csi_object_t * obj)193 csi_dictionary_new (csi_t *ctx,
194 csi_object_t *obj)
195
196 {
197 csi_dictionary_t *dict;
198
199 if (ctx->free_dictionary != NULL) {
200 dict = ctx->free_dictionary;
201 ctx->free_dictionary = NULL;
202 } else {
203 csi_status_t status;
204
205 dict = _csi_slab_alloc (ctx, sizeof (csi_dictionary_t));
206 if (_csi_unlikely (dict == NULL))
207 return _csi_error (CSI_STATUS_NO_MEMORY);
208
209 status = _csi_hash_table_init (&dict->hash_table,
210 _dictionary_name_equal);
211 if (_csi_unlikely (status)) {
212 _csi_slab_free (ctx, dict, sizeof (csi_dictionary_t));
213 return status;
214 }
215 }
216
217 dict->base.type = CSI_OBJECT_TYPE_DICTIONARY;
218 dict->base.ref = 1;
219
220 obj->type = CSI_OBJECT_TYPE_DICTIONARY;
221 obj->datum.dictionary = dict;
222
223 return CSI_STATUS_SUCCESS;
224 }
225
226 struct _dictionary_entry_pluck {
227 csi_t *ctx;
228 csi_hash_table_t *hash_table;
229 };
230
231 static void
_dictionary_entry_pluck(void * entry,void * data)232 _dictionary_entry_pluck (void *entry, void *data)
233 {
234 csi_dictionary_entry_t *dict_entry;
235 struct _dictionary_entry_pluck *pluck_data;
236
237 dict_entry = entry;
238 pluck_data = data;
239
240 _csi_hash_table_remove (pluck_data->hash_table, entry);
241 csi_object_free (pluck_data->ctx, &dict_entry->value);
242 _csi_slab_free (pluck_data->ctx, entry, sizeof (csi_dictionary_entry_t));
243 }
244
245 void
csi_dictionary_free(csi_t * ctx,csi_dictionary_t * dict)246 csi_dictionary_free (csi_t *ctx,
247 csi_dictionary_t *dict)
248 {
249 struct _dictionary_entry_pluck data;
250
251 data.ctx = ctx;
252 data.hash_table = &dict->hash_table;
253 _csi_hash_table_foreach (&dict->hash_table,
254 _dictionary_entry_pluck,
255 &data);
256
257 #if CSI_DEBUG_MALLOC
258 _csi_hash_table_fini (&dict->hash_table);
259 _csi_slab_free (ctx, dict, sizeof (csi_dictionary_t));
260 #else
261 if (ctx->free_dictionary != NULL) {
262 _csi_hash_table_fini (&dict->hash_table);
263 _csi_slab_free (ctx, dict, sizeof (csi_dictionary_t));
264 } else
265 ctx->free_dictionary = dict;
266 #endif
267 }
268
269 csi_status_t
csi_dictionary_put(csi_t * ctx,csi_dictionary_t * dict,csi_name_t name,csi_object_t * value)270 csi_dictionary_put (csi_t *ctx,
271 csi_dictionary_t *dict,
272 csi_name_t name,
273 csi_object_t *value)
274 {
275 csi_dictionary_entry_t *entry;
276 csi_status_t status;
277
278 entry = _csi_hash_table_lookup (&dict->hash_table,
279 (csi_hash_entry_t *) &name);
280 if (entry != NULL) {
281 /* replace the existing entry */
282 csi_object_free (ctx, &entry->value);
283 entry->value = *csi_object_reference (value);
284 return CSI_STATUS_SUCCESS;
285 }
286
287 entry = _csi_slab_alloc (ctx, sizeof (*entry));
288 if (_csi_unlikely (entry == NULL))
289 return _csi_error (CSI_STATUS_NO_MEMORY);
290
291 entry->hash_entry.hash = name;
292 status = _csi_hash_table_insert (&dict->hash_table, &entry->hash_entry);
293 if (_csi_unlikely (status)) {
294 _csi_slab_free (ctx, entry, sizeof (*entry));
295 return status;
296 }
297
298 entry->value = *csi_object_reference (value);
299
300 return CSI_STATUS_SUCCESS;
301 }
302
303 csi_status_t
csi_dictionary_get(csi_t * ctx,csi_dictionary_t * dict,csi_name_t name,csi_object_t * value)304 csi_dictionary_get (csi_t *ctx,
305 csi_dictionary_t *dict,
306 csi_name_t name,
307 csi_object_t *value)
308 {
309 csi_dictionary_entry_t *entry;
310
311 entry = _csi_hash_table_lookup (&dict->hash_table,
312 (csi_hash_entry_t *) &name);
313 if (_csi_unlikely (entry == NULL))
314 return _csi_error (CSI_STATUS_INVALID_SCRIPT);
315
316 *value = entry->value;
317 return CSI_STATUS_SUCCESS;
318 }
319
320 csi_boolean_t
csi_dictionary_has(csi_dictionary_t * dict,csi_name_t name)321 csi_dictionary_has (csi_dictionary_t *dict,
322 csi_name_t name)
323 {
324 return _csi_hash_table_lookup (&dict->hash_table,
325 (csi_hash_entry_t *) &name) != NULL;
326 }
327
328 void
csi_dictionary_remove(csi_t * ctx,csi_dictionary_t * dict,csi_name_t name)329 csi_dictionary_remove (csi_t *ctx,
330 csi_dictionary_t *dict,
331 csi_name_t name)
332 {
333 csi_dictionary_entry_t *entry;
334
335 entry = _csi_hash_table_lookup (&dict->hash_table,
336 (csi_hash_entry_t *) &name);
337 if (entry != NULL) {
338 _csi_hash_table_remove (&dict->hash_table, &entry->hash_entry);
339 csi_object_free (ctx, &entry->value);
340 _csi_slab_free (ctx, entry, sizeof (csi_dictionary_entry_t));
341 }
342 }
343
344 csi_status_t
csi_matrix_new(csi_t * ctx,csi_object_t * obj)345 csi_matrix_new (csi_t *ctx,
346 csi_object_t *obj)
347 {
348 csi_matrix_t *matrix;
349
350 matrix = _csi_slab_alloc (ctx, sizeof (csi_matrix_t));
351 if (_csi_unlikely (matrix == NULL))
352 return _csi_error (CSI_STATUS_NO_MEMORY);
353
354 matrix->base.type = CSI_OBJECT_TYPE_MATRIX;
355 matrix->base.ref = 1;
356 cairo_matrix_init_identity (&matrix->matrix);
357
358 obj->type = CSI_OBJECT_TYPE_MATRIX;
359 obj->datum.matrix = matrix;
360
361 return CSI_STATUS_SUCCESS;
362 }
363
364 csi_status_t
csi_matrix_new_from_array(csi_t * ctx,csi_object_t * obj,csi_array_t * array)365 csi_matrix_new_from_array (csi_t *ctx,
366 csi_object_t *obj,
367 csi_array_t *array)
368 {
369 csi_matrix_t *matrix;
370
371 if (_csi_unlikely (array->stack.len != 6))
372 return _csi_error (CSI_STATUS_INVALID_SCRIPT);
373
374 matrix = _csi_slab_alloc (ctx, sizeof (csi_matrix_t));
375 if (_csi_unlikely (matrix == NULL))
376 return _csi_error (CSI_STATUS_NO_MEMORY);
377
378 matrix->base.type = CSI_OBJECT_TYPE_MATRIX;
379 matrix->base.ref = 1;
380 cairo_matrix_init (&matrix->matrix,
381 csi_number_get_value (&array->stack.objects[0]),
382 csi_number_get_value (&array->stack.objects[1]),
383 csi_number_get_value (&array->stack.objects[2]),
384 csi_number_get_value (&array->stack.objects[3]),
385 csi_number_get_value (&array->stack.objects[4]),
386 csi_number_get_value (&array->stack.objects[5]));
387
388 obj->type = CSI_OBJECT_TYPE_MATRIX;
389 obj->datum.matrix = matrix;
390
391 return CSI_STATUS_SUCCESS;
392 }
393
394 csi_status_t
csi_matrix_new_from_matrix(csi_t * ctx,csi_object_t * obj,const cairo_matrix_t * m)395 csi_matrix_new_from_matrix (csi_t *ctx,
396 csi_object_t *obj,
397 const cairo_matrix_t *m)
398 {
399 csi_matrix_t *matrix;
400
401 matrix = _csi_slab_alloc (ctx, sizeof (csi_matrix_t));
402 if (_csi_unlikely (matrix == NULL))
403 return _csi_error (CSI_STATUS_NO_MEMORY);
404
405 matrix->base.type = CSI_OBJECT_TYPE_MATRIX;
406 matrix->base.ref = 1;
407 matrix->matrix = *m;
408
409 obj->type = CSI_OBJECT_TYPE_MATRIX;
410 obj->datum.matrix = matrix;
411
412 return CSI_STATUS_SUCCESS;
413 }
414
415 csi_status_t
csi_matrix_new_from_values(csi_t * ctx,csi_object_t * obj,double v[6])416 csi_matrix_new_from_values (csi_t *ctx,
417 csi_object_t *obj,
418 double v[6])
419 {
420 csi_matrix_t *matrix;
421
422 matrix = _csi_slab_alloc (ctx, sizeof (csi_matrix_t));
423 if (_csi_unlikely (matrix == NULL))
424 return _csi_error (CSI_STATUS_NO_MEMORY);
425
426 matrix->base.type = CSI_OBJECT_TYPE_MATRIX;
427 matrix->base.ref = 1;
428 cairo_matrix_init (&matrix->matrix, v[0], v[1], v[2], v[3], v[4], v[5]);
429
430 obj->type = CSI_OBJECT_TYPE_MATRIX;
431 obj->datum.matrix = matrix;
432
433 return CSI_STATUS_SUCCESS;
434 }
435
436 void
csi_matrix_free(csi_t * ctx,csi_matrix_t * obj)437 csi_matrix_free (csi_t *ctx,
438 csi_matrix_t *obj)
439 {
440 _csi_slab_free (ctx, obj, sizeof (csi_matrix_t));
441 }
442
443
444 csi_status_t
csi_name_new(csi_t * ctx,csi_object_t * obj,const char * str,int len)445 csi_name_new (csi_t *ctx,
446 csi_object_t *obj,
447 const char *str,
448 int len)
449 {
450 csi_status_t status;
451
452 status = _csi_intern_string (ctx, &str, len);
453 if (_csi_unlikely (status))
454 return status;
455
456 obj->type = CSI_OBJECT_TYPE_NAME;
457 obj->datum.name = (csi_name_t) str;
458
459 return CSI_STATUS_SUCCESS;
460 }
461
462 csi_status_t
csi_name_new_static(csi_t * ctx,csi_object_t * obj,const char * str)463 csi_name_new_static (csi_t *ctx,
464 csi_object_t *obj,
465 const char *str)
466 {
467 csi_status_t status;
468
469 status = _csi_intern_string (ctx, &str, strlen (str));
470 if (_csi_unlikely (status))
471 return status;
472
473 obj->type = CSI_OBJECT_TYPE_NAME;
474 obj->datum.name = (csi_name_t) str;
475
476 return CSI_STATUS_SUCCESS;
477 }
478
479 csi_status_t
csi_string_new(csi_t * ctx,csi_object_t * obj,const char * str,int len)480 csi_string_new (csi_t *ctx,
481 csi_object_t *obj,
482 const char *str,
483 int len)
484 {
485 csi_string_t *string;
486
487 if (len < 0)
488 len = strlen (str);
489 if (_csi_unlikely (len >= INT_MAX))
490 return _csi_error (CSI_STATUS_NO_MEMORY);
491
492 if (ctx->free_string == NULL || ctx->free_string->len <= len) {
493 string = _csi_slab_alloc (ctx, sizeof (csi_string_t));
494 if (_csi_unlikely (string == NULL))
495 return _csi_error (CSI_STATUS_NO_MEMORY);
496
497 string->string = _csi_alloc (ctx, len + 1);
498 if (_csi_unlikely (string->string == NULL)) {
499 _csi_slab_free (ctx, string, sizeof (csi_string_t));
500 return _csi_error (CSI_STATUS_NO_MEMORY);
501 }
502 } else {
503 string = ctx->free_string;
504 ctx->free_string = NULL;
505 }
506
507 if (str != NULL) {
508 memcpy (string->string, str, len);
509 string->string[len] = '\0';
510 }
511 string->len = len;
512 string->deflate = 0;
513 string->method = NONE;
514
515 string->base.type = CSI_OBJECT_TYPE_STRING;
516 string->base.ref = 1;
517
518 obj->type = CSI_OBJECT_TYPE_STRING;
519 obj->datum.string = string;
520
521 return CSI_STATUS_SUCCESS;
522 }
523
524 csi_status_t
csi_string_deflate_new(csi_t * ctx,csi_object_t * obj,void * bytes,int in_len,int out_len)525 csi_string_deflate_new (csi_t *ctx,
526 csi_object_t *obj,
527 void *bytes,
528 int in_len,
529 int out_len)
530 {
531 csi_status_t status;
532 csi_string_t *string;
533
534 status = csi_string_new (ctx, obj, bytes, in_len);
535 if (_csi_unlikely (status))
536 return status;
537
538 string = obj->datum.string;
539 string->deflate = out_len;
540 string->method = ZLIB;
541
542 return CSI_STATUS_SUCCESS;
543 }
544
545 csi_status_t
csi_string_new_from_bytes(csi_t * ctx,csi_object_t * obj,char * bytes,unsigned int len)546 csi_string_new_from_bytes (csi_t *ctx,
547 csi_object_t *obj,
548 char *bytes,
549 unsigned int len)
550 {
551 csi_string_t *string;
552
553 if (_csi_unlikely (len >= INT_MAX))
554 return _csi_error (CSI_STATUS_NO_MEMORY);
555
556 string = _csi_slab_alloc (ctx, sizeof (csi_string_t));
557 if (_csi_unlikely (string == NULL))
558 return _csi_error (CSI_STATUS_NO_MEMORY);
559
560 string->string = bytes;
561 string->len = len;
562 string->deflate = 0;
563 string->method = NONE;
564
565 string->base.type = CSI_OBJECT_TYPE_STRING;
566 string->base.ref = 1;
567
568 obj->type = CSI_OBJECT_TYPE_STRING;
569 obj->datum.string = string;
570
571 return CSI_STATUS_SUCCESS;
572 }
573
574 static inline csi_status_t
_csi_string_execute(csi_t * ctx,csi_string_t * string)575 _csi_string_execute (csi_t *ctx, csi_string_t *string)
576 {
577 csi_status_t status;
578 csi_object_t obj;
579
580 if (_csi_unlikely (string->len == 0))
581 return CSI_STATUS_SUCCESS;
582
583 status = csi_file_new_for_bytes (ctx, &obj, string->string, string->len);
584 if (_csi_unlikely (status))
585 return status;
586
587 status = _csi_scan_file (ctx, obj.datum.file);
588 csi_object_free (ctx, &obj);
589
590 return status;
591 }
592
593 void
csi_string_free(csi_t * ctx,csi_string_t * string)594 csi_string_free (csi_t *ctx, csi_string_t *string)
595 {
596 #if CSI_DEBUG_MALLOC
597 _csi_free (ctx, string->string);
598 _csi_slab_free (ctx, string, sizeof (csi_string_t));
599 #else
600 if (ctx->free_string != NULL) {
601 if (string->len > ctx->free_string->len) {
602 csi_string_t *tmp = ctx->free_string;
603 ctx->free_string = string;
604 string = tmp;
605 }
606
607 _csi_free (ctx, string->string);
608 _csi_slab_free (ctx, string, sizeof (csi_string_t));
609 } else
610 ctx->free_string = string;
611 #endif
612 }
613
614 csi_status_t
csi_object_execute(csi_t * ctx,csi_object_t * obj)615 csi_object_execute (csi_t *ctx, csi_object_t *obj)
616 {
617 csi_status_t status;
618 csi_object_t indirect;
619
620 INDIRECT:
621 switch (obj->type & CSI_OBJECT_TYPE_MASK) {
622 case CSI_OBJECT_TYPE_NAME:
623 status = _csi_name_lookup (ctx, obj->datum.name, &indirect);
624 if (_csi_unlikely (status))
625 return status;
626 if (indirect.type & CSI_OBJECT_ATTR_EXECUTABLE) {
627 obj = &indirect;
628 goto INDIRECT;
629 } else
630 return _csi_push_ostack_copy (ctx, &indirect);
631
632 case CSI_OBJECT_TYPE_OPERATOR:
633 return obj->datum.op (ctx);
634
635 case CSI_OBJECT_TYPE_ARRAY:
636 return _csi_array_execute (ctx, obj->datum.array);
637 case CSI_OBJECT_TYPE_FILE:
638 return _csi_file_execute (ctx, obj->datum.file);
639 case CSI_OBJECT_TYPE_STRING:
640 return _csi_string_execute (ctx, obj->datum.string);
641
642 default:
643 return _csi_push_ostack_copy (ctx, obj);
644 }
645 }
646
647 csi_object_t *
csi_object_reference(csi_object_t * obj)648 csi_object_reference (csi_object_t *obj)
649 {
650 if (CSI_OBJECT_IS_CAIRO (obj)) {
651 switch (obj->type & CSI_OBJECT_TYPE_MASK) {
652 case CSI_OBJECT_TYPE_CONTEXT:
653 cairo_reference (obj->datum.cr);
654 break;
655 case CSI_OBJECT_TYPE_FONT:
656 cairo_font_face_reference (obj->datum.font_face);
657 break;
658 case CSI_OBJECT_TYPE_PATTERN:
659 cairo_pattern_reference (obj->datum.pattern);
660 break;
661 case CSI_OBJECT_TYPE_SCALED_FONT:
662 cairo_scaled_font_reference (obj->datum.scaled_font);
663 break;
664 case CSI_OBJECT_TYPE_SURFACE:
665 cairo_surface_reference (obj->datum.surface);
666 break;
667 }
668 } else if (CSI_OBJECT_IS_COMPOUND (obj)) {
669 obj->datum.object->ref++;
670 }
671
672 return obj;
673 }
674
675 void
csi_object_free(csi_t * ctx,csi_object_t * obj)676 csi_object_free (csi_t *ctx,
677 csi_object_t *obj)
678 {
679 if (CSI_OBJECT_IS_CAIRO (obj)) {
680 switch (obj->type & CSI_OBJECT_TYPE_MASK) {
681 case CSI_OBJECT_TYPE_CONTEXT:
682 cairo_destroy (obj->datum.cr);
683 break;
684 case CSI_OBJECT_TYPE_FONT:
685 cairo_font_face_destroy (obj->datum.font_face);
686 break;
687 case CSI_OBJECT_TYPE_PATTERN:
688 cairo_pattern_destroy (obj->datum.pattern);
689 break;
690 case CSI_OBJECT_TYPE_SCALED_FONT:
691 cairo_scaled_font_destroy (obj->datum.scaled_font);
692 break;
693 case CSI_OBJECT_TYPE_SURFACE:
694 cairo_surface_destroy (obj->datum.surface);
695 break;
696 }
697 } else if (CSI_OBJECT_IS_COMPOUND (obj)) {
698 if (--obj->datum.object->ref)
699 return;
700
701 switch (obj->type & CSI_OBJECT_TYPE_MASK) {
702 case CSI_OBJECT_TYPE_ARRAY:
703 csi_array_free (ctx, obj->datum.array);
704 break;
705 case CSI_OBJECT_TYPE_DICTIONARY:
706 csi_dictionary_free (ctx, obj->datum.dictionary);
707 break;
708 case CSI_OBJECT_TYPE_FILE:
709 _csi_file_free (ctx, obj->datum.file);
710 break;
711 case CSI_OBJECT_TYPE_MATRIX:
712 csi_matrix_free (ctx, obj->datum.matrix);
713 break;
714 case CSI_OBJECT_TYPE_STRING:
715 csi_string_free (ctx, obj->datum.string);
716 break;
717 default:
718 break;
719 }
720 }
721 }
722
723 csi_status_t
csi_object_as_file(csi_t * ctx,csi_object_t * src,csi_object_t * file)724 csi_object_as_file (csi_t *ctx,
725 csi_object_t *src,
726 csi_object_t *file)
727 {
728 int type = csi_object_get_type (src);
729 switch (type) {
730 case CSI_OBJECT_TYPE_FILE:
731 *file = *csi_object_reference (src);
732 return CSI_STATUS_SUCCESS;
733 case CSI_OBJECT_TYPE_STRING:
734 return csi_file_new_from_string (ctx, file, src->datum.string);
735 case CSI_OBJECT_TYPE_ARRAY:
736 #if 0
737 if (src->type & CSI_OBJECT_ATTR_EXECUTABLE)
738 return _csi_file_new_from_procedure (cs, src);
739 #endif
740 default:
741 return _csi_error (CSI_STATUS_INVALID_SCRIPT);
742 }
743 }
744
745 static int
lexcmp(void const * a,size_t alen,void const * b,size_t blen)746 lexcmp (void const *a, size_t alen,
747 void const *b, size_t blen)
748 {
749 size_t len = alen < blen ? alen : blen;
750 int cmp = memcmp (a, b, len);
751 if (cmp)
752 return cmp;
753 if (alen == blen)
754 return 0;
755 return alen < blen ? -1 : +1;
756 }
757
758 csi_boolean_t
csi_object_eq(csi_object_t * a,csi_object_t * b)759 csi_object_eq (csi_object_t *a,
760 csi_object_t *b)
761 {
762 csi_object_type_t atype = csi_object_get_type (a);
763 csi_object_type_t btype = csi_object_get_type (b);
764
765 if (atype == btype) {
766 switch (atype) {
767 case CSI_OBJECT_TYPE_BOOLEAN:
768 return a->datum.boolean == b->datum.boolean;
769 case CSI_OBJECT_TYPE_INTEGER:
770 return a->datum.integer == b->datum.integer;
771 case CSI_OBJECT_TYPE_REAL:
772 return a->datum.real == b->datum.real;
773 case CSI_OBJECT_TYPE_NAME:
774 return a->datum.name == b->datum.name;
775 case CSI_OBJECT_TYPE_STRING:
776 return 0 == lexcmp (a->datum.string->string,
777 a->datum.string->len,
778 b->datum.string->string,
779 b->datum.string->len);
780 case CSI_OBJECT_TYPE_NULL:
781 case CSI_OBJECT_TYPE_MARK:
782 return TRUE;
783 case CSI_OBJECT_TYPE_OPERATOR:
784 return a->datum.op == b->datum.op;
785 case CSI_OBJECT_TYPE_ARRAY:
786 case CSI_OBJECT_TYPE_DICTIONARY:
787 case CSI_OBJECT_TYPE_FILE:
788 case CSI_OBJECT_TYPE_MATRIX:
789 case CSI_OBJECT_TYPE_CONTEXT:
790 case CSI_OBJECT_TYPE_FONT:
791 case CSI_OBJECT_TYPE_PATTERN:
792 case CSI_OBJECT_TYPE_SCALED_FONT:
793 case CSI_OBJECT_TYPE_SURFACE:
794 return a->datum.ptr == b->datum.ptr;
795 }
796 }
797
798 if (atype < btype) {
799 csi_object_t *c;
800 csi_object_type_t ctype;
801 c = a; a = b; b = c;
802 ctype = atype; atype = btype; btype = ctype;
803 }
804
805 switch ((int) atype) {
806 case CSI_OBJECT_TYPE_INTEGER:
807 if (btype == CSI_OBJECT_TYPE_BOOLEAN) {
808 return a->datum.integer == b->datum.boolean;
809 }
810 break;
811 case CSI_OBJECT_TYPE_REAL:
812 if (btype == CSI_OBJECT_TYPE_INTEGER) {
813 return a->datum.real == b->datum.integer;
814 }
815 else if (btype == CSI_OBJECT_TYPE_BOOLEAN) {
816 return a->datum.real == b->datum.boolean;
817 }
818 break;
819
820 case CSI_OBJECT_TYPE_STRING:
821 if (btype == CSI_OBJECT_TYPE_NAME) {
822 const char *bstr = (const char *) b->datum.name;
823 return 0 == lexcmp (a->datum.string->string,
824 a->datum.string->len,
825 bstr,
826 strlen (bstr));
827 }
828 break;
829
830 default:
831 break;
832 }
833
834 return FALSE;
835 }
836
837 csi_status_t
csi_object_compare(csi_object_t * a,csi_object_t * b,int * out)838 csi_object_compare (csi_object_t *a,
839 csi_object_t *b,
840 int *out)
841 {
842 csi_object_type_t atype = csi_object_get_type (a);
843 csi_object_type_t btype = csi_object_get_type (b);
844 int sign;
845
846 if (csi_object_eq (a, b)){
847 *out = 0;
848 return CSI_STATUS_SUCCESS;
849 }
850
851 #define CMP(x,y) ((x) < (y) ? -1 : +1)
852
853 if (atype == btype) {
854 switch (atype) {
855 case CSI_OBJECT_TYPE_BOOLEAN:
856 *out = CMP (a->datum.boolean, b->datum.boolean);
857 return CSI_STATUS_SUCCESS;
858 case CSI_OBJECT_TYPE_INTEGER:
859 *out = CMP (a->datum.integer, b->datum.integer);
860 return CSI_STATUS_SUCCESS;
861 case CSI_OBJECT_TYPE_REAL:
862 *out = CMP (a->datum.real, b->datum.real);
863 return CSI_STATUS_SUCCESS;
864 case CSI_OBJECT_TYPE_NAME: {
865 const char *x = (char const *) a->datum.name;
866 const char *y = (char const *) b->datum.name;
867 *out = lexcmp (x, strlen(x), y, strlen (y));
868 return CSI_STATUS_SUCCESS;
869 }
870 case CSI_OBJECT_TYPE_STRING:
871 *out = lexcmp (a->datum.string->string,
872 a->datum.string->len,
873 b->datum.string->string,
874 b->datum.string->len);
875 return CSI_STATUS_SUCCESS;
876 case CSI_OBJECT_TYPE_NULL:
877 case CSI_OBJECT_TYPE_MARK:
878 case CSI_OBJECT_TYPE_OPERATOR:
879 case CSI_OBJECT_TYPE_ARRAY:
880 case CSI_OBJECT_TYPE_DICTIONARY:
881 case CSI_OBJECT_TYPE_FILE:
882 case CSI_OBJECT_TYPE_MATRIX:
883 case CSI_OBJECT_TYPE_CONTEXT:
884 case CSI_OBJECT_TYPE_FONT:
885 case CSI_OBJECT_TYPE_PATTERN:
886 case CSI_OBJECT_TYPE_SCALED_FONT:
887 case CSI_OBJECT_TYPE_SURFACE:
888 goto TYPE_CHECK_ERROR;
889 }
890 }
891
892 sign = +1;
893 if (atype < btype) {
894 csi_object_t *c;
895 csi_object_type_t ctype;
896 c = a; a = b; b = c;
897 ctype = atype; atype = btype; btype = ctype;
898 sign = -1;
899 }
900
901 switch ((int) atype) {
902 case CSI_OBJECT_TYPE_INTEGER:
903 if (btype == CSI_OBJECT_TYPE_BOOLEAN) {
904 *out = sign * CMP (a->datum.integer, !!b->datum.boolean);
905 return CSI_STATUS_SUCCESS;
906 }
907 break;
908 case CSI_OBJECT_TYPE_REAL:
909 if (btype == CSI_OBJECT_TYPE_INTEGER) {
910 *out = sign * CMP (a->datum.real, b->datum.integer);
911 return CSI_STATUS_SUCCESS;
912 }
913 else if (btype == CSI_OBJECT_TYPE_BOOLEAN) {
914 *out = sign * CMP (a->datum.real, !!b->datum.boolean);
915 return CSI_STATUS_SUCCESS;
916 }
917 break;
918
919 case CSI_OBJECT_TYPE_STRING:
920 if (btype == CSI_OBJECT_TYPE_NAME) {
921 const char *bstr = (const char *) b->datum.name;
922 *out = sign * lexcmp (a->datum.string->string,
923 a->datum.string->len,
924 bstr,
925 strlen (bstr));
926 return CSI_STATUS_SUCCESS;
927 }
928 break;
929
930 default:
931 break;
932 }
933
934 #undef CMP
935
936 TYPE_CHECK_ERROR:
937 return _csi_error (CSI_STATUS_SCRIPT_INVALID_TYPE);
938 }
939