Add CairoScript interpreter

Add a CairoScript interpreter library and use it to replay the test output
for the CairoScript backend. The library is also used by the currently
standalone Sphinx debugger [git://anongit.freedesktop.org/~ickle/sphinx].
The syntax/operator semantics are not yet finalized, but are expected to
mature before the next stable release.
This commit is contained in:
Chris Wilson 2008-11-13 11:07:45 +00:00
parent a856371bef
commit cdfffc7420
15 changed files with 10801 additions and 33 deletions

View file

@ -236,18 +236,12 @@ CAIRO_ENABLE_SURFACE_BACKEND(directfb, directfb, no, [
dnl ===========================================================================
any2ppm_cs=no
CAIRO_ENABLE_SURFACE_BACKEND(script, script, no, [
test_script="yes"
csi_CFLAGS=
csi_LIBS=-lcairo-script-interpreter
if test "x$test_script" = "xyes"; then
AC_DEFINE([CAIRO_CAN_TEST_SCRIPT_SURFACE], 1,
[Define to 1 if the CairoScript backend can be tested])
else
AC_MSG_WARN([CairoScript backend will not be tested])
fi
AC_SUBST(csi_CFLAGS)
AC_SUBST(csi_LIBS)
test_script=yes
any2ppm_cs=yes
AC_DEFINE([CAIRO_CAN_TEST_SCRIPT_SURFACE], 1,
[Define to 1 if the CairoScript backend can be tested])
])
dnl ===========================================================================
@ -494,7 +488,8 @@ dnl Build the external converter if we have any of the test backends
AM_CONDITIONAL(BUILD_ANY2PPM,
test "x$any2ppm_svg" = "xyes" \
-o "x$any2ppm_pdf" = "xyes" \
-o "x$any2ppm_ps" = "xyes")
-o "x$any2ppm_ps" = "xyes" \
-o "x$any2ppm_cs" = "xyes")
dnl ===========================================================================
dnl The tracing utility requires LD_PRELOAD, so only build it for systems
@ -558,6 +553,7 @@ test/Makefile
test/pdiff/Makefile
perf/Makefile
util/Makefile
util/cairo-script/Makefile
util/cairo-trace/Makefile
util/cairo-trace/cairo-trace
doc/Makefile

View file

@ -1170,10 +1170,10 @@ png_flatten_LDADD = $(top_builddir)/src/libcairo.la $(CAIRO_LDADD)
if BUILD_ANY2PPM
check_PROGRAMS += any2ppm
any2ppm_CFLAGS = $(POPPLER_CFLAGS) $(LIBRSVG_CFLAGS) $(LIBSPECTRE_CFLAGS) $(csi_CFLAGS)
any2ppm_CFLAGS = $(POPPLER_CFLAGS) $(LIBRSVG_CFLAGS) $(LIBSPECTRE_CFLAGS)
# add LDADD, so poppler/librsvg uses "our" cairo
any2ppm_LDFLAGS = $(CAIRO_TEST_UNDEFINED_LDFLAGS)
any2ppm_LDADD = $(top_builddir)/src/libcairo.la $(CAIRO_LDADD) $(POPPLER_LIBS) $(LIBRSVG_LIBS) $(LIBSPECTRE_LIBS) $(csi_LIBS)
any2ppm_LDADD = $(top_builddir)/util/cairo-script/libcairo-script-interpreter.la $(top_builddir)/src/libcairo.la $(CAIRO_LDADD) $(POPPLER_LIBS) $(LIBRSVG_LIBS) $(LIBSPECTRE_LIBS)
endif
if CAIRO_CAN_TEST_PDF_SURFACE

View file

@ -171,12 +171,15 @@ write_ppm (cairo_surface_t *surface, int fd)
format = cairo_image_surface_get_format (surface);
if (format == CAIRO_FORMAT_ARGB32) {
/* see if we can convert to a standard ppm type and trim a few bytes */
cairo_bool_t uses_alpha = FALSE;
const unsigned char *alpha = data;
for (j = height * stride; j-- && uses_alpha == FALSE; alpha += 4)
uses_alpha = *alpha != 0xff;
if (! uses_alpha)
format = CAIRO_FORMAT_RGB24;
for (j = height; j--; alpha += stride) {
for (i = 0; i < width; i++) {
if ((*(unsigned int *) (alpha+4*i) & 0xff000000) != 0xff000000)
goto done;
}
}
format = CAIRO_FORMAT_RGB24;
done: ;
}
switch (format) {
@ -236,14 +239,12 @@ write_ppm (cairo_surface_t *surface, int fd)
#if CAIRO_CAN_TEST_SCRIPT_SURFACE
static cairo_surface_t *
_create_image (void *closure,
double width, double height,
csi_object_t *dictionary)
double width, double height)
//csi_object_t *dictionary)
{
cairo_surface_t **out = closure;
cairo_surface_t *surface;
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
return *out = cairo_surface_reference (surface);
*out = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
return cairo_surface_reference (*out);
}
static const char *
@ -253,17 +254,21 @@ _cairo_script_render_page (const char *filename,
cairo_script_interpreter_t *csi;
cairo_surface_t *surface = NULL;
cairo_status_t status;
const cairo_script_interpreter_hooks_t hooks = {
.closure = &surface,
.surface_create = _create_image,
};
csi = csi_create ();
csi_set_surface_create_function (csi, _create_image, &surface);
csi_run (csi, filename);
status = csi_destroy (csi);
if (status || surface == NULL) {
cairo_surface_destroy (surface);
csi = cairo_script_interpreter_create ();
cairo_script_interpreter_install_hooks (csi, &hooks);
cairo_script_interpreter_run (csi, filename);
status = cairo_script_interpreter_destroy (csi);
if (surface == NULL) {
return "cairo-script interpreter failed";
}
status = cairo_surface_status (surface);
if (status == CAIRO_STATUS_SUCCESS)
status = cairo_surface_status (surface);
if (status) {
cairo_surface_destroy (surface);
return cairo_status_to_string (status);

View file

@ -1,6 +1,6 @@
include $(top_srcdir)/build/Makefile.am.common
SUBDIRS = .
SUBDIRS = . cairo-script
if BUILD_TRACE
SUBDIRS += cairo-trace

17
util/cairo-script/COPYING Normal file
View file

@ -0,0 +1,17 @@
Cairo is free software.
Every source file in the implementation of cairo is available to be
redistributed and/or modified under the terms of either the GNU Lesser
General Public License (LGPL) version 2.1 or the Mozilla Public
License (MPL) version 1.1. Some files are available under more
liberal terms, but we believe that in all cases, each file may be used
under either the LGPL or the MPL.
See the following files in this directory for the precise terms and
conditions of either license:
COPYING-LGPL-2.1
COPYING-MPL-1.1
Please see each file in the implementation for copyright and licensing
information, (in the opening comment of each file).

View file

@ -0,0 +1,21 @@
lib_LTLIBRARIES = libcairo-script-interpreter.la
cairoincludedir=$(includedir)/cairo
cairoinclude_HEADERS = cairo-script-interpreter.h
libcairo_script_interpreter_la_SOURCES = \
cairo-script-private.h \
cairo-script-file.c \
cairo-script-hash.c \
cairo-script-interpreter.c \
cairo-script-objects.c \
cairo-script-operators.c \
cairo-script-scanner.c \
cairo-script-stack.c \
$(NULL)
libcairo_script_interpreter_la_CPPFLAGS = -I$(top_srcdir)/src
libcairo_script_interpreter_la_CFLAGS = $(CAIRO_CFLAGS)
libcairo_script_interpreter_la_LDFLAGS = -version-info $(CAIRO_LIBTOOL_VERSION_INFO) -no-undefined $(export_symbols)
libcairo_script_interpreter_la_LIBADD = -lz $(CAIRO_LIBS) -L$(top_builddir)/src -lcairo
EXTRA_DIST = \
COPYING

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,448 @@
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2004 Red Hat, Inc.
* Copyright © 2005 Red Hat, Inc.
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Red Hat, Inc.
*
* Contributor(s):
* Keith Packard <keithp@keithp.com>
* Graydon Hoare <graydon@redhat.com>
* Carl Worth <cworth@cworth.org>
*/
#include "cairo-script-private.h"
#include <stdlib.h>
/*
* An entry can be in one of three states:
*
* FREE: Entry has never been used, terminates all searches.
* Appears in the table as a %NULL pointer.
*
* DEAD: Entry had been live in the past. A dead entry can be reused
* but does not terminate a search for an exact entry.
* Appears in the table as a pointer to DEAD_ENTRY.
*
* LIVE: Entry is currently being used.
* Appears in the table as any non-%NULL, non-DEAD_ENTRY pointer.
*/
#define DEAD_ENTRY ((csi_hash_entry_t *) 0x1)
#define ENTRY_IS_FREE(entry) ((entry) == NULL)
#define ENTRY_IS_DEAD(entry) ((entry) == DEAD_ENTRY)
#define ENTRY_IS_LIVE(entry) ((entry) > DEAD_ENTRY)
/* We expect keys will not be destroyed frequently, so our table does not
* contain any explicit shrinking code nor any chain-coalescing code for
* entries randomly deleted by memory pressure (except during rehashing, of
* course). These assumptions are potentially bad, but they make the
* implementation straightforward.
*
* Revisit later if evidence appears that we're using excessive memory from
* a mostly-dead table.
*
* This table is open-addressed with double hashing. Each table size is a
* prime chosen to be a little more than double the high water mark for a
* given arrangement, so the tables should remain < 50% full. The table
* size makes for the "first" hash modulus; a second prime (2 less than the
* first prime) serves as the "second" hash modulus, which is co-prime and
* thus guarantees a complete permutation of table indices.
*
* This structure, and accompanying table, is borrowed/modified from the
* file xserver/render/glyph.c in the freedesktop.org x server, with
* permission (and suggested modification of doubling sizes) by Keith
* Packard.
*/
static const csi_hash_table_arrangement_t hash_table_arrangements [] = {
{ 16, 43, 41 },
{ 32, 73, 71 },
{ 64, 151, 149 },
{ 128, 283, 281 },
{ 256, 571, 569 },
{ 512, 1153, 1151 },
{ 1024, 2269, 2267 },
{ 2048, 4519, 4517 },
{ 4096, 9013, 9011 },
{ 8192, 18043, 18041 },
{ 16384, 36109, 36107 },
{ 32768, 72091, 72089 },
{ 65536, 144409, 144407 },
{ 131072, 288361, 288359 },
{ 262144, 576883, 576881 },
{ 524288, 1153459, 1153457 },
{ 1048576, 2307163, 2307161 },
{ 2097152, 4613893, 4613891 },
{ 4194304, 9227641, 9227639 },
{ 8388608, 18455029, 18455027 },
{ 16777216, 36911011, 36911009 },
{ 33554432, 73819861, 73819859 },
{ 67108864, 147639589, 147639587 },
{ 134217728, 295279081, 295279079 },
{ 268435456, 590559793, 590559791 }
};
#define NUM_HASH_TABLE_ARRANGEMENTS ARRAY_LENGTH (hash_table_arrangements)
/**
* _csi_hash_table_create:
* @keys_equal: a function to return %TRUE if two keys are equal
*
* Creates a new hash table which will use the keys_equal() function
* to compare hash keys. Data is provided to the hash table in the
* form of user-derived versions of #csi_hash_entry_t. A hash entry
* must be able to hold both a key (including a hash code) and a
* value. Sometimes only the key will be necessary, (as in
* _csi_hash_table_remove), and other times both a key and a value
* will be necessary, (as in _csi_hash_table_insert).
*
* See #csi_hash_entry_t for more details.
*
* Return value: the new hash table or %NULL if out of memory.
**/
csi_status_t
_csi_hash_table_init (csi_hash_table_t *hash_table,
csi_hash_keys_equal_func_t keys_equal)
{
hash_table->keys_equal = keys_equal;
hash_table->arrangement = &hash_table_arrangements[0];
hash_table->entries = calloc (hash_table->arrangement->size,
sizeof(csi_hash_entry_t *));
if (hash_table->entries == NULL)
return _csi_error (CAIRO_STATUS_NO_MEMORY);
hash_table->live_entries = 0;
hash_table->iterating = 0;
return CSI_STATUS_SUCCESS;
}
/**
* _csi_hash_table_destroy:
* @hash_table: an empty hash table to destroy
*
* Immediately destroys the given hash table, freeing all resources
* associated with it.
*
* WARNING: The hash_table must have no live entries in it before
* _csi_hash_table_destroy is called. It is a fatal error otherwise,
* and this function will halt. The rationale for this behavior is to
* avoid memory leaks and to avoid needless complication of the API
* with destroy notifiy callbacks.
*
* WARNING: The hash_table must have no running iterators in it when
* _csi_hash_table_destroy is called. It is a fatal error otherwise,
* and this function will halt.
**/
void
_csi_hash_table_fini (csi_hash_table_t *hash_table)
{
free (hash_table->entries);
}
static csi_hash_entry_t **
_csi_hash_table_lookup_unique_key (csi_hash_table_t *hash_table,
csi_hash_entry_t *key)
{
unsigned long table_size, i, idx, step;
csi_hash_entry_t **entry;
table_size = hash_table->arrangement->size;
idx = key->hash % table_size;
entry = &hash_table->entries[idx];
if (! ENTRY_IS_LIVE (*entry))
return entry;
i = 1;
step = key->hash % hash_table->arrangement->rehash;
if (step == 0)
step = 1;
do {
idx += step;
if (idx >= table_size)
idx -= table_size;
entry = &hash_table->entries[idx];
if (! ENTRY_IS_LIVE (*entry))
return entry;
} while (++i < table_size);
return NULL;
}
/**
* _csi_hash_table_resize:
* @hash_table: a hash table
*
* Resize the hash table if the number of entries has gotten much
* bigger or smaller than the ideal number of entries for the current
* size.
*
* Return value: %CAIRO_STATUS_SUCCESS if successful or
* %CAIRO_STATUS_NO_MEMORY if out of memory.
**/
static csi_status_t
_csi_hash_table_resize (csi_hash_table_t *hash_table)
{
csi_hash_table_t tmp;
unsigned long new_size, i;
/* This keeps the hash table between 25% and 50% full. */
unsigned long high = hash_table->arrangement->high_water_mark;
unsigned long low = high >> 2;
if (hash_table->live_entries >= low && hash_table->live_entries <= high)
return CAIRO_STATUS_SUCCESS;
tmp = *hash_table;
if (hash_table->live_entries > high) {
tmp.arrangement = hash_table->arrangement + 1;
/* This code is being abused if we can't make a table big enough. */
} else { /* hash_table->live_entries < low */
/* Can't shrink if we're at the smallest size */
if (hash_table->arrangement == &hash_table_arrangements[0])
return CAIRO_STATUS_SUCCESS;
tmp.arrangement = hash_table->arrangement - 1;
}
new_size = tmp.arrangement->size;
tmp.entries = calloc (new_size, sizeof (csi_hash_entry_t*));
if (tmp.entries == NULL)
return _csi_error (CAIRO_STATUS_NO_MEMORY);
for (i = 0; i < hash_table->arrangement->size; ++i) {
if (ENTRY_IS_LIVE (hash_table->entries[i])) {
*_csi_hash_table_lookup_unique_key (&tmp, hash_table->entries[i])
= hash_table->entries[i];
}
}
free (hash_table->entries);
hash_table->entries = tmp.entries;
hash_table->arrangement = tmp.arrangement;
return CAIRO_STATUS_SUCCESS;
}
/**
* _csi_hash_table_lookup:
* @hash_table: a hash table
* @key: the key of interest
*
* Performs a lookup in @hash_table looking for an entry which has a
* key that matches @key, (as determined by the keys_equal() function
* passed to _csi_hash_table_create).
*
* Return value: the matching entry, of %NULL if no match was found.
**/
void *
_csi_hash_table_lookup (csi_hash_table_t *hash_table,
csi_hash_entry_t *key)
{
csi_hash_entry_t **entry;
unsigned long table_size, i, idx, step;
table_size = hash_table->arrangement->size;
idx = key->hash % table_size;
entry = &hash_table->entries[idx];
if (ENTRY_IS_LIVE (*entry)) {
if (hash_table->keys_equal (key, *entry))
return *entry;
} else if (ENTRY_IS_FREE (*entry))
return NULL;
i = 1;
step = key->hash % hash_table->arrangement->rehash;
if (step == 0)
step = 1;
do {
idx += step;
if (idx >= table_size)
idx -= table_size;
entry = &hash_table->entries[idx];
if (ENTRY_IS_LIVE (*entry)) {
if (hash_table->keys_equal (key, *entry))
return *entry;
} else if (ENTRY_IS_FREE (*entry))
return NULL;
} while (++i < table_size);
return NULL;
}
/**
* _csi_hash_table_insert:
* @hash_table: a hash table
* @key_and_value: an entry to be inserted
*
* Insert the entry #key_and_value into the hash table.
*
* WARNING: There must not be an existing entry in the hash table
* with a matching key.
*
* WARNING: It is a fatal error to insert an element while
* an iterator is running
*
* Instead of using insert to replace an entry, consider just editing
* the entry obtained with _csi_hash_table_lookup. Or if absolutely
* necessary, use _csi_hash_table_remove first.
*
* Return value: %CAIRO_STATUS_SUCCESS if successful or
* %CAIRO_STATUS_NO_MEMORY if insufficient memory is available.
**/
csi_status_t
_csi_hash_table_insert (csi_hash_table_t *hash_table,
csi_hash_entry_t *key_and_value)
{
csi_status_t status;
hash_table->live_entries++;
status = _csi_hash_table_resize (hash_table);
if (_csi_unlikely (status)) {
/* abort the insert... */
hash_table->live_entries--;
return status;
}
*_csi_hash_table_lookup_unique_key (hash_table,
key_and_value) = key_and_value;
return CAIRO_STATUS_SUCCESS;
}
static csi_hash_entry_t **
_csi_hash_table_lookup_exact_key (csi_hash_table_t *hash_table,
csi_hash_entry_t *key)
{
unsigned long table_size, i, idx, step;
csi_hash_entry_t **entry;
table_size = hash_table->arrangement->size;
idx = key->hash % table_size;
entry = &hash_table->entries[idx];
if (*entry == key)
return entry;
i = 1;
step = key->hash % hash_table->arrangement->rehash;
if (step == 0)
step = 1;
do {
idx += step;
if (idx >= table_size)
idx -= table_size;
entry = &hash_table->entries[idx];
if (*entry == key)
return entry;
} while (++i < table_size);
return NULL;
}
/**
* _csi_hash_table_remove:
* @hash_table: a hash table
* @key: key of entry to be removed
*
* Remove an entry from the hash table which points to @key.
*
* Return value: %CAIRO_STATUS_SUCCESS if successful or
* %CAIRO_STATUS_NO_MEMORY if out of memory.
**/
void
_csi_hash_table_remove (csi_hash_table_t *hash_table,
csi_hash_entry_t *key)
{
*_csi_hash_table_lookup_exact_key (hash_table, key) = DEAD_ENTRY;
hash_table->live_entries--;
/* Check for table resize. Don't do this when iterating as this will
* reorder elements of the table and cause the iteration to potentially
* skip some elements. */
if (hash_table->iterating == 0) {
/* This call _can_ fail, but only in failing to allocate new
* memory to shrink the hash table. It does leave the table in a
* consistent state, and we've already succeeded in removing the
* entry, so we don't examine the failure status of this call. */
_csi_hash_table_resize (hash_table);
}
}
/**
* _csi_hash_table_foreach:
* @hash_table: a hash table
* @hash_callback: function to be called for each live entry
* @closure: additional argument to be passed to @hash_callback
*
* Call @hash_callback for each live entry in the hash table, in a
* non-specified order.
*
* Entries in @hash_table may be removed by code executed from @hash_callback.
*
* Entries may not be inserted to @hash_table, nor may @hash_table
* be destroyed by code executed from @hash_callback. The relevant
* functions will halt in these cases.
**/
void
_csi_hash_table_foreach (csi_hash_table_t *hash_table,
csi_hash_callback_func_t hash_callback,
void *closure)
{
unsigned long i;
csi_hash_entry_t *entry;
if (hash_table == NULL)
return;
/* Mark the table for iteration */
++hash_table->iterating;
for (i = 0; i < hash_table->arrangement->size; i++) {
entry = hash_table->entries[i];
if (ENTRY_IS_LIVE(entry))
hash_callback (entry, closure);
}
/* If some elements were deleted during the iteration,
* the table may need resizing. Just do this every time
* as the check is inexpensive.
*/
if (--hash_table->iterating == 0) {
/* Should we fail to shrink the hash table, it is left unaltered,
* and we don't need to propagate the error status. */
_csi_hash_table_resize (hash_table);
}
}

View file

@ -0,0 +1,473 @@
/*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 <cairo.h>
#include "cairo-script-private.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <byteswap.h>
#include <math.h>
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)
{
if (_csi_unlikely (ctx->status))
return NULL;
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)
{
if (_csi_unlikely (ctx->status))
return NULL;
return realloc (ptr, size);
}
void
_csi_free (csi_t *ctx, void *ptr)
{
if (_csi_unlikely (ptr == NULL))
return;
free (ptr);
}
void *
_csi_slab_alloc (csi_t *ctx, int size)
{
if (_csi_unlikely (ctx->status))
return NULL;
return malloc (size);
}
void
_csi_slab_free (csi_t *ctx, void *ptr, int size)
{
if (_csi_unlikely (ptr == NULL))
return;
free (ptr);
}
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;
status = csi_operator_new (ctx, &operator, def->op);
if (status)
return status;
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;
status = csi_integer_new (ctx, &constant, def->value);
if (status)
return status;
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;
const csi_operator_def_t *odef;
const csi_integer_constant_def_t *idef;
stack = &ctx->dstack;
status = _csi_stack_init (ctx, stack, 4);
if (status)
return status;
/* systemdict */
status = csi_dictionary_new (ctx, &obj);
if (status)
return status;
status = _csi_stack_push (ctx, stack, &obj);
if (status)
return status;
dict = obj.datum.dictionary;
/* fill systemdict with operators */
for (odef = _csi_operators (); odef->name != NULL; odef++) {
status = _add_operator (ctx, dict, odef);
if (status)
return status;
}
/* add constants */
for (idef = _csi_integer_constants (); idef->name != NULL; idef++) {
status = _add_integer_constant (ctx, dict, idef);
if (status)
return status;
}
/* and seal */
//dict.type &= ~CSI_OBJECT_ATTR_WRITABLE;
/* globaldict */
status = csi_dictionary_new (ctx, &obj);
if (status)
return status;
status = _csi_stack_push (ctx, stack, &obj);
if (status)
return status;
/* userdict */
status = csi_dictionary_new (ctx, &obj);
if (status)
return status;
status = _csi_stack_push (ctx, stack, &obj);
if (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;
unsigned int h = *p;
for (p += 1; --len; p++)
h = (h << 5) - h + *p;
return h;
}
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;
ctx->status = CSI_STATUS_SUCCESS;
ctx->ref_count = 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
_intern_string_pluck (void *entry, void *closure)
{
csi_t *ctx = closure;
_csi_hash_table_remove (&ctx->strings, entry);
_csi_free (ctx, entry);
}
static void
_csi_fini (csi_t *ctx)
{
_csi_stack_fini (ctx, &ctx->ostack);
_csi_stack_fini (ctx, &ctx->dstack);
_csi_scanner_fini (ctx, &ctx->scanner);
_csi_hash_table_foreach (&ctx->strings, _intern_string_pluck, ctx);
_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;
if (len < 0)
len = strlen (str);
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_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 = calloc (1, 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;
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_string (csi_t *ctx, const char *line, int len)
{
csi_object_t file;
if (ctx->status)
return ctx->status;
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;
}
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_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);

View file

@ -0,0 +1,104 @@
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2008 Chris Wilson
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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>
*/
#ifndef CAIRO_SCRIPT_INTERPRETER_H
#define CAIRO_SCRIPT_INTERPRETER_H
#include <cairo.h>
#if CAIRO_HAS_SCRIPT_SURFACE
CAIRO_BEGIN_DECLS
typedef struct _cairo_script_interpreter cairo_script_interpreter_t;
/* XXX expose csi_dictionary_t and pass to hooks */
typedef void
(*csi_destroy_func_t) (void *closure,
void *ptr);
typedef cairo_surface_t *
(*csi_surface_create_func_t) (void *closure,
double width,
double height);
typedef cairo_t *
(*csi_context_create_func_t) (void *closure,
cairo_surface_t *surface);
typedef void
(*csi_show_page_func_t) (void *closure,
cairo_t *cr);
typedef void
(*csi_copy_page_func_t) (void *closure,
cairo_t *cr);
typedef struct _cairo_script_interpreter_hooks {
void *closure;
csi_surface_create_func_t surface_create;
csi_destroy_func_t surface_destroy;
csi_context_create_func_t context_create;
csi_destroy_func_t context_destroy;
csi_show_page_func_t show_page;
csi_copy_page_func_t copy_page;
} cairo_script_interpreter_hooks_t;
cairo_public cairo_script_interpreter_t *
cairo_script_interpreter_create (void);
cairo_public void
cairo_script_interpreter_install_hooks (cairo_script_interpreter_t *ctx,
const cairo_script_interpreter_hooks_t *hooks);
cairo_public cairo_status_t
cairo_script_interpreter_run (cairo_script_interpreter_t *ctx,
const char *filename);
cairo_public cairo_status_t
cairo_script_interpreter_feed_string (cairo_script_interpreter_t *ctx,
const char *line,
int len);
cairo_public cairo_script_interpreter_t *
cairo_script_interpreter_reference (cairo_script_interpreter_t *ctx);
cairo_public cairo_status_t
cairo_script_interpreter_destroy (cairo_script_interpreter_t *ctx);
CAIRO_END_DECLS
#else /*CAIRO_HAS_SCRIPT_SURFACE*/
# error Cairo was not compiled with support for the CairoScript backend
#endif /*CAIRO_HAS_SCRIPT_SURFACE*/
#endif /*CAIRO_SCRIPT_INTERPRETER_H*/

View file

@ -0,0 +1,666 @@
/*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 "cairo-script-private.h"
#include <string.h>
csi_status_t
csi_array_new (csi_t *ctx,
csi_object_t *obj)
{
csi_array_t *array;
csi_status_t status;
array = _csi_slab_alloc (ctx, sizeof (csi_array_t));
if (_csi_unlikely (array == NULL))
return _csi_error (CSI_STATUS_NO_MEMORY);
array->base.type = CSI_OBJECT_TYPE_ARRAY;
array->base.ref = 1;
status = _csi_stack_init (ctx, &array->stack, 32);
if (_csi_unlikely (status)) {
_csi_slab_free (ctx, array, sizeof (csi_array_t));
return status;
}
obj->type = CSI_OBJECT_TYPE_ARRAY;
obj->datum.array = array;
return CSI_STATUS_SUCCESS;
}
csi_status_t
csi_array_put (csi_t *ctx,
csi_array_t *array,
csi_integer_t elem,
csi_object_t *value)
{
if (_csi_unlikely (elem < 0))
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
if (_csi_unlikely (elem >= array->stack.len)) {
csi_status_t status;
status = _csi_stack_grow (ctx, &array->stack, elem + 1);
if (_csi_unlikely (status))
return status;
memset (array->stack.objects + array->stack.len,
0, (elem - array->stack.len + 1) * sizeof (csi_object_t));
array->stack.len = elem + 1;
}
csi_object_free (ctx, &array->stack.objects[elem]);
array->stack.objects[elem] = *csi_object_reference (value);
return CSI_STATUS_SUCCESS;
}
csi_status_t
csi_array_get (csi_t *ctx,
csi_array_t *array,
csi_integer_t elem,
csi_object_t *value)
{
if (_csi_unlikely (elem < 0))
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
if (_csi_unlikely (elem > array->stack.len))
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
*value = array->stack.objects[elem];
return CSI_STATUS_SUCCESS;
}
csi_status_t
csi_array_append (csi_t *ctx,
csi_array_t *array,
csi_object_t *obj)
{
return _csi_stack_push (ctx, &array->stack, csi_object_reference (obj));
}
inline csi_status_t
_csi_array_execute (csi_t *ctx, csi_array_t *array)
{
csi_integer_t i;
csi_status_t status;
if (_csi_unlikely (array->stack.len == 0))
return CSI_STATUS_SUCCESS;
for (i = 0; i < array->stack.len; i++) {
csi_object_t *obj = &array->stack.objects[i];
if (obj->type & CSI_OBJECT_ATTR_EXECUTABLE) {
if (obj->type == (CSI_OBJECT_TYPE_ARRAY |
CSI_OBJECT_ATTR_EXECUTABLE))
{
status = _csi_push_ostack_copy (ctx, &array->stack.objects[i]);
}
else
status = csi_object_execute (ctx, &array->stack.objects[i]);
} else
status = _csi_push_ostack_copy (ctx, &array->stack.objects[i]);
if (_csi_unlikely (status))
return status;
}
return CSI_STATUS_SUCCESS;
}
void
csi_array_free (csi_t *ctx, csi_array_t *array)
{
_csi_stack_fini (ctx, &array->stack);
_csi_slab_free (ctx, array, sizeof (csi_array_t));
}
csi_status_t
csi_boolean_new (csi_t *ctx,
csi_object_t *obj,
csi_boolean_t v)
{
obj->type = CSI_OBJECT_TYPE_BOOLEAN;
obj->datum.boolean = v;
return CSI_STATUS_SUCCESS;
}
static cairo_bool_t
_dictionary_name_equal (const void *_a, const void *_b)
{
const csi_dictionary_entry_t *a = _a;
const csi_dictionary_entry_t *b = _b;
return a->hash_entry.hash == b->hash_entry.hash;
}
csi_status_t
csi_dictionary_new (csi_t *ctx,
csi_object_t *obj)
{
csi_dictionary_t *dict;
csi_status_t status;
dict = _csi_slab_alloc (ctx, sizeof (csi_dictionary_t));
if (_csi_unlikely (dict == NULL))
return _csi_error (CSI_STATUS_NO_MEMORY);
dict->base.type = CSI_OBJECT_TYPE_DICTIONARY;
dict->base.ref = 1;
status = _csi_hash_table_init (&dict->hash_table, _dictionary_name_equal);
if (_csi_unlikely (status)) {
_csi_slab_free (ctx, dict, sizeof (csi_dictionary_t));
return status;
}
obj->type = CSI_OBJECT_TYPE_DICTIONARY;
obj->datum.dictionary = dict;
return CSI_STATUS_SUCCESS;
}
struct _dictionary_entry_pluck {
csi_t *ctx;
csi_hash_table_t *hash_table;
};
static void
_dictionary_entry_pluck (void *entry, void *data)
{
csi_dictionary_entry_t *dict_entry;
struct _dictionary_entry_pluck *pluck_data;
dict_entry = entry;
pluck_data = data;
_csi_hash_table_remove (pluck_data->hash_table, entry);
csi_object_free (pluck_data->ctx, &dict_entry->value);
_csi_slab_free (pluck_data->ctx, entry, sizeof (csi_dictionary_entry_t));
}
void
csi_dictionary_free (csi_t *ctx,
csi_dictionary_t *dict)
{
struct _dictionary_entry_pluck data;
data.ctx = ctx;
data.hash_table = &dict->hash_table;
_csi_hash_table_foreach (&dict->hash_table,
_dictionary_entry_pluck,
&data);
_csi_hash_table_fini (&dict->hash_table);
_csi_slab_free (ctx, dict, sizeof (csi_dictionary_t));
}
csi_status_t
csi_dictionary_put (csi_t *ctx,
csi_dictionary_t *dict,
csi_name_t name,
csi_object_t *value)
{
csi_dictionary_entry_t *entry;
csi_status_t status;
entry = _csi_hash_table_lookup (&dict->hash_table,
(csi_hash_entry_t *) &name);
if (entry != NULL) {
/* replace the existing entry */
csi_object_free (ctx, &entry->value);
entry->value = *csi_object_reference (value);
return CSI_STATUS_SUCCESS;
}
entry = _csi_slab_alloc (ctx, sizeof (*entry));
if (_csi_unlikely (entry == NULL))
return _csi_error (CSI_STATUS_NO_MEMORY);
entry->hash_entry.hash = name;
status = _csi_hash_table_insert (&dict->hash_table, &entry->hash_entry);
if (_csi_unlikely (status)) {
_csi_slab_free (ctx, entry, sizeof (*entry));
return status;
}
entry->value = *csi_object_reference (value);
return CSI_STATUS_SUCCESS;
}
csi_status_t
csi_dictionary_get (csi_t *ctx,
csi_dictionary_t *dict,
csi_name_t name,
csi_object_t *value)
{
csi_dictionary_entry_t *entry;
entry = _csi_hash_table_lookup (&dict->hash_table,
(csi_hash_entry_t *) &name);
if (_csi_unlikely (entry == NULL))
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
*value = entry->value;
return CSI_STATUS_SUCCESS;
}
csi_boolean_t
csi_dictionary_has (csi_dictionary_t *dict,
csi_name_t name)
{
return _csi_hash_table_lookup (&dict->hash_table,
(csi_hash_entry_t *) &name) != NULL;
}
void
csi_dictionary_remove (csi_t *ctx,
csi_dictionary_t *dict,
csi_name_t name)
{
csi_dictionary_entry_t *entry;
entry = _csi_hash_table_lookup (&dict->hash_table,
(csi_hash_entry_t *) &name);
if (entry != NULL) {
_csi_hash_table_remove (&dict->hash_table, &entry->hash_entry);
csi_object_free (ctx, &entry->value);
_csi_slab_free (ctx, entry, sizeof (csi_dictionary_entry_t));
}
}
csi_status_t
csi_integer_new (csi_t *ctx,
csi_object_t *obj,
csi_integer_t v)
{
obj->type = CSI_OBJECT_TYPE_INTEGER;
obj->datum.integer = v;
return CSI_STATUS_SUCCESS;
}
csi_status_t
csi_matrix_new (csi_t *ctx,
csi_object_t *obj)
{
csi_matrix_t *matrix;
matrix = _csi_slab_alloc (ctx, sizeof (csi_matrix_t));
if (_csi_unlikely (matrix == NULL))
return _csi_error (CSI_STATUS_NO_MEMORY);
matrix->base.type = CSI_OBJECT_TYPE_MATRIX;
matrix->base.ref = 1;
cairo_matrix_init_identity (&matrix->matrix);
obj->type = CSI_OBJECT_TYPE_MATRIX;
obj->datum.matrix = matrix;
return CSI_STATUS_SUCCESS;
}
csi_status_t
csi_matrix_new_from_array (csi_t *ctx,
csi_object_t *obj,
csi_array_t *array)
{
csi_matrix_t *matrix;
if (_csi_unlikely (array->stack.len != 6))
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
matrix = _csi_slab_alloc (ctx, sizeof (csi_matrix_t));
if (_csi_unlikely (matrix == NULL))
return _csi_error (CSI_STATUS_NO_MEMORY);
matrix->base.type = CSI_OBJECT_TYPE_MATRIX;
matrix->base.ref = 1;
cairo_matrix_init (&matrix->matrix,
csi_number_get_value (&array->stack.objects[0]),
csi_number_get_value (&array->stack.objects[1]),
csi_number_get_value (&array->stack.objects[2]),
csi_number_get_value (&array->stack.objects[3]),
csi_number_get_value (&array->stack.objects[4]),
csi_number_get_value (&array->stack.objects[5]));
obj->type = CSI_OBJECT_TYPE_MATRIX;
obj->datum.matrix = matrix;
return CSI_STATUS_SUCCESS;
}
csi_status_t
csi_matrix_new_from_matrix (csi_t *ctx,
csi_object_t *obj,
const cairo_matrix_t *m)
{
csi_matrix_t *matrix;
matrix = _csi_slab_alloc (ctx, sizeof (csi_matrix_t));
if (_csi_unlikely (matrix == NULL))
return _csi_error (CSI_STATUS_NO_MEMORY);
matrix->base.type = CSI_OBJECT_TYPE_MATRIX;
matrix->base.ref = 1;
matrix->matrix = *m;
obj->type = CSI_OBJECT_TYPE_MATRIX;
obj->datum.matrix = matrix;
return CSI_STATUS_SUCCESS;
}
csi_status_t
csi_matrix_new_from_values (csi_t *ctx,
csi_object_t *obj,
double v[6])
{
csi_matrix_t *matrix;
matrix = _csi_slab_alloc (ctx, sizeof (csi_matrix_t));
if (_csi_unlikely (matrix == NULL))
return _csi_error (CSI_STATUS_NO_MEMORY);
matrix->base.type = CSI_OBJECT_TYPE_MATRIX;
matrix->base.ref = 1;
cairo_matrix_init (&matrix->matrix, v[0], v[1], v[2], v[3], v[4], v[5]);
obj->type = CSI_OBJECT_TYPE_MATRIX;
obj->datum.matrix = matrix;
return CSI_STATUS_SUCCESS;
}
void
csi_matrix_free (csi_t *ctx,
csi_matrix_t *obj)
{
_csi_slab_free (ctx, obj, sizeof (csi_matrix_t));
}
csi_status_t
csi_name_new (csi_t *ctx,
csi_object_t *obj,
const char *str,
int len)
{
csi_status_t status;
status = _csi_intern_string (ctx, &str, len);
if (_csi_unlikely (status))
return status;
obj->type = CSI_OBJECT_TYPE_NAME;
obj->datum.name = (csi_name_t) str;
return CSI_STATUS_SUCCESS;
}
csi_status_t
csi_name_new_static (csi_t *ctx,
csi_object_t *obj,
const char *str)
{
csi_status_t status;
status = _csi_intern_string (ctx, &str, -1);
if (_csi_unlikely (status))
return status;
obj->type = CSI_OBJECT_TYPE_NAME;
obj->datum.name = (csi_name_t) str;
return CSI_STATUS_SUCCESS;
}
csi_status_t
csi_operator_new (csi_t *ctx,
csi_object_t *obj,
csi_operator_t op)
{
obj->type = CSI_OBJECT_TYPE_OPERATOR | CSI_OBJECT_ATTR_EXECUTABLE;
obj->datum.op = op;
return CSI_STATUS_SUCCESS;
}
csi_status_t
csi_real_new (csi_t *ctx,
csi_object_t *obj,
csi_real_t v)
{
obj->type = CSI_OBJECT_TYPE_REAL;
obj->datum.real = v;
return CSI_STATUS_SUCCESS;
}
csi_status_t
csi_string_new (csi_t *ctx,
csi_object_t *obj,
const char *str,
int len)
{
csi_string_t *string;
string = _csi_slab_alloc (ctx, sizeof (csi_string_t));
if (_csi_unlikely (string == NULL))
return _csi_error (CSI_STATUS_NO_MEMORY);
string->base.type = CSI_OBJECT_TYPE_STRING;
string->base.ref = 1;
if (len < 0)
len = strlen (str);
if (_csi_unlikely (len >= INT32_MAX)) {
_csi_slab_free (ctx, string, sizeof (csi_string_t));
return _csi_error (CSI_STATUS_NO_MEMORY);
}
string->string = _csi_alloc (ctx, len + 1);
if (_csi_unlikely (string->string == NULL)) {
_csi_slab_free (ctx, string, sizeof (csi_string_t));
return _csi_error (CSI_STATUS_NO_MEMORY);
}
memcpy (string->string, str, len);
string->string[len] = '\0';
string->len = len;
obj->type = CSI_OBJECT_TYPE_STRING;
obj->datum.string = string;
return CSI_STATUS_SUCCESS;
}
static inline csi_status_t
_csi_string_execute (csi_t *ctx, csi_string_t *string)
{
csi_status_t status;
csi_object_t obj;
if (_csi_unlikely (string->len == 0))
return CSI_STATUS_SUCCESS;
status = csi_file_new_for_bytes (ctx, &obj, string->string, string->len);
if (_csi_unlikely (status))
return status;
status = _csi_scan_file (ctx, &ctx->scanner, obj.datum.file);
csi_object_free (ctx, &obj);
return status;
}
void
csi_string_free (csi_t *ctx, csi_string_t *string)
{
_csi_free (ctx, string->string);
_csi_slab_free (ctx, string, sizeof (csi_string_t));
}
csi_status_t
csi_object_execute (csi_t *ctx, csi_object_t *obj)
{
csi_status_t status;
csi_object_t indirect;
INDIRECT:
switch (obj->type & CSI_OBJECT_TYPE_MASK) {
case CSI_OBJECT_TYPE_NAME:
status = _csi_name_lookup (ctx, obj->datum.name, &indirect);
if (_csi_unlikely (status))
return status;
if (indirect.type & CSI_OBJECT_ATTR_EXECUTABLE) {
obj = &indirect;
goto INDIRECT;
} else
return _csi_push_ostack_copy (ctx, &indirect);
case CSI_OBJECT_TYPE_OPERATOR:
return obj->datum.op (ctx);
case CSI_OBJECT_TYPE_ARRAY:
return _csi_array_execute (ctx, obj->datum.array);
case CSI_OBJECT_TYPE_FILE:
return _csi_file_execute (ctx, obj->datum.file);
case CSI_OBJECT_TYPE_STRING:
return _csi_string_execute (ctx, obj->datum.string);
default:
return _csi_push_ostack_copy (ctx, obj);
}
}
csi_object_t *
csi_object_reference (csi_object_t *obj)
{
if (CSI_OBJECT_IS_CAIRO (obj)) {
switch (obj->type & CSI_OBJECT_TYPE_MASK) {
case CSI_OBJECT_TYPE_CONTEXT:
cairo_reference (obj->datum.cr);
break;
case CSI_OBJECT_TYPE_FONT:
cairo_font_face_reference (obj->datum.font_face);
break;
case CSI_OBJECT_TYPE_PATTERN:
cairo_pattern_reference (obj->datum.pattern);
break;
case CSI_OBJECT_TYPE_SCALED_FONT:
cairo_scaled_font_reference (obj->datum.scaled_font);
break;
case CSI_OBJECT_TYPE_SURFACE:
cairo_surface_reference (obj->datum.surface);
break;
}
} else if (CSI_OBJECT_IS_COMPOUND (obj)) {
obj->datum.object->ref++;
}
return obj;
}
void
csi_object_free (csi_t *ctx,
csi_object_t *obj)
{
if (CSI_OBJECT_IS_CAIRO (obj)) {
switch (obj->type & CSI_OBJECT_TYPE_MASK) {
case CSI_OBJECT_TYPE_CONTEXT:
cairo_destroy (obj->datum.cr);
break;
case CSI_OBJECT_TYPE_FONT:
cairo_font_face_destroy (obj->datum.font_face);
break;
case CSI_OBJECT_TYPE_PATTERN:
cairo_pattern_destroy (obj->datum.pattern);
break;
case CSI_OBJECT_TYPE_SCALED_FONT:
cairo_scaled_font_destroy (obj->datum.scaled_font);
break;
case CSI_OBJECT_TYPE_SURFACE:
cairo_surface_destroy (obj->datum.surface);
break;
}
} else if (CSI_OBJECT_IS_COMPOUND (obj)) {
if (--obj->datum.object->ref)
return;
switch (obj->type & CSI_OBJECT_TYPE_MASK) {
case CSI_OBJECT_TYPE_ARRAY:
csi_array_free (ctx, obj->datum.array);
break;
case CSI_OBJECT_TYPE_DICTIONARY:
csi_dictionary_free (ctx, obj->datum.dictionary);
break;
case CSI_OBJECT_TYPE_FILE:
_csi_file_free (ctx, obj->datum.file);
break;
case CSI_OBJECT_TYPE_MATRIX:
csi_matrix_free (ctx, obj->datum.matrix);
break;
case CSI_OBJECT_TYPE_STRING:
csi_string_free (ctx, obj->datum.string);
break;
default:
break;
}
}
}
csi_status_t
csi_object_as_file (csi_t *ctx,
csi_object_t *src,
csi_object_t *file)
{
switch ((int) csi_object_get_type (src)) {
case CSI_OBJECT_TYPE_FILE:
*file = *csi_object_reference (src);
return CSI_STATUS_SUCCESS;
case CSI_OBJECT_TYPE_STRING:
return csi_file_new_from_string (ctx, file, src->datum.string);
case CSI_OBJECT_TYPE_ARRAY:
#if 0
if (src->type & CSI_OBJECT_ATTR_EXECUTABLE)
return _csi_file_new_from_procedure (cs, src);
#endif
default:
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,853 @@
/*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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>
*/
#ifndef CAIRO_SCRIPT_PRIVATE_H
#define CAIRO_SCRIPT_PRIVATE_H
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "cairo-script-interpreter.h"
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE (!FALSE)
#endif
#ifndef NULL
#define NULL (void *) 0
#endif
#if HAVE_STDINT_H
# include <stdint.h>
#elif HAVE_INTTYPES_H
# include <inttypes.h>
#elif HAVE_SYS_INT_TYPES_H
# include <sys/int_types.h>
#elif defined(_MSC_VER)
typedef __int8 int8_t;
typedef unsigned __int8 uint8_t;
typedef __int16 int16_t;
typedef unsigned __int16 uint16_t;
typedef __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
# ifndef HAVE_UINT64_T
# define HAVE_UINT64_T 1
# endif
#else
#error Cannot find definitions for fixed-width integral types (uint8_t, uint32_t, etc.)
#endif
#if __GNUC__ >= 3 && defined(__ELF__) && !defined(__sun)
# define slim_hidden_proto(name) slim_hidden_proto1(name, slim_hidden_int_name(name)) csi_private
# define slim_hidden_proto_no_warn(name) slim_hidden_proto1(name, slim_hidden_int_name(name)) csi_private_no_warn
# define slim_hidden_def(name) slim_hidden_def1(name, slim_hidden_int_name(name))
# define slim_hidden_int_name(name) INT_##name
# define slim_hidden_proto1(name, internal) \
extern __typeof (name) name \
__asm__ (slim_hidden_asmname (internal))
# define slim_hidden_def1(name, internal) \
extern __typeof (name) EXT_##name __asm__(slim_hidden_asmname(name)) \
__attribute__((__alias__(slim_hidden_asmname(internal))))
# define slim_hidden_ulp slim_hidden_ulp1(__USER_LABEL_PREFIX__)
# define slim_hidden_ulp1(x) slim_hidden_ulp2(x)
# define slim_hidden_ulp2(x) #x
# define slim_hidden_asmname(name) slim_hidden_asmname1(name)
# define slim_hidden_asmname1(name) slim_hidden_ulp #name
#else
# define slim_hidden_proto(name) int _csi_dummy_prototype(void)
# define slim_hidden_proto_no_warn(name) int _csi_dummy_prototype(void)
# define slim_hidden_def(name) int _csi_dummy_prototype(void)
#endif
#if __GNUC__ >= 3
#define csi_pure __attribute__((pure))
#define csi_const __attribute__((const))
#else
#define csi_pure
#define csi_const
#endif
#if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__)
#define _CSI_BOOLEAN_EXPR(expr) \
__extension__ ({ \
int _csi_boolean_var_; \
if (expr) \
_csi_boolean_var_ = 1; \
else \
_csi_boolean_var_ = 0; \
_csi_boolean_var_; \
})
#define _csi_likely(expr) (__builtin_expect (_CSI_BOOLEAN_EXPR(expr), 1))
#define _csi_unlikely(expr) (__builtin_expect (_CSI_BOOLEAN_EXPR(expr), 0))
#else
#define _csi_likely(expr) (expr)
#define _csi_unlikely(expr) (expr)
#endif
#ifdef __GNUC__
#ifndef offsetof
#define offsetof(type, member) \
((char *) &((type *) 0)->member - (char *) 0)
#endif
#define csi_container_of(ptr, type, member) ({ \
const typeof(((type *) 0)->member) *mptr__ = (ptr); \
(type *) ((char *) mptr__ - offsetof (type, member)); \
})
#else
#define csi_container_of(ptr, type, member) \
(type *)((char *) (ptr) - (char *) &((type *)0)->member)
#endif
/* slim_internal.h */
#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)) && defined(__ELF__) && !defined(__sun)
#define csi_private_no_warn __attribute__((__visibility__("hidden")))
#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550)
#define csi_private_no_warn __hidden
#else /* not gcc >= 3.3 and not Sun Studio >= 8 */
#define csi_private_no_warn
#endif
#undef ARRAY_LENGTH
#define ARRAY_LENGTH(__array) ((int) (sizeof (__array) / sizeof (__array[0])))
#ifndef WARN_UNUSED_RESULT
#define WARN_UNUSED_RESULT
#endif
/* Add attribute(warn_unused_result) if supported */
#define csi_warn WARN_UNUSED_RESULT
#define csi_private csi_private_no_warn csi_warn
#define CSI_BITSWAP8(c) ((((c) * 0x0802LU & 0x22110LU) | ((c) * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16)
#ifdef WORDS_BIGENDIAN
#define CSI_BITSWAP8_IF_LITTLE_ENDIAN(c) (c)
#else
#define CSI_BITSWAP8_IF_LITTLE_ENDIAN(c) CSI_BITSWAP8(c)
#endif
typedef enum _csi_status {
CSI_STATUS_SUCCESS = CAIRO_STATUS_SUCCESS,
CSI_STATUS_NO_MEMORY = CAIRO_STATUS_NO_MEMORY,
CSI_STATUS_INVALID_RESTORE = CAIRO_STATUS_INVALID_RESTORE,
CSI_STATUS_INVALID_POP_GROUP = CAIRO_STATUS_INVALID_POP_GROUP,
CSI_STATUS_NO_CURRENT_POINT = CAIRO_STATUS_NO_CURRENT_POINT,
CSI_STATUS_INVALID_MATRIX = CAIRO_STATUS_INVALID_MATRIX,
CSI_STATUS_INVALID_STATUS = CAIRO_STATUS_INVALID_STATUS,
CSI_STATUS_NULL_POINTER = CAIRO_STATUS_NULL_POINTER,
CSI_STATUS_INVALID_STRING = CAIRO_STATUS_INVALID_STRING,
CSI_STATUS_INVALID_PATH_DATA = CAIRO_STATUS_INVALID_PATH_DATA,
CSI_STATUS_READ_ERROR = CAIRO_STATUS_READ_ERROR,
CSI_STATUS_WRITE_ERROR = CAIRO_STATUS_WRITE_ERROR,
CSI_STATUS_SURFACE_FINISHED = CAIRO_STATUS_SURFACE_FINISHED,
CSI_STATUS_SURFACE_TYPE_MISMATCH = CAIRO_STATUS_SURFACE_TYPE_MISMATCH,
CSI_STATUS_PATTERN_TYPE_MISMATCH = CAIRO_STATUS_PATTERN_TYPE_MISMATCH,
CSI_STATUS_INVALID_CONTENT = CAIRO_STATUS_INVALID_CONTENT,
CSI_STATUS_INVALID_FORMAT = CAIRO_STATUS_INVALID_FORMAT,
CSI_STATUS_INVALID_VISUAL = CAIRO_STATUS_INVALID_VISUAL,
CSI_STATUS_FILE_NOT_FOUND = CAIRO_STATUS_FILE_NOT_FOUND,
CSI_STATUS_INVALID_DASH = CAIRO_STATUS_INVALID_DASH,
CSI_STATUS_INVALID_DSC_COMMENT = CAIRO_STATUS_INVALID_DSC_COMMENT,
CSI_STATUS_INVALID_INDEX = CAIRO_STATUS_INVALID_INDEX,
CSI_STATUS_CLIP_NOT_REPRESENTABLE = CAIRO_STATUS_CLIP_NOT_REPRESENTABLE,
CSI_STATUS_TEMP_FILE_ERROR = CAIRO_STATUS_TEMP_FILE_ERROR,
CSI_STATUS_INVALID_STRIDE = CAIRO_STATUS_INVALID_STRIDE,
CSI_STATUS_FONT_TYPE_MISMATCH = CAIRO_STATUS_FONT_TYPE_MISMATCH,
CSI_STATUS_USER_FONT_IMMUTABLE = CAIRO_STATUS_USER_FONT_IMMUTABLE,
CSI_STATUS_USER_FONT_ERROR = CAIRO_STATUS_USER_FONT_ERROR,
CSI_STATUS_NEGATIVE_COUNT = CAIRO_STATUS_NEGATIVE_COUNT,
CSI_STATUS_INVALID_CLUSTERS = CAIRO_STATUS_INVALID_CLUSTERS,
CSI_STATUS_INVALID_SLANT = CAIRO_STATUS_INVALID_SLANT,
CSI_STATUS_INVALID_WEIGHT = CAIRO_STATUS_INVALID_WEIGHT,
/* cairo-script-interpreter specific errors */
CSI_STATUS_INVALID_SCRIPT,
CSI_STATUS_SCRIPT_INVALID_TYPE,
CSI_STATUS_SCRIPT_INVALID_INDEX,
CSI_STATUS_SCRIPT_UNDEFINED_NAME,
_CSI_STATUS_SCRIPT_LAST_ERROR,
CSI_INT_STATUS_UNSUPPORTED
} csi_status_t;
typedef enum {
CSI_OBJECT_TYPE_NULL = 0,
/* atomics */
CSI_OBJECT_TYPE_BOOLEAN,
CSI_OBJECT_TYPE_INTEGER,
CSI_OBJECT_TYPE_MARK,
CSI_OBJECT_TYPE_NAME,
CSI_OBJECT_TYPE_OPERATOR,
CSI_OBJECT_TYPE_REAL,
/* compound */
CSI_OBJECT_TYPE_ARRAY = 0x8,
CSI_OBJECT_TYPE_DICTIONARY,
CSI_OBJECT_TYPE_FILE,
CSI_OBJECT_TYPE_MATRIX,
CSI_OBJECT_TYPE_STRING,
/* cairo */
CSI_OBJECT_TYPE_CONTEXT = 0x10,
CSI_OBJECT_TYPE_FONT,
CSI_OBJECT_TYPE_PATTERN,
CSI_OBJECT_TYPE_SCALED_FONT,
CSI_OBJECT_TYPE_SURFACE,
} csi_object_type_t;
#define CSI_OBJECT_IS_ATOM(OBJ) (((OBJ)->type & CSI_OBJECT_TYPE_MASK) < 0x08)
#define CSI_OBJECT_IS_COMPOUND(OBJ) ((OBJ)->type & 0x08)
#define CSI_OBJECT_IS_CAIRO(OBJ) ((OBJ)->type & 0x10)
enum { /* attributes */
CSI_OBJECT_ATTR_EXECUTABLE = 1 << 6,
CSI_OBJECT_ATTR_WRITABLE = 1 << 7,
};
#define CSI_OBJECT_ATTR_MASK (CSI_OBJECT_ATTR_EXECUTABLE | \
CSI_OBJECT_ATTR_WRITABLE)
#define CSI_OBJECT_TYPE_MASK (~CSI_OBJECT_ATTR_MASK)
typedef struct _cairo_script_interpreter csi_t;
typedef cairo_bool_t csi_boolean_t;
typedef csi_status_t (*csi_operator_t) (csi_t *);
typedef float csi_real_t;
typedef long csi_integer_t;
typedef long csi_name_t;
typedef struct _csi_array csi_array_t;
typedef struct _csi_buffer csi_buffer_t;
typedef struct _csi_compound_object csi_compound_object_t;
typedef struct _csi_dictionary csi_dictionary_t;
typedef struct _csi_file csi_file_t;
typedef struct _csi_hash_entry csi_hash_entry_t;
typedef struct _csi_hash_table csi_hash_table_t;
typedef struct _csi_hash_table_arrangement csi_hash_table_arrangement_t;
typedef struct _csi_list csi_list_t;
typedef struct _csi_matrix csi_matrix_t;
typedef struct _csi_object csi_object_t;
typedef struct _csi_scanner csi_scanner_t;
typedef struct _csi_stack csi_stack_t;
typedef struct _csi_string csi_string_t;
typedef cairo_bool_t
(*csi_hash_predicate_func_t) (void *entry);
typedef void
(*csi_hash_callback_func_t) (void *entry,
void *closure);
typedef cairo_bool_t
(*csi_hash_keys_equal_func_t) (const void *key_a, const void *key_b);
struct _csi_object {
csi_object_type_t type;
union {
cairo_t *cr;
cairo_font_face_t *font_face;
cairo_pattern_t *pattern;
cairo_scaled_font_t *scaled_font;
cairo_surface_t *surface;
csi_array_t *array;
csi_boolean_t boolean;
csi_compound_object_t *object;
csi_dictionary_t *dictionary;
csi_file_t *file;
csi_integer_t integer;
csi_matrix_t *matrix;
csi_operator_t op;
csi_name_t name;
csi_real_t real;
csi_string_t *string;
void *ptr;
} datum;
};
struct _csi_compound_object {
csi_object_type_t type;
unsigned int ref;
};
struct _csi_hash_entry {
unsigned long hash;
};
struct _csi_hash_table_arrangement {
unsigned long high_water_mark;
unsigned long size;
unsigned long rehash;
};
struct _csi_hash_table {
csi_hash_keys_equal_func_t keys_equal;
const csi_hash_table_arrangement_t *arrangement;
csi_hash_entry_t **entries;
unsigned long live_entries;
unsigned long iterating; /* Iterating, no insert, no resize */
};
/* simple, embedded doubly-linked links */
struct _csi_list {
struct _csi_list *next, *prev;
};
struct _csi_buffer {
csi_status_t status;
char *base, *ptr, *end;
unsigned int size;
};
struct _csi_stack {
csi_object_t *objects;
csi_integer_t len;
csi_integer_t size;
};
struct _csi_array {
csi_compound_object_t base;
csi_stack_t stack;
};
typedef struct _csi_dictionary_entry {
csi_hash_entry_t hash_entry;
csi_object_t value;
} csi_dictionary_entry_t;
struct _csi_dictionary {
csi_compound_object_t base;
csi_hash_table_t hash_table;
};
struct _csi_matrix {
csi_compound_object_t base;
cairo_matrix_t matrix;
};
struct _csi_string {
csi_compound_object_t base;
csi_integer_t len;
char *string;
};
typedef struct _csi_filter_funcs {
int (*filter_getc) (csi_file_t *);
void (*filter_putc) (csi_file_t *, int);
int (*filter_read) (csi_file_t *, uint8_t *, int);
void (*filter_destroy) (csi_t *, void *);
} csi_filter_funcs_t;
struct _csi_file {
csi_compound_object_t base;
enum {
STDIO,
BYTES,
PROCEDURE,
FILTER,
} type;
void *src;
void *data;
uint8_t *bp;
int rem;
const csi_filter_funcs_t *filter;
};
struct _csi_scanner {
csi_status_t status;
enum {
NONE,
TOKEN,
COMMENT,
STRING,
HEX,
BASE85,
} state;
csi_buffer_t buffer;
csi_stack_t procedure_stack;
csi_object_t build_procedure;
int string_p;
unsigned int accumulator;
unsigned int accumulator_count;
};
typedef cairo_script_interpreter_hooks_t csi_hooks_t;
struct _cairo_script_interpreter {
int ref_count;
csi_status_t status;
csi_hooks_t hooks;
csi_hash_table_t strings;
csi_stack_t ostack;
csi_stack_t dstack;
csi_scanner_t scanner;
/* caches of live data */
csi_list_t *_images;
csi_list_t *_faces;
};
typedef struct _csi_operator_def {
const char *name;
csi_operator_t op;
} csi_operator_def_t;
typedef struct _csi_integer_constant_def {
const char *name;
csi_integer_t value;
} csi_integer_constant_def_t;
/* cairo-script-file.c */
csi_private csi_status_t
csi_file_new (csi_t *ctx,
csi_object_t *obj,
const char *path, const char *mode);
csi_private csi_status_t
csi_file_new_for_bytes (csi_t *ctx,
csi_object_t *obj,
const char *bytes,
unsigned int length);
csi_private csi_status_t
csi_file_new_from_string (csi_t *ctx,
csi_object_t *obj,
csi_string_t *src);
csi_private csi_status_t
csi_file_new_ascii85_decode (csi_t *ctx,
csi_object_t *obj,
csi_dictionary_t *dict,
csi_object_t *src);
csi_private csi_status_t
csi_file_new_deflate_decode (csi_t *ctx,
csi_object_t *obj,
csi_dictionary_t *dict,
csi_object_t *src);
csi_private csi_status_t
_csi_file_execute (csi_t *ctx, csi_file_t *obj);
csi_private int
csi_file_getc (csi_file_t *obj);
csi_private int
csi_file_read (csi_file_t *obj, void *buf, int len);
csi_private void
csi_file_putc (csi_file_t *obj, int c);
csi_private void
csi_file_flush (csi_file_t *obj);
csi_private void
csi_file_close (csi_t *ctx, csi_file_t *obj);
csi_private void
_csi_file_free (csi_t *ctx, csi_file_t *obj);
csi_private csi_status_t
_csi_file_as_string (csi_t *ctx,
csi_file_t *file,
csi_object_t *obj);
/* cairo-script-hash.c */
csi_private csi_status_t
_csi_hash_table_init (csi_hash_table_t *hash_table,
csi_hash_keys_equal_func_t keys_equal);
csi_private void
_csi_hash_table_fini (csi_hash_table_t *hash_table);
csi_private void *
_csi_hash_table_lookup (csi_hash_table_t *hash_table,
csi_hash_entry_t *key);
csi_private csi_status_t
_csi_hash_table_insert (csi_hash_table_t *hash_table,
csi_hash_entry_t *entry);
csi_private void
_csi_hash_table_remove (csi_hash_table_t *hash_table,
csi_hash_entry_t *key);
csi_private void
_csi_hash_table_foreach (csi_hash_table_t *hash_table,
csi_hash_callback_func_t hash_callback,
void *closure);
/* cairo-script-interpreter.c */
csi_private void *
_csi_alloc (csi_t *ctx, int size);
csi_private void *
_csi_alloc0 (csi_t *ctx, int size);
csi_private void *
_csi_realloc (csi_t *ctx, void *ptr, int size);
csi_private void
_csi_free (csi_t *ctx, void *ptr);
csi_private void *
_csi_slab_alloc (csi_t *ctx, int size);
csi_private void
_csi_slab_free (csi_t *ctx, void *ptr, int size);
csi_private csi_status_t
csi_push_ostack (csi_t *ctx, csi_object_t *obj);
csi_private csi_status_t
_csi_name_define (csi_t *ctx, csi_name_t name, csi_object_t *obj);
csi_private csi_status_t
_csi_name_lookup (csi_t *ctx, csi_name_t name, csi_object_t *obj);
csi_private csi_status_t
_csi_name_undefine (csi_t *ctx, csi_name_t name);
csi_private csi_status_t
_csi_intern_string (csi_t *ctx, const char **str_inout, int len);
csi_private csi_status_t
_csi_error (csi_status_t status);
/* cairo-script-objects.c */
csi_private csi_status_t
csi_array_new (csi_t *ctx,
csi_object_t *obj);
csi_private csi_status_t
_csi_array_execute (csi_t *ctx, csi_array_t *array);
csi_private csi_status_t
csi_array_get (csi_t *ctx,
csi_array_t *array,
long elem,
csi_object_t *value);
csi_private csi_status_t
csi_array_put (csi_t *ctx,
csi_array_t *array,
csi_integer_t elem,
csi_object_t *value);
csi_private csi_status_t
csi_array_append (csi_t *ctx,
csi_array_t *array,
csi_object_t *obj);
csi_private void
csi_array_free (csi_t *ctx, csi_array_t *array);
csi_private csi_status_t
csi_boolean_new (csi_t *ctx,
csi_object_t *obj,
csi_boolean_t v);
csi_private csi_status_t
csi_dictionary_new (csi_t *ctx,
csi_object_t *obj);
csi_private csi_status_t
csi_dictionary_put (csi_t *ctx,
csi_dictionary_t *dict,
csi_name_t name,
csi_object_t *value);
csi_private csi_status_t
csi_dictionary_get (csi_t *ctx,
csi_dictionary_t *dict,
csi_name_t name,
csi_object_t *value);
csi_private csi_boolean_t
csi_dictionary_has (csi_dictionary_t *dict,
csi_name_t name);
csi_private void
csi_dictionary_remove (csi_t *ctx,
csi_dictionary_t *dict,
csi_name_t name);
csi_private void
csi_dictionary_free (csi_t *ctx,
csi_dictionary_t *dict);
csi_private csi_status_t
csi_integer_new (csi_t *ctx,
csi_object_t *obj,
csi_integer_t v);
csi_private csi_status_t
csi_matrix_new (csi_t *ctx,
csi_object_t *obj);
csi_private csi_status_t
csi_matrix_new_from_array (csi_t *ctx,
csi_object_t *obj,
csi_array_t *array);
csi_private csi_status_t
csi_matrix_new_from_matrix (csi_t *ctx,
csi_object_t *obj,
const cairo_matrix_t *m);
csi_private csi_status_t
csi_matrix_new_from_values (csi_t *ctx,
csi_object_t *obj,
double v[6]);
csi_private void
csi_matrix_free (csi_t *ctx,
csi_matrix_t *obj);
csi_private csi_status_t
csi_name_new (csi_t *ctx,
csi_object_t *obj,
const char *str,
int len);
csi_private csi_status_t
csi_name_new_static (csi_t *ctx,
csi_object_t *obj,
const char *str);
csi_private csi_status_t
csi_operator_new (csi_t *ctx,
csi_object_t *obj,
csi_operator_t op);
csi_private csi_status_t
csi_real_new (csi_t *ctx,
csi_object_t *obj,
csi_real_t v);
csi_private csi_status_t
csi_string_new (csi_t *ctx,
csi_object_t *obj,
const char *str,
int len);
csi_private void
csi_string_free (csi_t *ctx, csi_string_t *string);
csi_private csi_status_t
csi_object_execute (csi_t *ctx, csi_object_t *obj);
csi_private csi_object_t *
csi_object_reference (csi_object_t *obj);
csi_private void
csi_object_free (csi_t *ctx,
csi_object_t *obj);
csi_private csi_status_t
csi_object_as_file (csi_t *ctx,
csi_object_t *src,
csi_object_t *file);
/* cairo-script-operators.c */
csi_private const csi_operator_def_t *
_csi_operators (void);
csi_private const csi_integer_constant_def_t *
_csi_integer_constants (void);
/* cairo-script-scanner.c */
csi_private csi_status_t
_csi_scanner_init (csi_t *ctx, csi_scanner_t *scanner);
csi_private csi_status_t
_csi_scan_file (csi_t *ctx, csi_scanner_t *scan, csi_file_t *src);
csi_private void
_csi_scanner_fini (csi_t *ctx, csi_scanner_t *scanner);
/* cairo-script-stack.c */
csi_private csi_status_t
_csi_stack_init (csi_t *ctx, csi_stack_t *stack, csi_integer_t size);
csi_private void
_csi_stack_fini (csi_t *ctx, csi_stack_t *stack);
csi_private csi_status_t
_csi_stack_roll (csi_t *ctx,
csi_stack_t *stack,
csi_integer_t mod,
csi_integer_t n);
csi_private csi_status_t
_csi_stack_grow (csi_t *ctx, csi_stack_t *stack, csi_integer_t cnt);
csi_private csi_status_t
_csi_stack_push (csi_t *ctx, csi_stack_t *stack, const csi_object_t *obj);
csi_private csi_object_t *
_csi_stack_peek (csi_stack_t *stack, csi_integer_t i);
csi_private void
_csi_stack_pop (csi_t *ctx, csi_stack_t *stack, csi_integer_t count);
csi_private csi_status_t
_csi_stack_exch (csi_stack_t *stack);
static inline csi_object_type_t
csi_object_get_type (const csi_object_t *obj)
{
return obj->type & CSI_OBJECT_TYPE_MASK;
}
static inline csi_boolean_t
csi_object_is_procedure (const csi_object_t *obj)
{
return obj->type == (CSI_OBJECT_TYPE_ARRAY | CSI_OBJECT_ATTR_EXECUTABLE);
}
static inline csi_boolean_t
csi_object_is_number (const csi_object_t *obj)
{
switch ((int) csi_object_get_type (obj)) {
case CSI_OBJECT_TYPE_BOOLEAN:
case CSI_OBJECT_TYPE_INTEGER:
case CSI_OBJECT_TYPE_REAL:
return 1;
default:
return 0;
}
}
static inline double
csi_number_get_value (const csi_object_t *obj)
{
switch ((int) csi_object_get_type (obj)) {
case CSI_OBJECT_TYPE_BOOLEAN: return obj->datum.boolean;
case CSI_OBJECT_TYPE_INTEGER: return obj->datum.integer;
case CSI_OBJECT_TYPE_REAL: return obj->datum.real;
default: return 0.;
}
}
static inline csi_boolean_t
_csi_check_ostack (csi_t *ctx, csi_integer_t count)
{
return ctx->ostack.len >= count;
}
static inline csi_object_t *
_csi_peek_ostack (csi_t *ctx, csi_integer_t i)
{
return &ctx->ostack.objects[ctx->ostack.len - i -1];
}
static inline void
_csi_pop_ostack (csi_t *ctx, csi_integer_t count)
{
do
csi_object_free (ctx, &ctx->ostack.objects[--ctx->ostack.len]);
while (--count);
}
static inline csi_status_t
_csi_push_ostack_copy (csi_t *ctx, csi_object_t *obj)
{
return _csi_stack_push (ctx, &ctx->ostack, csi_object_reference (obj));
}
static inline csi_status_t
_csi_push_ostack (csi_t *ctx, csi_object_t *obj)
{
return _csi_stack_push (ctx, &ctx->ostack, obj);
}
static inline csi_status_t
_csi_push_ostack_boolean (csi_t *ctx, csi_boolean_t v)
{
csi_object_t obj;
obj.type = CSI_OBJECT_TYPE_BOOLEAN;
obj.datum.boolean = v;
return _csi_stack_push (ctx, &ctx->ostack, &obj);
}
static inline csi_status_t
_csi_push_ostack_integer (csi_t *ctx, csi_integer_t v)
{
csi_object_t obj;
obj.type = CSI_OBJECT_TYPE_INTEGER;
obj.datum.integer = v;
return _csi_stack_push (ctx, &ctx->ostack, &obj);
}
static inline csi_status_t
_csi_push_ostack_mark (csi_t *ctx)
{
csi_object_t obj;
obj.type = CSI_OBJECT_TYPE_MARK;
return _csi_stack_push (ctx, &ctx->ostack, &obj);
}
static inline csi_status_t
_csi_push_ostack_real (csi_t *ctx, csi_real_t v)
{
csi_object_t obj;
obj.type = CSI_OBJECT_TYPE_REAL;
obj.datum.real = v;
return _csi_stack_push (ctx, &ctx->ostack, &obj);
}
slim_hidden_proto_no_warn (cairo_script_interpreter_destroy);
slim_hidden_proto_no_warn (cairo_script_interpreter_reference);
#endif /* CAIRO_SCRIPT_PRIVATE_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,196 @@
/*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 "cairo-script-private.h"
#include <string.h>
csi_status_t
_csi_stack_init (csi_t *ctx, csi_stack_t *stack, csi_integer_t size)
{
csi_status_t status = CSI_STATUS_SUCCESS;
stack->len = 0;
stack->size = size;
/* assert ((unsigned) size < INT32_MAX / sizeof (csi_object_t)); */
stack->objects = _csi_alloc (ctx, stack->size * sizeof (csi_object_t));
if (_csi_unlikely (stack->objects == NULL))
status = _csi_error (CSI_STATUS_NO_MEMORY);
return status;
}
void
_csi_stack_fini (csi_t *ctx, csi_stack_t *stack)
{
csi_integer_t n;
for (n = 0; n < stack->len; n++)
csi_object_free (ctx, &stack->objects[n]);
_csi_free (ctx, stack->objects);
}
csi_status_t
_csi_stack_roll (csi_t *ctx,
csi_stack_t *stack,
csi_integer_t mod, csi_integer_t n)
{
csi_object_t stack_copy[128];
csi_object_t *copy;
csi_integer_t last, i, len;
switch (mod) { /* special cases */
case 1:
last = stack->len - 1;
stack_copy[0] = stack->objects[last];
for (i = last; --n; i--)
stack->objects[i] = stack->objects[i-1];
stack->objects[i] = stack_copy[0];
return CSI_STATUS_SUCCESS;
case -1:
last = stack->len - 1;
stack_copy[0] = stack->objects[i = last - n + 1];
for (; --n; i++)
stack->objects[i] = stack->objects[i+1];
stack->objects[i] = stack_copy[0];
return CSI_STATUS_SUCCESS;
}
/* fall back to a copy */
if (n > ARRAY_LENGTH (stack_copy)) {
if (_csi_unlikely ((unsigned) n > INT32_MAX / sizeof (csi_object_t)))
return _csi_error (CSI_STATUS_NO_MEMORY);
copy = _csi_alloc (ctx, n * sizeof (csi_object_t));
if (copy == NULL)
return _csi_error (CSI_STATUS_NO_MEMORY);
} else
copy = stack_copy;
i = stack->len - n;
memcpy (copy, stack->objects + i, n * sizeof (csi_object_t));
mod = -mod;
if (mod < 0)
mod += n;
last = mod;
for (len = n; n--; i++) {
stack->objects[i] = copy[last];
if (++last == len)
last = 0;
}
if (copy != stack_copy)
_csi_free (ctx, copy);
return CSI_STATUS_SUCCESS;
}
csi_status_t
_csi_stack_grow (csi_t *ctx, csi_stack_t *stack, csi_integer_t cnt)
{
csi_integer_t newsize;
csi_object_t *newstack;
if (_csi_likely (cnt <= stack->size))
return CSI_STATUS_SUCCESS;
if (_csi_unlikely ((unsigned) cnt >= INT32_MAX / sizeof (csi_object_t)))
return _csi_error (CSI_STATUS_NO_MEMORY);
newsize = stack->size;
do {
newsize *= 2;
} while (newsize <= cnt);
newstack = _csi_realloc (ctx,
stack->objects,
newsize * sizeof (csi_object_t));
if (_csi_unlikely (newstack == NULL))
return _csi_error (CSI_STATUS_NO_MEMORY);
stack->objects = newstack;
stack->size = newsize;
return CSI_STATUS_SUCCESS;
}
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)) {
csi_status_t status;
status = _csi_stack_grow (ctx, stack, stack->size + 1);
if (status)
return status;
}
stack->objects[stack->len++] = *obj;
return CSI_STATUS_SUCCESS;
}
csi_object_t *
_csi_stack_peek (csi_stack_t *stack, csi_integer_t i)
{
if (_csi_unlikely (stack->len < i))
return NULL;
return &stack->objects[stack->len - i -1];
}
void
_csi_stack_pop (csi_t *ctx, csi_stack_t *stack, csi_integer_t count)
{
if (_csi_unlikely (stack->len < count))
count = stack->len;
while (count--)
csi_object_free (ctx, &stack->objects[--stack->len]);
}
csi_status_t
_csi_stack_exch (csi_stack_t *stack)
{
csi_object_t tmp;
csi_integer_t n;
if (_csi_unlikely (stack->len < 2))
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
n = stack->len - 1;
tmp = stack->objects[n];
stack->objects[n] = stack->objects[n - 1];
stack->objects[n - 1] = tmp;
return CSI_STATUS_SUCCESS;
}