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 #include "cairo.h"
39
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <math.h>
44 #include <assert.h>
45
46 #ifndef MAX
47 #define MAX(a,b) (((a)>=(b))?(a):(b))
48 #endif
49
50 csi_status_t
_csi_error(csi_status_t status)51 _csi_error (csi_status_t status)
52 {
53 return status;
54 }
55
56 /* XXX track global/local memory, cap etc, mark/sweep GC */
57 void *
_csi_alloc(csi_t * ctx,int size)58 _csi_alloc (csi_t *ctx, int size)
59 {
60 return malloc (size);
61 }
62
63 void *
_csi_alloc0(csi_t * ctx,int size)64 _csi_alloc0 (csi_t *ctx, int size)
65 {
66 void *ptr;
67
68 ptr = _csi_alloc (ctx, size);
69 if (_csi_likely (ptr != NULL))
70 memset (ptr, 0, size);
71
72 return ptr;
73 }
74
75 void *
_csi_realloc(csi_t * ctx,void * ptr,int size)76 _csi_realloc (csi_t *ctx, void *ptr, int size)
77 {
78 return realloc (ptr, size);
79 }
80
81 void
_csi_free(csi_t * ctx,void * ptr)82 _csi_free (csi_t *ctx, void *ptr)
83 {
84 if (_csi_unlikely (ptr == NULL))
85 return;
86
87 free (ptr);
88 }
89
90 void *
_csi_perm_alloc(csi_t * ctx,int size)91 _csi_perm_alloc (csi_t *ctx, int size)
92 {
93 csi_chunk_t *chunk;
94 void *ptr;
95
96 size = (size + sizeof (void *)-1) & -sizeof (void *);
97
98 chunk = ctx->perm_chunk;
99 if (chunk == NULL || chunk->rem < size) {
100 int chunk_size = (size + 8191) & -8192;
101 chunk = _csi_alloc (ctx, sizeof (csi_chunk_t) + chunk_size);
102 if (_csi_unlikely (chunk == NULL))
103 return NULL;
104
105 chunk->rem = chunk_size;
106 chunk->ptr = (char *) (chunk + 1);
107 chunk->next = ctx->perm_chunk;
108 ctx->perm_chunk = chunk;
109 }
110
111 ptr = chunk->ptr;
112 chunk->ptr += size;
113 chunk->rem -= size;
114
115 return ptr;
116 }
117
118 void *
_csi_slab_alloc(csi_t * ctx,int size)119 _csi_slab_alloc (csi_t *ctx, int size)
120 {
121 #if CSI_DEBUG_MALLOC
122 return malloc (size);
123 #else
124 int chunk_size;
125 csi_chunk_t *chunk;
126 void *ptr;
127
128 chunk_size = 2 * sizeof (void *);
129 chunk_size = (size + chunk_size - 1) / chunk_size;
130
131 if (ctx->slabs[chunk_size].free_list) {
132 ptr = ctx->slabs[chunk_size].free_list;
133 ctx->slabs[chunk_size].free_list = *(void **) ptr;
134 return ptr;
135 }
136
137 chunk = ctx->slabs[chunk_size].chunk;
138 if (chunk == NULL || ! chunk->rem) {
139 int cnt = MAX (128, 8192 / (chunk_size * 2 * sizeof (void *)));
140
141 chunk = _csi_alloc (ctx,
142 sizeof (csi_chunk_t) +
143 cnt * chunk_size * 2 * sizeof (void *));
144 if (_csi_unlikely (chunk == NULL))
145 return NULL;
146
147 chunk->rem = cnt;
148 chunk->ptr = (char *) (chunk + 1);
149 chunk->next = ctx->slabs[chunk_size].chunk;
150 ctx->slabs[chunk_size].chunk = chunk;
151 }
152
153 ptr = chunk->ptr;
154 chunk->ptr += chunk_size * 2 * sizeof (void *);
155 chunk->rem--;
156
157 return ptr;
158 #endif
159 }
160
161 void
_csi_slab_free(csi_t * ctx,void * ptr,int size)162 _csi_slab_free (csi_t *ctx, void *ptr, int size)
163 {
164 int chunk_size;
165 void **free_list;
166
167 if (_csi_unlikely (ptr == NULL))
168 return;
169
170 #if CSI_DEBUG_MALLOC
171 free (ptr);
172 #else
173 chunk_size = 2 * sizeof (void *);
174 chunk_size = (size + chunk_size - 1) / chunk_size;
175
176 free_list = ptr;
177 *free_list = ctx->slabs[chunk_size].free_list;
178 ctx->slabs[chunk_size].free_list = ptr;
179 #endif
180 }
181
182 csi_status_t
_csi_stack_push(csi_t * ctx,csi_stack_t * stack,const csi_object_t * obj)183 _csi_stack_push (csi_t *ctx, csi_stack_t *stack,
184 const csi_object_t *obj)
185 {
186 if (_csi_unlikely (stack->len == stack->size))
187 return _csi_stack_push_internal (ctx, stack, obj);
188
189 stack->objects[stack->len++] = *obj;
190 return CSI_STATUS_SUCCESS;
191 }
192
193 static void
_csi_perm_fini(csi_t * ctx)194 _csi_perm_fini (csi_t *ctx)
195 {
196 while (ctx->perm_chunk != NULL) {
197 csi_chunk_t *chunk = ctx->perm_chunk;
198 ctx->perm_chunk = chunk->next;
199 _csi_free (ctx, chunk);
200 }
201 }
202
203 static void
_csi_slab_fini(csi_t * ctx)204 _csi_slab_fini (csi_t *ctx)
205 {
206 unsigned int i;
207
208 for (i = 0; i < sizeof (ctx->slabs) / sizeof (ctx->slabs[0]); i++) {
209 while (ctx->slabs[i].chunk != NULL) {
210 csi_chunk_t *chunk = ctx->slabs[i].chunk;
211 ctx->slabs[i].chunk = chunk->next;
212 _csi_free (ctx, chunk);
213 }
214 }
215 }
216
217 static csi_status_t
_add_operator(csi_t * ctx,csi_dictionary_t * dict,const csi_operator_def_t * def)218 _add_operator (csi_t *ctx,
219 csi_dictionary_t *dict,
220 const csi_operator_def_t *def)
221 {
222 csi_object_t name;
223 csi_object_t operator;
224 csi_status_t status;
225
226 status = csi_name_new_static (ctx, &name, def->name);
227 if (status)
228 return status;
229
230 csi_operator_new (&operator, def->op);
231
232 return csi_dictionary_put (ctx, dict, name.datum.name, &operator);
233 }
234
235 static csi_status_t
_add_integer_constant(csi_t * ctx,csi_dictionary_t * dict,const csi_integer_constant_def_t * def)236 _add_integer_constant (csi_t *ctx,
237 csi_dictionary_t *dict,
238 const csi_integer_constant_def_t *def)
239 {
240 csi_object_t name;
241 csi_object_t constant;
242 csi_status_t status;
243
244 status = csi_name_new_static (ctx, &name, def->name);
245 if (status)
246 return status;
247
248 csi_integer_new (&constant, def->value);
249
250 return csi_dictionary_put (ctx, dict, name.datum.name, &constant);
251 }
252
253 static csi_status_t
_add_real_constant(csi_t * ctx,csi_dictionary_t * dict,const csi_real_constant_def_t * def)254 _add_real_constant (csi_t *ctx,
255 csi_dictionary_t *dict,
256 const csi_real_constant_def_t *def)
257 {
258 csi_object_t name;
259 csi_object_t constant;
260 csi_status_t status;
261
262 status = csi_name_new_static (ctx, &name, def->name);
263 if (status)
264 return status;
265
266 csi_real_new (&constant, def->value);
267
268 return csi_dictionary_put (ctx, dict, name.datum.name, &constant);
269 }
270
271 static csi_status_t
_init_dictionaries(csi_t * ctx)272 _init_dictionaries (csi_t *ctx)
273 {
274 csi_status_t status;
275 csi_stack_t *stack;
276 csi_object_t obj;
277 csi_dictionary_t *dict, *opcodes;
278 const csi_operator_def_t *odef;
279 const csi_integer_constant_def_t *idef;
280 const csi_real_constant_def_t *rdef;
281 unsigned n;
282
283 stack = &ctx->dstack;
284
285 status = _csi_stack_init (ctx, stack, 4);
286 if (_csi_unlikely (status))
287 return status;
288
289 /* systemdict */
290 status = csi_dictionary_new (ctx, &obj);
291 if (_csi_unlikely (status))
292 return status;
293
294 status = _csi_stack_push (ctx, stack, &obj);
295 if (_csi_unlikely (status))
296 return status;
297
298 dict = obj.datum.dictionary;
299
300 status = csi_dictionary_new (ctx, &obj);
301 if (_csi_unlikely (status))
302 return status;
303
304 opcodes = obj.datum.dictionary;
305
306 n = 0;
307 csi_integer_new (&obj, n);
308 status = csi_dictionary_put (ctx, opcodes, 0, &obj);
309 if (_csi_unlikely (status))
310 return status;
311 ctx->opcode[n++] = NULL;
312
313 /* fill systemdict with operators */
314 for (odef = _csi_operators (); odef->name != NULL; odef++) {
315 status = _add_operator (ctx, dict, odef);
316 if (_csi_unlikely (status))
317 return status;
318
319 if (! csi_dictionary_has (opcodes, (csi_name_t) odef->op)) {
320 csi_integer_new (&obj, n);
321 status = csi_dictionary_put (ctx,
322 opcodes, (csi_name_t) odef->op, &obj);
323 if (_csi_unlikely (status))
324 return status;
325
326 assert (n < sizeof (ctx->opcode) / sizeof (ctx->opcode[0]));
327 ctx->opcode[n++] = odef->op;
328 }
329 }
330 csi_dictionary_free (ctx, opcodes);
331
332 /* add constants */
333 for (idef = _csi_integer_constants (); idef->name != NULL; idef++) {
334 status = _add_integer_constant (ctx, dict, idef);
335 if (_csi_unlikely (status))
336 return status;
337 }
338 for (rdef = _csi_real_constants (); rdef->name != NULL; rdef++) {
339 status = _add_real_constant (ctx, dict, rdef);
340 if (_csi_unlikely (status))
341 return status;
342 }
343
344 /* and seal */
345 //dict.type &= ~CSI_OBJECT_ATTR_WRITABLE;
346
347
348 /* globaldict */
349 status = csi_dictionary_new (ctx, &obj);
350 if (_csi_unlikely (status))
351 return status;
352 status = _csi_stack_push (ctx, stack, &obj);
353 if (_csi_unlikely (status))
354 return status;
355
356 /* userdict */
357 status = csi_dictionary_new (ctx, &obj);
358 if (_csi_unlikely (status))
359 return status;
360 status = _csi_stack_push (ctx, stack, &obj);
361 if (_csi_unlikely (status))
362 return status;
363
364 return CSI_STATUS_SUCCESS;
365 }
366
367 /* intern string */
368
369 typedef struct _cairo_intern_string {
370 csi_hash_entry_t hash_entry;
371 int len;
372 char *string;
373 } csi_intern_string_t;
374
375 static unsigned long
_intern_string_hash(const char * str,int len)376 _intern_string_hash (const char *str, int len)
377 {
378 const signed char *p = (const signed char *) str;
379 if (len > 0) {
380 unsigned int h = *p;
381
382 while (--len)
383 h = (h << 5) - h + *++p;
384
385 return h;
386 }
387 return 0;
388 }
389
390 static cairo_bool_t
_intern_string_equal(const void * _a,const void * _b)391 _intern_string_equal (const void *_a, const void *_b)
392 {
393 const csi_intern_string_t *a = _a;
394 const csi_intern_string_t *b = _b;
395
396 if (a->len != b->len)
397 return FALSE;
398
399 return memcmp (a->string, b->string, a->len) == 0;
400 }
401
402 static void
_csi_init(csi_t * ctx)403 _csi_init (csi_t *ctx)
404 {
405 csi_status_t status;
406
407 memset (ctx, 0, sizeof (*ctx));
408
409 ctx->status = CSI_STATUS_SUCCESS;
410 ctx->ref_count = 1;
411 ctx->scanner.line_number = -1;
412
413 status = _csi_hash_table_init (&ctx->strings, _intern_string_equal);
414 if (status)
415 goto FAIL;
416
417 status = _csi_stack_init (ctx, &ctx->ostack, 2048);
418 if (status)
419 goto FAIL;
420 status = _init_dictionaries (ctx);
421 if (status)
422 goto FAIL;
423
424 status = _csi_scanner_init (ctx, &ctx->scanner);
425 if (status)
426 goto FAIL;
427
428 return;
429
430 FAIL:
431 if (ctx->status == CSI_STATUS_SUCCESS)
432 ctx->status = status;
433 }
434
435 static void
_csi_finish(csi_t * ctx)436 _csi_finish (csi_t *ctx)
437 {
438 _csi_stack_fini (ctx, &ctx->ostack);
439 _csi_stack_fini (ctx, &ctx->dstack);
440 _csi_scanner_fini (ctx, &ctx->scanner);
441
442 _csi_hash_table_fini (&ctx->strings);
443 }
444
445 csi_status_t
_csi_name_define(csi_t * ctx,csi_name_t name,csi_object_t * obj)446 _csi_name_define (csi_t *ctx, csi_name_t name, csi_object_t *obj)
447 {
448 return csi_dictionary_put (ctx,
449 ctx->dstack.objects[ctx->dstack.len-1].datum.dictionary,
450 name,
451 obj);
452 }
453
454 csi_status_t
_csi_name_lookup(csi_t * ctx,csi_name_t name,csi_object_t * obj)455 _csi_name_lookup (csi_t *ctx, csi_name_t name, csi_object_t *obj)
456 {
457 int i;
458
459 for (i = ctx->dstack.len; i--; ) {
460 csi_dictionary_t *dict;
461 csi_dictionary_entry_t *entry;
462
463 dict = ctx->dstack.objects[i].datum.dictionary;
464 entry = _csi_hash_table_lookup (&dict->hash_table,
465 (csi_hash_entry_t *) &name);
466 if (entry != NULL) {
467 *obj = entry->value;
468 return CSI_STATUS_SUCCESS;
469 }
470 }
471
472 return _csi_error (CSI_STATUS_INVALID_SCRIPT);
473 }
474
475 csi_status_t
_csi_name_undefine(csi_t * ctx,csi_name_t name)476 _csi_name_undefine (csi_t *ctx, csi_name_t name)
477 {
478 unsigned int i;
479
480 for (i = ctx->dstack.len; --i; ) {
481 if (csi_dictionary_has (ctx->dstack.objects[i].datum.dictionary,
482 name))
483 {
484 csi_dictionary_remove (ctx,
485 ctx->dstack.objects[i].datum.dictionary,
486 name);
487 return CSI_STATUS_SUCCESS;
488 }
489 }
490
491 return _csi_error (CSI_STATUS_INVALID_SCRIPT);
492 }
493
494 csi_status_t
_csi_intern_string(csi_t * ctx,const char ** str_inout,int len)495 _csi_intern_string (csi_t *ctx, const char **str_inout, int len)
496 {
497 char *str = (char *) *str_inout;
498 csi_intern_string_t tmpl, *istring;
499 csi_status_t status = CSI_STATUS_SUCCESS;
500
501 tmpl.hash_entry.hash = _intern_string_hash (str, len);
502 tmpl.len = len;
503 tmpl.string = (char *) str;
504
505 istring = _csi_hash_table_lookup (&ctx->strings, &tmpl.hash_entry);
506 if (istring == NULL) {
507 istring = _csi_perm_alloc (ctx,
508 sizeof (csi_intern_string_t) + len + 1);
509 if (istring != NULL) {
510 istring->hash_entry.hash = tmpl.hash_entry.hash;
511 istring->len = tmpl.len;
512 istring->string = (char *) (istring + 1);
513 memcpy (istring->string, str, len);
514 istring->string[len] = '\0';
515
516 status = _csi_hash_table_insert (&ctx->strings,
517 &istring->hash_entry);
518 if (_csi_unlikely (status)) {
519 _csi_free (ctx, istring);
520 return status;
521 }
522 } else
523 return _csi_error (CSI_STATUS_NO_MEMORY);
524 }
525
526 *str_inout = istring->string;
527 return CSI_STATUS_SUCCESS;
528 }
529
530 /* Public */
531
532 static csi_t _csi_nil = { -1, CSI_STATUS_NO_MEMORY };
533
534 csi_t *
cairo_script_interpreter_create(void)535 cairo_script_interpreter_create (void)
536 {
537 csi_t *ctx;
538
539 ctx = malloc (sizeof (csi_t));
540 if (ctx == NULL)
541 return (csi_t *) &_csi_nil;
542
543 _csi_init (ctx);
544
545 return ctx;
546 }
547
548 void
cairo_script_interpreter_install_hooks(csi_t * ctx,const csi_hooks_t * hooks)549 cairo_script_interpreter_install_hooks (csi_t *ctx,
550 const csi_hooks_t *hooks)
551 {
552 if (ctx->status)
553 return;
554
555 ctx->hooks = *hooks;
556 }
557
558 cairo_status_t
cairo_script_interpreter_run(csi_t * ctx,const char * filename)559 cairo_script_interpreter_run (csi_t *ctx, const char *filename)
560 {
561 csi_object_t file;
562
563 if (ctx->status)
564 return ctx->status;
565 if (ctx->finished)
566 return ctx->status = CSI_STATUS_INTERPRETER_FINISHED;
567
568 ctx->status = csi_file_new (ctx, &file, filename, "r");
569 if (ctx->status)
570 return ctx->status;
571
572 file.type |= CSI_OBJECT_ATTR_EXECUTABLE;
573
574 ctx->status = csi_object_execute (ctx, &file);
575 csi_object_free (ctx, &file);
576
577 return ctx->status;
578 }
579
580 cairo_status_t
cairo_script_interpreter_feed_stream(csi_t * ctx,FILE * stream)581 cairo_script_interpreter_feed_stream (csi_t *ctx, FILE *stream)
582 {
583 csi_object_t file;
584
585 if (ctx->status)
586 return ctx->status;
587 if (ctx->finished)
588 return ctx->status = CSI_STATUS_INTERPRETER_FINISHED;
589
590 ctx->status = csi_file_new_for_stream (ctx, &file, stream);
591 if (ctx->status)
592 return ctx->status;
593
594 file.type |= CSI_OBJECT_ATTR_EXECUTABLE;
595
596 ctx->status = csi_object_execute (ctx, &file);
597 csi_object_free (ctx, &file);
598
599 return ctx->status;
600 }
601
602 cairo_status_t
cairo_script_interpreter_feed_string(csi_t * ctx,const char * line,int len)603 cairo_script_interpreter_feed_string (csi_t *ctx, const char *line, int len)
604 {
605 csi_object_t file;
606
607 if (ctx->status)
608 return ctx->status;
609 if (ctx->finished)
610 return ctx->status = CSI_STATUS_INTERPRETER_FINISHED;
611
612 if (len < 0)
613 len = strlen (line);
614 ctx->status = csi_file_new_for_bytes (ctx, &file, line, len);
615 if (ctx->status)
616 return ctx->status;
617
618 file.type |= CSI_OBJECT_ATTR_EXECUTABLE;
619
620 ctx->status = csi_object_execute (ctx, &file);
621 csi_object_free (ctx, &file);
622
623 return ctx->status;
624 }
625
626 unsigned int
cairo_script_interpreter_get_line_number(csi_t * ctx)627 cairo_script_interpreter_get_line_number (csi_t *ctx)
628 {
629 return ctx->scanner.line_number + 1; /* 1 index based */
630 }
631
632 csi_t *
cairo_script_interpreter_reference(csi_t * ctx)633 cairo_script_interpreter_reference (csi_t *ctx)
634 {
635 ctx->ref_count++;
636 return ctx;
637 }
638 slim_hidden_def (cairo_script_interpreter_reference);
639
640 cairo_status_t
cairo_script_interpreter_finish(csi_t * ctx)641 cairo_script_interpreter_finish (csi_t *ctx)
642 {
643 csi_status_t status;
644
645 status = ctx->status;
646 if (! ctx->finished) {
647 _csi_finish (ctx);
648 ctx->finished = 1;
649 } else if (status == CSI_STATUS_SUCCESS) {
650 status = ctx->status = CSI_STATUS_INTERPRETER_FINISHED;
651 }
652
653 return status;
654 }
655
656 static void
_csi_fini(csi_t * ctx)657 _csi_fini (csi_t *ctx)
658 {
659 if (! ctx->finished)
660 _csi_finish (ctx);
661
662 if (ctx->free_array != NULL)
663 csi_array_free (ctx, ctx->free_array);
664 if (ctx->free_dictionary != NULL)
665 csi_dictionary_free (ctx, ctx->free_dictionary);
666 if (ctx->free_string != NULL)
667 csi_string_free (ctx, ctx->free_string);
668
669 _csi_slab_fini (ctx);
670 _csi_perm_fini (ctx);
671 }
672
673 cairo_status_t
cairo_script_interpreter_destroy(csi_t * ctx)674 cairo_script_interpreter_destroy (csi_t *ctx)
675 {
676 csi_status_t status;
677
678 status = ctx->status;
679 if (--ctx->ref_count)
680 return status;
681
682 _csi_fini (ctx);
683 free (ctx);
684
685 return status;
686 }
687 slim_hidden_def (cairo_script_interpreter_destroy);
688
689 cairo_status_t
cairo_script_interpreter_translate_stream(FILE * stream,cairo_write_func_t write_func,void * closure)690 cairo_script_interpreter_translate_stream (FILE *stream,
691 cairo_write_func_t write_func,
692 void *closure)
693 {
694 csi_t ctx;
695 csi_object_t src;
696 csi_status_t status;
697
698 _csi_init (&ctx);
699
700 status = csi_file_new_for_stream (&ctx, &src, stream);
701 if (status)
702 goto BAIL;
703
704 status = _csi_translate_file (&ctx, src.datum.file, write_func, closure);
705
706 BAIL:
707 csi_object_free (&ctx, &src);
708 _csi_fini (&ctx);
709
710 return status;
711 }
712