| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711 |
- /*
- * Copyright © 2008 Chris Wilson <chris@chris-wilson.co.uk>
- *
- * This library is free software; you can redistribute it and/or
- * modify it either under the terms of the GNU Lesser General Public
- * License version 2.1 as published by the Free Software Foundation
- * (the "LGPL") or, at your option, under the terms of the Mozilla
- * Public License Version 1.1 (the "MPL"). If you do not alter this
- * notice, a recipient may use your version of this file under either
- * the MPL or the LGPL.
- *
- * You should have received a copy of the LGPL along with this library
- * in the file COPYING-LGPL-2.1; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
- * You should have received a copy of the MPL along with this library
- * in the file COPYING-MPL-1.1
- *
- * The contents of this file are subject to the Mozilla Public License
- * Version 1.1 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
- * OF ANY KIND, either express or implied. See the LGPL or the MPL for
- * the specific language governing rights and limitations.
- *
- * The Original Code is the cairo graphics library.
- *
- * The Initial Developer of the Original Code is Chris Wilson.
- *
- * Contributor(s):
- * Chris Wilson <chris@chris-wilson.co.uk>
- */
- #include "config.h"
- #include "cairo-script-private.h"
- #include "cairo.h"
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <math.h>
- #include <assert.h>
- #ifndef MAX
- #define MAX(a,b) (((a)>=(b))?(a):(b))
- #endif
- csi_status_t
- _csi_error (csi_status_t status)
- {
- return status;
- }
- /* XXX track global/local memory, cap etc, mark/sweep GC */
- void *
- _csi_alloc (csi_t *ctx, int size)
- {
- return malloc (size);
- }
- void *
- _csi_alloc0 (csi_t *ctx, int size)
- {
- void *ptr;
- ptr = _csi_alloc (ctx, size);
- if (_csi_likely (ptr != NULL))
- memset (ptr, 0, size);
- return ptr;
- }
- void *
- _csi_realloc (csi_t *ctx, void *ptr, int size)
- {
- return realloc (ptr, size);
- }
- void
- _csi_free (csi_t *ctx, void *ptr)
- {
- if (_csi_unlikely (ptr == NULL))
- return;
- free (ptr);
- }
- void *
- _csi_perm_alloc (csi_t *ctx, int size)
- {
- csi_chunk_t *chunk;
- void *ptr;
- size = (size + sizeof (void *)-1) & -sizeof (void *);
- chunk = ctx->perm_chunk;
- if (chunk == NULL || chunk->rem < size) {
- int chunk_size = (size + 8191) & -8192;
- chunk = _csi_alloc (ctx, sizeof (csi_chunk_t) + chunk_size);
- if (_csi_unlikely (chunk == NULL))
- return NULL;
- chunk->rem = chunk_size;
- chunk->ptr = (char *) (chunk + 1);
- chunk->next = ctx->perm_chunk;
- ctx->perm_chunk = chunk;
- }
- ptr = chunk->ptr;
- chunk->ptr += size;
- chunk->rem -= size;
- return ptr;
- }
- void *
- _csi_slab_alloc (csi_t *ctx, int size)
- {
- #if CSI_DEBUG_MALLOC
- return malloc (size);
- #else
- int chunk_size;
- csi_chunk_t *chunk;
- void *ptr;
- chunk_size = 2 * sizeof (void *);
- chunk_size = (size + chunk_size - 1) / chunk_size;
- if (ctx->slabs[chunk_size].free_list) {
- ptr = ctx->slabs[chunk_size].free_list;
- ctx->slabs[chunk_size].free_list = *(void **) ptr;
- return ptr;
- }
- chunk = ctx->slabs[chunk_size].chunk;
- if (chunk == NULL || ! chunk->rem) {
- int cnt = MAX (128, 8192 / (chunk_size * 2 * sizeof (void *)));
- chunk = _csi_alloc (ctx,
- sizeof (csi_chunk_t) +
- cnt * chunk_size * 2 * sizeof (void *));
- if (_csi_unlikely (chunk == NULL))
- return NULL;
- chunk->rem = cnt;
- chunk->ptr = (char *) (chunk + 1);
- chunk->next = ctx->slabs[chunk_size].chunk;
- ctx->slabs[chunk_size].chunk = chunk;
- }
- ptr = chunk->ptr;
- chunk->ptr += chunk_size * 2 * sizeof (void *);
- chunk->rem--;
- return ptr;
- #endif
- }
- void
- _csi_slab_free (csi_t *ctx, void *ptr, int size)
- {
- int chunk_size;
- void **free_list;
- if (_csi_unlikely (ptr == NULL))
- return;
- #if CSI_DEBUG_MALLOC
- free (ptr);
- #else
- chunk_size = 2 * sizeof (void *);
- chunk_size = (size + chunk_size - 1) / chunk_size;
- free_list = ptr;
- *free_list = ctx->slabs[chunk_size].free_list;
- ctx->slabs[chunk_size].free_list = ptr;
- #endif
- }
- csi_status_t
- _csi_stack_push (csi_t *ctx, csi_stack_t *stack,
- const csi_object_t *obj)
- {
- if (_csi_unlikely (stack->len == stack->size))
- return _csi_stack_push_internal (ctx, stack, obj);
- stack->objects[stack->len++] = *obj;
- return CSI_STATUS_SUCCESS;
- }
- static void
- _csi_perm_fini (csi_t *ctx)
- {
- while (ctx->perm_chunk != NULL) {
- csi_chunk_t *chunk = ctx->perm_chunk;
- ctx->perm_chunk = chunk->next;
- _csi_free (ctx, chunk);
- }
- }
- static void
- _csi_slab_fini (csi_t *ctx)
- {
- unsigned int i;
- for (i = 0; i < sizeof (ctx->slabs) / sizeof (ctx->slabs[0]); i++) {
- while (ctx->slabs[i].chunk != NULL) {
- csi_chunk_t *chunk = ctx->slabs[i].chunk;
- ctx->slabs[i].chunk = chunk->next;
- _csi_free (ctx, chunk);
- }
- }
- }
- static csi_status_t
- _add_operator (csi_t *ctx,
- csi_dictionary_t *dict,
- const csi_operator_def_t *def)
- {
- csi_object_t name;
- csi_object_t operator;
- csi_status_t status;
- status = csi_name_new_static (ctx, &name, def->name);
- if (status)
- return status;
- csi_operator_new (&operator, def->op);
- return csi_dictionary_put (ctx, dict, name.datum.name, &operator);
- }
- static csi_status_t
- _add_integer_constant (csi_t *ctx,
- csi_dictionary_t *dict,
- const csi_integer_constant_def_t *def)
- {
- csi_object_t name;
- csi_object_t constant;
- csi_status_t status;
- status = csi_name_new_static (ctx, &name, def->name);
- if (status)
- return status;
- csi_integer_new (&constant, def->value);
- return csi_dictionary_put (ctx, dict, name.datum.name, &constant);
- }
- static csi_status_t
- _add_real_constant (csi_t *ctx,
- csi_dictionary_t *dict,
- const csi_real_constant_def_t *def)
- {
- csi_object_t name;
- csi_object_t constant;
- csi_status_t status;
- status = csi_name_new_static (ctx, &name, def->name);
- if (status)
- return status;
- csi_real_new (&constant, def->value);
- return csi_dictionary_put (ctx, dict, name.datum.name, &constant);
- }
- static csi_status_t
- _init_dictionaries (csi_t *ctx)
- {
- csi_status_t status;
- csi_stack_t *stack;
- csi_object_t obj;
- csi_dictionary_t *dict, *opcodes;
- const csi_operator_def_t *odef;
- const csi_integer_constant_def_t *idef;
- const csi_real_constant_def_t *rdef;
- unsigned n;
- stack = &ctx->dstack;
- status = _csi_stack_init (ctx, stack, 4);
- if (_csi_unlikely (status))
- return status;
- /* systemdict */
- status = csi_dictionary_new (ctx, &obj);
- if (_csi_unlikely (status))
- return status;
- status = _csi_stack_push (ctx, stack, &obj);
- if (_csi_unlikely (status))
- return status;
- dict = obj.datum.dictionary;
- status = csi_dictionary_new (ctx, &obj);
- if (_csi_unlikely (status))
- return status;
- opcodes = obj.datum.dictionary;
- n = 0;
- csi_integer_new (&obj, n);
- status = csi_dictionary_put (ctx, opcodes, 0, &obj);
- if (_csi_unlikely (status))
- return status;
- ctx->opcode[n++] = NULL;
- /* fill systemdict with operators */
- for (odef = _csi_operators (); odef->name != NULL; odef++) {
- status = _add_operator (ctx, dict, odef);
- if (_csi_unlikely (status))
- return status;
- if (! csi_dictionary_has (opcodes, (csi_name_t) odef->op)) {
- csi_integer_new (&obj, n);
- status = csi_dictionary_put (ctx,
- opcodes, (csi_name_t) odef->op, &obj);
- if (_csi_unlikely (status))
- return status;
- assert (n < sizeof (ctx->opcode) / sizeof (ctx->opcode[0]));
- ctx->opcode[n++] = odef->op;
- }
- }
- csi_dictionary_free (ctx, opcodes);
- /* add constants */
- for (idef = _csi_integer_constants (); idef->name != NULL; idef++) {
- status = _add_integer_constant (ctx, dict, idef);
- if (_csi_unlikely (status))
- return status;
- }
- for (rdef = _csi_real_constants (); rdef->name != NULL; rdef++) {
- status = _add_real_constant (ctx, dict, rdef);
- if (_csi_unlikely (status))
- return status;
- }
- /* and seal */
- //dict.type &= ~CSI_OBJECT_ATTR_WRITABLE;
- /* globaldict */
- status = csi_dictionary_new (ctx, &obj);
- if (_csi_unlikely (status))
- return status;
- status = _csi_stack_push (ctx, stack, &obj);
- if (_csi_unlikely (status))
- return status;
- /* userdict */
- status = csi_dictionary_new (ctx, &obj);
- if (_csi_unlikely (status))
- return status;
- status = _csi_stack_push (ctx, stack, &obj);
- if (_csi_unlikely (status))
- return status;
- return CSI_STATUS_SUCCESS;
- }
- /* intern string */
- typedef struct _cairo_intern_string {
- csi_hash_entry_t hash_entry;
- int len;
- char *string;
- } csi_intern_string_t;
- static unsigned long
- _intern_string_hash (const char *str, int len)
- {
- const signed char *p = (const signed char *) str;
- if (len > 0) {
- unsigned int h = *p;
- while (--len)
- h = (h << 5) - h + *++p;
- return h;
- }
- return 0;
- }
- static cairo_bool_t
- _intern_string_equal (const void *_a, const void *_b)
- {
- const csi_intern_string_t *a = _a;
- const csi_intern_string_t *b = _b;
- if (a->len != b->len)
- return FALSE;
- return memcmp (a->string, b->string, a->len) == 0;
- }
- static void
- _csi_init (csi_t *ctx)
- {
- csi_status_t status;
- memset (ctx, 0, sizeof (*ctx));
- ctx->status = CSI_STATUS_SUCCESS;
- ctx->ref_count = 1;
- ctx->scanner.line_number = -1;
- status = _csi_hash_table_init (&ctx->strings, _intern_string_equal);
- if (status)
- goto FAIL;
- status = _csi_stack_init (ctx, &ctx->ostack, 2048);
- if (status)
- goto FAIL;
- status = _init_dictionaries (ctx);
- if (status)
- goto FAIL;
- status = _csi_scanner_init (ctx, &ctx->scanner);
- if (status)
- goto FAIL;
- return;
- FAIL:
- if (ctx->status == CSI_STATUS_SUCCESS)
- ctx->status = status;
- }
- static void
- _csi_finish (csi_t *ctx)
- {
- _csi_stack_fini (ctx, &ctx->ostack);
- _csi_stack_fini (ctx, &ctx->dstack);
- _csi_scanner_fini (ctx, &ctx->scanner);
- _csi_hash_table_fini (&ctx->strings);
- }
- csi_status_t
- _csi_name_define (csi_t *ctx, csi_name_t name, csi_object_t *obj)
- {
- return csi_dictionary_put (ctx,
- ctx->dstack.objects[ctx->dstack.len-1].datum.dictionary,
- name,
- obj);
- }
- csi_status_t
- _csi_name_lookup (csi_t *ctx, csi_name_t name, csi_object_t *obj)
- {
- int i;
- for (i = ctx->dstack.len; i--; ) {
- csi_dictionary_t *dict;
- csi_dictionary_entry_t *entry;
- dict = ctx->dstack.objects[i].datum.dictionary;
- entry = _csi_hash_table_lookup (&dict->hash_table,
- (csi_hash_entry_t *) &name);
- if (entry != NULL) {
- *obj = entry->value;
- return CSI_STATUS_SUCCESS;
- }
- }
- return _csi_error (CSI_STATUS_INVALID_SCRIPT);
- }
- csi_status_t
- _csi_name_undefine (csi_t *ctx, csi_name_t name)
- {
- unsigned int i;
- for (i = ctx->dstack.len; --i; ) {
- if (csi_dictionary_has (ctx->dstack.objects[i].datum.dictionary,
- name))
- {
- csi_dictionary_remove (ctx,
- ctx->dstack.objects[i].datum.dictionary,
- name);
- return CSI_STATUS_SUCCESS;
- }
- }
- return _csi_error (CSI_STATUS_INVALID_SCRIPT);
- }
- csi_status_t
- _csi_intern_string (csi_t *ctx, const char **str_inout, int len)
- {
- char *str = (char *) *str_inout;
- csi_intern_string_t tmpl, *istring;
- csi_status_t status = CSI_STATUS_SUCCESS;
- tmpl.hash_entry.hash = _intern_string_hash (str, len);
- tmpl.len = len;
- tmpl.string = (char *) str;
- istring = _csi_hash_table_lookup (&ctx->strings, &tmpl.hash_entry);
- if (istring == NULL) {
- istring = _csi_perm_alloc (ctx,
- sizeof (csi_intern_string_t) + len + 1);
- if (istring != NULL) {
- istring->hash_entry.hash = tmpl.hash_entry.hash;
- istring->len = tmpl.len;
- istring->string = (char *) (istring + 1);
- memcpy (istring->string, str, len);
- istring->string[len] = '\0';
- status = _csi_hash_table_insert (&ctx->strings,
- &istring->hash_entry);
- if (_csi_unlikely (status)) {
- _csi_free (ctx, istring);
- return status;
- }
- } else
- return _csi_error (CSI_STATUS_NO_MEMORY);
- }
- *str_inout = istring->string;
- return CSI_STATUS_SUCCESS;
- }
- /* Public */
- static csi_t _csi_nil = { -1, CSI_STATUS_NO_MEMORY };
- csi_t *
- cairo_script_interpreter_create (void)
- {
- csi_t *ctx;
- ctx = malloc (sizeof (csi_t));
- if (ctx == NULL)
- return (csi_t *) &_csi_nil;
- _csi_init (ctx);
- return ctx;
- }
- void
- cairo_script_interpreter_install_hooks (csi_t *ctx,
- const csi_hooks_t *hooks)
- {
- if (ctx->status)
- return;
- ctx->hooks = *hooks;
- }
- cairo_status_t
- cairo_script_interpreter_run (csi_t *ctx, const char *filename)
- {
- csi_object_t file;
- if (ctx->status)
- return ctx->status;
- if (ctx->finished)
- return ctx->status = CSI_STATUS_INTERPRETER_FINISHED;
- ctx->status = csi_file_new (ctx, &file, filename, "r");
- if (ctx->status)
- return ctx->status;
- file.type |= CSI_OBJECT_ATTR_EXECUTABLE;
- ctx->status = csi_object_execute (ctx, &file);
- csi_object_free (ctx, &file);
- return ctx->status;
- }
- cairo_status_t
- cairo_script_interpreter_feed_stream (csi_t *ctx, FILE *stream)
- {
- csi_object_t file;
- if (ctx->status)
- return ctx->status;
- if (ctx->finished)
- return ctx->status = CSI_STATUS_INTERPRETER_FINISHED;
- ctx->status = csi_file_new_for_stream (ctx, &file, stream);
- if (ctx->status)
- return ctx->status;
- file.type |= CSI_OBJECT_ATTR_EXECUTABLE;
- ctx->status = csi_object_execute (ctx, &file);
- csi_object_free (ctx, &file);
- return ctx->status;
- }
- cairo_status_t
- cairo_script_interpreter_feed_string (csi_t *ctx, const char *line, int len)
- {
- csi_object_t file;
- if (ctx->status)
- return ctx->status;
- if (ctx->finished)
- return ctx->status = CSI_STATUS_INTERPRETER_FINISHED;
- if (len < 0)
- len = strlen (line);
- ctx->status = csi_file_new_for_bytes (ctx, &file, line, len);
- if (ctx->status)
- return ctx->status;
- file.type |= CSI_OBJECT_ATTR_EXECUTABLE;
- ctx->status = csi_object_execute (ctx, &file);
- csi_object_free (ctx, &file);
- return ctx->status;
- }
- unsigned int
- cairo_script_interpreter_get_line_number (csi_t *ctx)
- {
- return ctx->scanner.line_number + 1; /* 1 index based */
- }
- csi_t *
- cairo_script_interpreter_reference (csi_t *ctx)
- {
- ctx->ref_count++;
- return ctx;
- }
- slim_hidden_def (cairo_script_interpreter_reference);
- cairo_status_t
- cairo_script_interpreter_finish (csi_t *ctx)
- {
- csi_status_t status;
- status = ctx->status;
- if (! ctx->finished) {
- _csi_finish (ctx);
- ctx->finished = 1;
- } else if (status == CSI_STATUS_SUCCESS) {
- status = ctx->status = CSI_STATUS_INTERPRETER_FINISHED;
- }
- return status;
- }
- static void
- _csi_fini (csi_t *ctx)
- {
- if (! ctx->finished)
- _csi_finish (ctx);
- if (ctx->free_array != NULL)
- csi_array_free (ctx, ctx->free_array);
- if (ctx->free_dictionary != NULL)
- csi_dictionary_free (ctx, ctx->free_dictionary);
- if (ctx->free_string != NULL)
- csi_string_free (ctx, ctx->free_string);
- _csi_slab_fini (ctx);
- _csi_perm_fini (ctx);
- }
- cairo_status_t
- cairo_script_interpreter_destroy (csi_t *ctx)
- {
- csi_status_t status;
- status = ctx->status;
- if (--ctx->ref_count)
- return status;
- _csi_fini (ctx);
- free (ctx);
- return status;
- }
- slim_hidden_def (cairo_script_interpreter_destroy);
- cairo_status_t
- cairo_script_interpreter_translate_stream (FILE *stream,
- cairo_write_func_t write_func,
- void *closure)
- {
- csi_t ctx;
- csi_object_t src;
- csi_status_t status;
- _csi_init (&ctx);
- status = csi_file_new_for_stream (&ctx, &src, stream);
- if (status)
- goto BAIL;
- status = _csi_translate_file (&ctx, src.datum.file, write_func, closure);
- BAIL:
- csi_object_free (&ctx, &src);
- _csi_fini (&ctx);
- return status;
- }
|