2 * Copyright © 2008 Chris Wilson <chris@chris-wilson.co.uk>
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.
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
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/
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.
27 * The Original Code is the cairo graphics library.
29 * The Initial Developer of the Original Code is Chris Wilson.
32 * Chris Wilson <chris@chris-wilson.co.uk>
37 #include "cairo-script-private.h"
46 #define MAX(a,b) (((a)>=(b))?(a):(b))
50 _csi_error (csi_status_t status)
55 /* XXX track global/local memory, cap etc, mark/sweep GC */
57 _csi_alloc (csi_t *ctx, int size)
63 _csi_alloc0 (csi_t *ctx, int size)
67 ptr = _csi_alloc (ctx, size);
68 if (_csi_likely (ptr != NULL))
69 memset (ptr, 0, size);
75 _csi_realloc (csi_t *ctx, void *ptr, int size)
77 return realloc (ptr, size);
81 _csi_free (csi_t *ctx, void *ptr)
83 if (_csi_unlikely (ptr == NULL))
90 _csi_perm_alloc (csi_t *ctx, int size)
95 size = (size + sizeof (void *)-1) & -sizeof (void *);
97 chunk = ctx->perm_chunk;
98 if (chunk == NULL || chunk->rem < size) {
99 int chunk_size = (size + 8191) & -8192;
100 chunk = _csi_alloc (ctx, sizeof (csi_chunk_t) + chunk_size);
101 if (_csi_unlikely (chunk == NULL))
104 chunk->rem = chunk_size;
105 chunk->ptr = (char *) (chunk + 1);
106 chunk->next = ctx->perm_chunk;
107 ctx->perm_chunk = chunk;
118 _csi_slab_alloc (csi_t *ctx, int size)
121 return malloc (size);
127 chunk_size = 2 * sizeof (void *);
128 chunk_size = (size + chunk_size - 1) / chunk_size;
130 if (ctx->slabs[chunk_size].free_list) {
131 ptr = ctx->slabs[chunk_size].free_list;
132 ctx->slabs[chunk_size].free_list = *(void **) ptr;
136 chunk = ctx->slabs[chunk_size].chunk;
137 if (chunk == NULL || ! chunk->rem) {
138 int cnt = MAX (128, 8192 / (chunk_size * 2 * sizeof (void *)));
140 chunk = _csi_alloc (ctx,
141 sizeof (csi_chunk_t) +
142 cnt * chunk_size * 2 * sizeof (void *));
143 if (_csi_unlikely (chunk == NULL))
147 chunk->ptr = (char *) (chunk + 1);
148 chunk->next = ctx->slabs[chunk_size].chunk;
149 ctx->slabs[chunk_size].chunk = chunk;
153 chunk->ptr += chunk_size * 2 * sizeof (void *);
161 _csi_slab_free (csi_t *ctx, void *ptr, int size)
166 if (_csi_unlikely (ptr == NULL))
172 chunk_size = 2 * sizeof (void *);
173 chunk_size = (size + chunk_size - 1) / chunk_size;
176 *free_list = ctx->slabs[chunk_size].free_list;
177 ctx->slabs[chunk_size].free_list = ptr;
182 _csi_perm_fini (csi_t *ctx)
184 while (ctx->perm_chunk != NULL) {
185 csi_chunk_t *chunk = ctx->perm_chunk;
186 ctx->perm_chunk = chunk->next;
187 _csi_free (ctx, chunk);
192 _csi_slab_fini (csi_t *ctx)
196 for (i = 0; i < sizeof (ctx->slabs) / sizeof (ctx->slabs[0]); i++) {
197 while (ctx->slabs[i].chunk != NULL) {
198 csi_chunk_t *chunk = ctx->slabs[i].chunk;
199 ctx->slabs[i].chunk = chunk->next;
200 _csi_free (ctx, chunk);
206 _add_operator (csi_t *ctx,
207 csi_dictionary_t *dict,
208 const csi_operator_def_t *def)
211 csi_object_t operator;
214 status = csi_name_new_static (ctx, &name, def->name);
218 csi_operator_new (&operator, def->op);
220 return csi_dictionary_put (ctx, dict, name.datum.name, &operator);
224 _add_integer_constant (csi_t *ctx,
225 csi_dictionary_t *dict,
226 const csi_integer_constant_def_t *def)
229 csi_object_t constant;
232 status = csi_name_new_static (ctx, &name, def->name);
236 csi_integer_new (&constant, def->value);
238 return csi_dictionary_put (ctx, dict, name.datum.name, &constant);
242 _add_real_constant (csi_t *ctx,
243 csi_dictionary_t *dict,
244 const csi_real_constant_def_t *def)
247 csi_object_t constant;
250 status = csi_name_new_static (ctx, &name, def->name);
254 csi_real_new (&constant, def->value);
256 return csi_dictionary_put (ctx, dict, name.datum.name, &constant);
260 _init_dictionaries (csi_t *ctx)
265 csi_dictionary_t *dict, *opcodes;
266 const csi_operator_def_t *odef;
267 const csi_integer_constant_def_t *idef;
268 const csi_real_constant_def_t *rdef;
271 stack = &ctx->dstack;
273 status = _csi_stack_init (ctx, stack, 4);
274 if (_csi_unlikely (status))
278 status = csi_dictionary_new (ctx, &obj);
279 if (_csi_unlikely (status))
282 status = _csi_stack_push (ctx, stack, &obj);
283 if (_csi_unlikely (status))
286 dict = obj.datum.dictionary;
288 status = csi_dictionary_new (ctx, &obj);
289 if (_csi_unlikely (status))
292 opcodes = obj.datum.dictionary;
295 csi_integer_new (&obj, n);
296 status = csi_dictionary_put (ctx, opcodes, 0, &obj);
297 if (_csi_unlikely (status))
299 ctx->opcode[n++] = NULL;
301 /* fill systemdict with operators */
302 for (odef = _csi_operators (); odef->name != NULL; odef++) {
303 status = _add_operator (ctx, dict, odef);
304 if (_csi_unlikely (status))
307 if (! csi_dictionary_has (opcodes, (csi_name_t) odef->op)) {
308 csi_integer_new (&obj, n);
309 status = csi_dictionary_put (ctx,
310 opcodes, (csi_name_t) odef->op, &obj);
311 if (_csi_unlikely (status))
314 assert (n < sizeof (ctx->opcode) / sizeof (ctx->opcode[0]));
315 ctx->opcode[n++] = odef->op;
318 csi_dictionary_free (ctx, opcodes);
321 for (idef = _csi_integer_constants (); idef->name != NULL; idef++) {
322 status = _add_integer_constant (ctx, dict, idef);
323 if (_csi_unlikely (status))
326 for (rdef = _csi_real_constants (); rdef->name != NULL; rdef++) {
327 status = _add_real_constant (ctx, dict, rdef);
328 if (_csi_unlikely (status))
333 //dict.type &= ~CSI_OBJECT_ATTR_WRITABLE;
337 status = csi_dictionary_new (ctx, &obj);
338 if (_csi_unlikely (status))
340 status = _csi_stack_push (ctx, stack, &obj);
341 if (_csi_unlikely (status))
345 status = csi_dictionary_new (ctx, &obj);
346 if (_csi_unlikely (status))
348 status = _csi_stack_push (ctx, stack, &obj);
349 if (_csi_unlikely (status))
352 return CSI_STATUS_SUCCESS;
357 typedef struct _cairo_intern_string {
358 csi_hash_entry_t hash_entry;
361 } csi_intern_string_t;
364 _intern_string_hash (const char *str, int len)
366 const signed char *p = (const signed char *) str;
371 h = (h << 5) - h + *++p;
379 _intern_string_equal (const void *_a, const void *_b)
381 const csi_intern_string_t *a = _a;
382 const csi_intern_string_t *b = _b;
384 if (a->len != b->len)
387 return memcmp (a->string, b->string, a->len) == 0;
391 _csi_init (csi_t *ctx)
395 memset (ctx, 0, sizeof (*ctx));
397 ctx->status = CSI_STATUS_SUCCESS;
399 ctx->scanner.line_number = -1;
401 status = _csi_hash_table_init (&ctx->strings, _intern_string_equal);
405 status = _csi_stack_init (ctx, &ctx->ostack, 2048);
408 status = _init_dictionaries (ctx);
412 status = _csi_scanner_init (ctx, &ctx->scanner);
419 if (ctx->status == CSI_STATUS_SUCCESS)
420 ctx->status = status;
424 _csi_finish (csi_t *ctx)
426 _csi_stack_fini (ctx, &ctx->ostack);
427 _csi_stack_fini (ctx, &ctx->dstack);
428 _csi_scanner_fini (ctx, &ctx->scanner);
430 _csi_hash_table_fini (&ctx->strings);
434 _csi_name_define (csi_t *ctx, csi_name_t name, csi_object_t *obj)
436 return csi_dictionary_put (ctx,
437 ctx->dstack.objects[ctx->dstack.len-1].datum.dictionary,
443 _csi_name_lookup (csi_t *ctx, csi_name_t name, csi_object_t *obj)
447 for (i = ctx->dstack.len; i--; ) {
448 csi_dictionary_t *dict;
449 csi_dictionary_entry_t *entry;
451 dict = ctx->dstack.objects[i].datum.dictionary;
452 entry = _csi_hash_table_lookup (&dict->hash_table,
453 (csi_hash_entry_t *) &name);
456 return CSI_STATUS_SUCCESS;
460 return _csi_error (CSI_STATUS_INVALID_SCRIPT);
464 _csi_name_undefine (csi_t *ctx, csi_name_t name)
468 for (i = ctx->dstack.len; --i; ) {
469 if (csi_dictionary_has (ctx->dstack.objects[i].datum.dictionary,
472 csi_dictionary_remove (ctx,
473 ctx->dstack.objects[i].datum.dictionary,
475 return CSI_STATUS_SUCCESS;
479 return _csi_error (CSI_STATUS_INVALID_SCRIPT);
483 _csi_intern_string (csi_t *ctx, const char **str_inout, int len)
485 char *str = (char *) *str_inout;
486 csi_intern_string_t tmpl, *istring;
487 csi_status_t status = CSI_STATUS_SUCCESS;
489 tmpl.hash_entry.hash = _intern_string_hash (str, len);
491 tmpl.string = (char *) str;
493 istring = _csi_hash_table_lookup (&ctx->strings, &tmpl.hash_entry);
494 if (istring == NULL) {
495 istring = _csi_perm_alloc (ctx,
496 sizeof (csi_intern_string_t) + len + 1);
497 if (istring != NULL) {
498 istring->hash_entry.hash = tmpl.hash_entry.hash;
499 istring->len = tmpl.len;
500 istring->string = (char *) (istring + 1);
501 memcpy (istring->string, str, len);
502 istring->string[len] = '\0';
504 status = _csi_hash_table_insert (&ctx->strings,
505 &istring->hash_entry);
506 if (_csi_unlikely (status)) {
507 _csi_free (ctx, istring);
511 return _csi_error (CSI_STATUS_NO_MEMORY);
514 *str_inout = istring->string;
515 return CSI_STATUS_SUCCESS;
520 static csi_t _csi_nil = { -1, CSI_STATUS_NO_MEMORY };
523 cairo_script_interpreter_create (void)
527 ctx = malloc (sizeof (csi_t));
529 return (csi_t *) &_csi_nil;
537 cairo_script_interpreter_install_hooks (csi_t *ctx,
538 const csi_hooks_t *hooks)
547 cairo_script_interpreter_run (csi_t *ctx, const char *filename)
554 return ctx->status = CSI_STATUS_INTERPRETER_FINISHED;
556 ctx->status = csi_file_new (ctx, &file, filename, "r");
560 file.type |= CSI_OBJECT_ATTR_EXECUTABLE;
562 ctx->status = csi_object_execute (ctx, &file);
563 csi_object_free (ctx, &file);
569 cairo_script_interpreter_feed_stream (csi_t *ctx, FILE *stream)
576 return ctx->status = CSI_STATUS_INTERPRETER_FINISHED;
578 ctx->status = csi_file_new_for_stream (ctx, &file, stream);
582 file.type |= CSI_OBJECT_ATTR_EXECUTABLE;
584 ctx->status = csi_object_execute (ctx, &file);
585 csi_object_free (ctx, &file);
591 cairo_script_interpreter_feed_string (csi_t *ctx, const char *line, int len)
598 return ctx->status = CSI_STATUS_INTERPRETER_FINISHED;
602 ctx->status = csi_file_new_for_bytes (ctx, &file, line, len);
606 file.type |= CSI_OBJECT_ATTR_EXECUTABLE;
608 ctx->status = csi_object_execute (ctx, &file);
609 csi_object_free (ctx, &file);
615 cairo_script_interpreter_get_line_number (csi_t *ctx)
617 return ctx->scanner.line_number + 1; /* 1 index based */
621 cairo_script_interpreter_reference (csi_t *ctx)
626 slim_hidden_def (cairo_script_interpreter_reference);
629 cairo_script_interpreter_finish (csi_t *ctx)
633 status = ctx->status;
634 if (! ctx->finished) {
637 } else if (status == CAIRO_STATUS_SUCCESS) {
638 status = ctx->status = CSI_STATUS_INTERPRETER_FINISHED;
645 _csi_fini (csi_t *ctx)
650 if (ctx->free_array != NULL)
651 csi_array_free (ctx, ctx->free_array);
652 if (ctx->free_dictionary != NULL)
653 csi_dictionary_free (ctx, ctx->free_dictionary);
654 if (ctx->free_string != NULL)
655 csi_string_free (ctx, ctx->free_string);
657 _csi_slab_fini (ctx);
658 _csi_perm_fini (ctx);
662 cairo_script_interpreter_destroy (csi_t *ctx)
666 status = ctx->status;
667 if (--ctx->ref_count)
675 slim_hidden_def (cairo_script_interpreter_destroy);
678 cairo_script_interpreter_translate_stream (FILE *stream,
679 cairo_write_func_t write_func,
688 status = csi_file_new_for_stream (&ctx, &src, stream);
692 status = _csi_translate_file (&ctx, src.datum.file, write_func, closure);
695 csi_object_free (&ctx, &src);