mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2025-12-20 23:20:10 +01:00
Hook into the scanner to write out binary version of the tokenized
objects -- note we bind executable names (i.e. check to see if is an
operator and substitute the name with an operator -- this breaks
overloading of operators by scripts).
By converting scripts to a binary form, they are more compact and
execute faster:
firefox-world-map.trace 526850146 bytes
bound.trace 275187755 bytes
[ # ] backend test min(s) median(s) stddev. count
[ 0] null bound 34.481 34.741 0.68% 3/3
[ 1] null firefox-world-map 89.635 89.716 0.19% 3/3
[ 0] drm bound 79.304 79.350 0.61% 3/3
[ 1] drm firefox-world-map 135.380 135.475 0.58% 3/3
[ 0] image bound 95.819 96.258 2.85% 3/3
[ 1] image firefox-world-map 156.889 156.935 1.36% 3/3
[ 0] xlib bound 539.130 550.220 1.40% 3/3
[ 1] xlib firefox-world-map 596.244 613.487 1.74% 3/3
This trace has a lot of complex paths and the use of binary floating point
reduces the file size by about 50%, with a commensurate reduction in scan
time and significant reduction in operator lookup overhead. Note that this
test is still IO/CPU bound on my i915 with its pitifully slow flash...
911 lines
22 KiB
C
911 lines
22 KiB
C
/*
|
|
* 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 <limits.h> /* INT_MAX */
|
|
#include <string.h>
|
|
|
|
csi_status_t
|
|
csi_array_new (csi_t *ctx,
|
|
csi_integer_t initial_size,
|
|
csi_object_t *obj)
|
|
|
|
{
|
|
csi_array_t *array;
|
|
|
|
if (ctx->free_array == NULL ||
|
|
ctx->free_array->stack.size <= initial_size)
|
|
{
|
|
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);
|
|
|
|
status = _csi_stack_init (ctx, &array->stack,
|
|
initial_size ? initial_size : 32);
|
|
if (_csi_unlikely (status)) {
|
|
_csi_slab_free (ctx, array, sizeof (csi_array_t));
|
|
return status;
|
|
}
|
|
} else {
|
|
array = ctx->free_array;
|
|
ctx->free_array = NULL;
|
|
}
|
|
|
|
array->base.type = CSI_OBJECT_TYPE_ARRAY;
|
|
array->base.ref = 1;
|
|
|
|
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)
|
|
{
|
|
#if CSI_DEBUG_MALLOC
|
|
_csi_stack_fini (ctx, &array->stack);
|
|
_csi_slab_free (ctx, array, sizeof (csi_array_t));
|
|
#else
|
|
csi_integer_t n;
|
|
|
|
for (n = 0; n < array->stack.len; n++)
|
|
csi_object_free (ctx, &array->stack.objects[n]);
|
|
array->stack.len = 0;
|
|
|
|
if (ctx->free_array != NULL) {
|
|
if (array->stack.size > ctx->free_array->stack.size) {
|
|
csi_array_t *tmp = ctx->free_array;
|
|
ctx->free_array = array;
|
|
array = tmp;
|
|
}
|
|
|
|
_csi_stack_fini (ctx, &array->stack);
|
|
_csi_slab_free (ctx, array, sizeof (csi_array_t));
|
|
} else
|
|
ctx->free_array = array;
|
|
#endif
|
|
}
|
|
|
|
static cairo_bool_t
|
|
_dictionary_name_equal (const void *_a, const void *_b)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
csi_status_t
|
|
csi_dictionary_new (csi_t *ctx,
|
|
csi_object_t *obj)
|
|
|
|
{
|
|
csi_dictionary_t *dict;
|
|
|
|
if (ctx->free_dictionary != NULL) {
|
|
dict = ctx->free_dictionary;
|
|
ctx->free_dictionary = NULL;
|
|
} else {
|
|
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);
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
dict->base.type = CSI_OBJECT_TYPE_DICTIONARY;
|
|
dict->base.ref = 1;
|
|
|
|
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);
|
|
|
|
#if CSI_DEBUG_MALLOC
|
|
_csi_hash_table_fini (&dict->hash_table);
|
|
_csi_slab_free (ctx, dict, sizeof (csi_dictionary_t));
|
|
#else
|
|
if (ctx->free_dictionary != NULL) {
|
|
_csi_hash_table_fini (&dict->hash_table);
|
|
_csi_slab_free (ctx, dict, sizeof (csi_dictionary_t));
|
|
} else
|
|
ctx->free_dictionary = dict;
|
|
#endif
|
|
}
|
|
|
|
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_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, strlen (str));
|
|
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_string_new (csi_t *ctx,
|
|
csi_object_t *obj,
|
|
const char *str,
|
|
int len)
|
|
{
|
|
csi_string_t *string;
|
|
|
|
if (len < 0)
|
|
len = strlen (str);
|
|
if (_csi_unlikely (len >= INT_MAX))
|
|
return _csi_error (CSI_STATUS_NO_MEMORY);
|
|
|
|
if (ctx->free_string == NULL || ctx->free_string->len <= len) {
|
|
string = _csi_slab_alloc (ctx, sizeof (csi_string_t));
|
|
if (_csi_unlikely (string == NULL))
|
|
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);
|
|
}
|
|
} else {
|
|
string = ctx->free_string;
|
|
ctx->free_string = NULL;
|
|
}
|
|
|
|
if (str != NULL) {
|
|
memcpy (string->string, str, len);
|
|
string->string[len] = '\0';
|
|
}
|
|
string->len = len;
|
|
|
|
string->base.type = CSI_OBJECT_TYPE_STRING;
|
|
string->base.ref = 1;
|
|
|
|
obj->type = CSI_OBJECT_TYPE_STRING;
|
|
obj->datum.string = string;
|
|
|
|
return CSI_STATUS_SUCCESS;
|
|
}
|
|
|
|
csi_status_t
|
|
csi_string_new_from_bytes (csi_t *ctx,
|
|
csi_object_t *obj,
|
|
char *bytes,
|
|
unsigned int len)
|
|
{
|
|
csi_string_t *string;
|
|
|
|
if (_csi_unlikely (len >= INT_MAX))
|
|
return _csi_error (CSI_STATUS_NO_MEMORY);
|
|
|
|
string = _csi_slab_alloc (ctx, sizeof (csi_string_t));
|
|
if (_csi_unlikely (string == NULL))
|
|
return _csi_error (CSI_STATUS_NO_MEMORY);
|
|
|
|
string->string = bytes;
|
|
string->len = len;
|
|
|
|
string->base.type = CSI_OBJECT_TYPE_STRING;
|
|
string->base.ref = 1;
|
|
|
|
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, obj.datum.file);
|
|
csi_object_free (ctx, &obj);
|
|
|
|
return status;
|
|
}
|
|
|
|
void
|
|
csi_string_free (csi_t *ctx, csi_string_t *string)
|
|
{
|
|
#if CSI_DEBUG_MALLOC
|
|
_csi_free (ctx, string->string);
|
|
_csi_slab_free (ctx, string, sizeof (csi_string_t));
|
|
#else
|
|
if (ctx->free_string != NULL) {
|
|
if (string->len > ctx->free_string->len) {
|
|
csi_string_t *tmp = ctx->free_string;
|
|
ctx->free_string = string;
|
|
string = tmp;
|
|
}
|
|
|
|
_csi_free (ctx, string->string);
|
|
_csi_slab_free (ctx, string, sizeof (csi_string_t));
|
|
} else
|
|
ctx->free_string = string;
|
|
#endif
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
static int
|
|
lexcmp (void const *a, size_t alen,
|
|
void const *b, size_t blen)
|
|
{
|
|
size_t len = alen < blen ? alen : blen;
|
|
int cmp = memcmp (a, b, len);
|
|
if (cmp)
|
|
return cmp;
|
|
if (alen == blen)
|
|
return 0;
|
|
return alen < blen ? -1 : +1;
|
|
}
|
|
|
|
csi_boolean_t
|
|
csi_object_eq (csi_object_t *a,
|
|
csi_object_t *b)
|
|
{
|
|
csi_object_type_t atype = csi_object_get_type (a);
|
|
csi_object_type_t btype = csi_object_get_type (b);
|
|
|
|
if (atype == btype) {
|
|
switch (atype) {
|
|
case CSI_OBJECT_TYPE_BOOLEAN:
|
|
return a->datum.boolean == b->datum.boolean;
|
|
case CSI_OBJECT_TYPE_INTEGER:
|
|
return a->datum.integer == b->datum.integer;
|
|
case CSI_OBJECT_TYPE_REAL:
|
|
return a->datum.real == b->datum.real;
|
|
case CSI_OBJECT_TYPE_NAME:
|
|
return a->datum.name == b->datum.name;
|
|
case CSI_OBJECT_TYPE_STRING:
|
|
return 0 == lexcmp (a->datum.string->string,
|
|
a->datum.string->len,
|
|
b->datum.string->string,
|
|
b->datum.string->len);
|
|
case CSI_OBJECT_TYPE_NULL:
|
|
case CSI_OBJECT_TYPE_MARK:
|
|
return TRUE;
|
|
case CSI_OBJECT_TYPE_OPERATOR:
|
|
return a->datum.op == b->datum.op;
|
|
case CSI_OBJECT_TYPE_ARRAY:
|
|
case CSI_OBJECT_TYPE_DICTIONARY:
|
|
case CSI_OBJECT_TYPE_FILE:
|
|
case CSI_OBJECT_TYPE_MATRIX:
|
|
case CSI_OBJECT_TYPE_CONTEXT:
|
|
case CSI_OBJECT_TYPE_FONT:
|
|
case CSI_OBJECT_TYPE_PATTERN:
|
|
case CSI_OBJECT_TYPE_SCALED_FONT:
|
|
case CSI_OBJECT_TYPE_SURFACE:
|
|
return a->datum.ptr == b->datum.ptr;
|
|
}
|
|
}
|
|
|
|
if (atype < btype) {
|
|
csi_object_t *c;
|
|
csi_object_type_t ctype;
|
|
c = a; a = b; b = c;
|
|
ctype = atype; atype = btype; btype = ctype;
|
|
}
|
|
|
|
switch ((int) atype) {
|
|
case CSI_OBJECT_TYPE_INTEGER:
|
|
if (btype == CSI_OBJECT_TYPE_BOOLEAN) {
|
|
return a->datum.integer == b->datum.boolean;
|
|
}
|
|
break;
|
|
case CSI_OBJECT_TYPE_REAL:
|
|
if (btype == CSI_OBJECT_TYPE_INTEGER) {
|
|
return a->datum.real == b->datum.integer;
|
|
}
|
|
else if (btype == CSI_OBJECT_TYPE_BOOLEAN) {
|
|
return a->datum.real == b->datum.boolean;
|
|
}
|
|
break;
|
|
|
|
case CSI_OBJECT_TYPE_STRING:
|
|
if (btype == CSI_OBJECT_TYPE_NAME) {
|
|
const char *bstr = (const char *) b->datum.name;
|
|
return 0 == lexcmp (a->datum.string->string,
|
|
a->datum.string->len,
|
|
bstr,
|
|
strlen (bstr));
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
csi_status_t
|
|
csi_object_compare (csi_object_t *a,
|
|
csi_object_t *b,
|
|
int *out)
|
|
{
|
|
csi_object_type_t atype = csi_object_get_type (a);
|
|
csi_object_type_t btype = csi_object_get_type (b);
|
|
int sign;
|
|
|
|
if (csi_object_eq (a, b)){
|
|
*out = 0;
|
|
return CSI_STATUS_SUCCESS;
|
|
}
|
|
|
|
#define CMP(x,y) ((x) < (y) ? -1 : +1)
|
|
|
|
if (atype == btype) {
|
|
switch (atype) {
|
|
case CSI_OBJECT_TYPE_BOOLEAN:
|
|
*out = CMP (a->datum.boolean, b->datum.boolean);
|
|
return CSI_STATUS_SUCCESS;
|
|
case CSI_OBJECT_TYPE_INTEGER:
|
|
*out = CMP (a->datum.integer, b->datum.integer);
|
|
return CSI_STATUS_SUCCESS;
|
|
case CSI_OBJECT_TYPE_REAL:
|
|
*out = CMP (a->datum.real, b->datum.real);
|
|
return CSI_STATUS_SUCCESS;
|
|
case CSI_OBJECT_TYPE_NAME: {
|
|
const char *x = (char const *) a->datum.name;
|
|
const char *y = (char const *) b->datum.name;
|
|
*out = lexcmp (x, strlen(x), y, strlen (y));
|
|
return CSI_STATUS_SUCCESS;
|
|
}
|
|
case CSI_OBJECT_TYPE_STRING:
|
|
*out = lexcmp (a->datum.string->string,
|
|
a->datum.string->len,
|
|
b->datum.string->string,
|
|
b->datum.string->len);
|
|
return CSI_STATUS_SUCCESS;
|
|
case CSI_OBJECT_TYPE_NULL:
|
|
case CSI_OBJECT_TYPE_MARK:
|
|
case CSI_OBJECT_TYPE_OPERATOR:
|
|
case CSI_OBJECT_TYPE_ARRAY:
|
|
case CSI_OBJECT_TYPE_DICTIONARY:
|
|
case CSI_OBJECT_TYPE_FILE:
|
|
case CSI_OBJECT_TYPE_MATRIX:
|
|
case CSI_OBJECT_TYPE_CONTEXT:
|
|
case CSI_OBJECT_TYPE_FONT:
|
|
case CSI_OBJECT_TYPE_PATTERN:
|
|
case CSI_OBJECT_TYPE_SCALED_FONT:
|
|
case CSI_OBJECT_TYPE_SURFACE:
|
|
goto TYPE_CHECK_ERROR;
|
|
}
|
|
}
|
|
|
|
sign = +1;
|
|
if (atype < btype) {
|
|
csi_object_t *c;
|
|
csi_object_type_t ctype;
|
|
c = a; a = b; b = c;
|
|
ctype = atype; atype = btype; btype = ctype;
|
|
sign = -1;
|
|
}
|
|
|
|
switch ((int) atype) {
|
|
case CSI_OBJECT_TYPE_INTEGER:
|
|
if (btype == CSI_OBJECT_TYPE_BOOLEAN) {
|
|
*out = sign * CMP (a->datum.integer, !!b->datum.boolean);
|
|
return CSI_STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
case CSI_OBJECT_TYPE_REAL:
|
|
if (btype == CSI_OBJECT_TYPE_INTEGER) {
|
|
*out = sign * CMP (a->datum.real, b->datum.integer);
|
|
return CSI_STATUS_SUCCESS;
|
|
}
|
|
else if (btype == CSI_OBJECT_TYPE_BOOLEAN) {
|
|
*out = sign * CMP (a->datum.real, !!b->datum.boolean);
|
|
return CSI_STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
|
|
case CSI_OBJECT_TYPE_STRING:
|
|
if (btype == CSI_OBJECT_TYPE_NAME) {
|
|
const char *bstr = (const char *) b->datum.name;
|
|
*out = sign * lexcmp (a->datum.string->string,
|
|
a->datum.string->len,
|
|
bstr,
|
|
strlen (bstr));
|
|
return CSI_STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
#undef CMP
|
|
|
|
TYPE_CHECK_ERROR:
|
|
return _csi_error (CSI_STATUS_SCRIPT_INVALID_TYPE);
|
|
}
|