summaryrefslogtreecommitdiff
path: root/libs/cairo-1.16.0/util/cairo-script/cairo-script-interpreter.c
diff options
context:
space:
mode:
Diffstat (limited to 'libs/cairo-1.16.0/util/cairo-script/cairo-script-interpreter.c')
-rw-r--r--libs/cairo-1.16.0/util/cairo-script/cairo-script-interpreter.c711
1 files changed, 711 insertions, 0 deletions
diff --git a/libs/cairo-1.16.0/util/cairo-script/cairo-script-interpreter.c b/libs/cairo-1.16.0/util/cairo-script/cairo-script-interpreter.c
new file mode 100644
index 0000000..a578ec4
--- /dev/null
+++ b/libs/cairo-1.16.0/util/cairo-script/cairo-script-interpreter.c
@@ -0,0 +1,711 @@
+/*
+ * 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;
+}