cairo/util/cairo-script/cairo-script-objects.c

934 lines
23 KiB
C
Raw Normal View History

/*
* 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;
Remove clip handling from generic surface layer. Handling clip as part of the surface state, as opposed to being part of the operation state, is cumbersome and a hindrance to providing true proxy surface support. For example, the clip must be copied from the surface onto the fallback image, but this was forgotten causing undue hassle in each backend. Another example is the contortion the meta surface endures to ensure the clip is correctly recorded. By contrast passing the clip along with the operation is quite simple and enables us to write generic handlers for providing surface wrappers. (And in the future, we should be able to write more esoteric wrappers, e.g. automatic 2x FSAA, trivially.) In brief, instead of the surface automatically applying the clip before calling the backend, the backend can call into a generic helper to apply clipping. For raster surfaces, clip regions are handled automatically as part of the composite interface. For vector surfaces, a clip helper is introduced to replay and callback into an intersect_clip_path() function as necessary. Whilst this is not primarily a performance related change (the change should just move the computation of the clip from the moment it is applied by the user to the moment it is required by the backend), it is important to track any potential regression: ppc: Speedups ======== image-rgba evolution-20090607-0 1026085.22 0.18% -> 672972.07 0.77%: 1.52x speedup ▌ image-rgba evolution-20090618-0 680579.98 0.12% -> 573237.66 0.16%: 1.19x speedup ▎ image-rgba swfdec-fill-rate-4xaa-0 460296.92 0.36% -> 407464.63 0.42%: 1.13x speedup ▏ image-rgba swfdec-fill-rate-2xaa-0 128431.95 0.47% -> 115051.86 0.42%: 1.12x speedup ▏ Slowdowns ========= image-rgba firefox-periodic-table-0 56837.61 0.78% -> 66055.17 3.20%: 1.09x slowdown ▏
2009-07-23 15:32:13 +01:00
string->deflate = 0;
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;
}
Remove clip handling from generic surface layer. Handling clip as part of the surface state, as opposed to being part of the operation state, is cumbersome and a hindrance to providing true proxy surface support. For example, the clip must be copied from the surface onto the fallback image, but this was forgotten causing undue hassle in each backend. Another example is the contortion the meta surface endures to ensure the clip is correctly recorded. By contrast passing the clip along with the operation is quite simple and enables us to write generic handlers for providing surface wrappers. (And in the future, we should be able to write more esoteric wrappers, e.g. automatic 2x FSAA, trivially.) In brief, instead of the surface automatically applying the clip before calling the backend, the backend can call into a generic helper to apply clipping. For raster surfaces, clip regions are handled automatically as part of the composite interface. For vector surfaces, a clip helper is introduced to replay and callback into an intersect_clip_path() function as necessary. Whilst this is not primarily a performance related change (the change should just move the computation of the clip from the moment it is applied by the user to the moment it is required by the backend), it is important to track any potential regression: ppc: Speedups ======== image-rgba evolution-20090607-0 1026085.22 0.18% -> 672972.07 0.77%: 1.52x speedup ▌ image-rgba evolution-20090618-0 680579.98 0.12% -> 573237.66 0.16%: 1.19x speedup ▎ image-rgba swfdec-fill-rate-4xaa-0 460296.92 0.36% -> 407464.63 0.42%: 1.13x speedup ▏ image-rgba swfdec-fill-rate-2xaa-0 128431.95 0.47% -> 115051.86 0.42%: 1.12x speedup ▏ Slowdowns ========= image-rgba firefox-periodic-table-0 56837.61 0.78% -> 66055.17 3.20%: 1.09x slowdown ▏
2009-07-23 15:32:13 +01:00
csi_status_t
csi_string_deflate_new (csi_t *ctx,
csi_object_t *obj,
void *bytes,
int in_len,
int out_len)
{
csi_status_t status;
csi_string_t *string;
status = csi_string_new (ctx, obj, bytes, in_len);
if (_csi_unlikely (status))
return status;
string = obj->datum.string;
string->deflate = out_len;
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;
Remove clip handling from generic surface layer. Handling clip as part of the surface state, as opposed to being part of the operation state, is cumbersome and a hindrance to providing true proxy surface support. For example, the clip must be copied from the surface onto the fallback image, but this was forgotten causing undue hassle in each backend. Another example is the contortion the meta surface endures to ensure the clip is correctly recorded. By contrast passing the clip along with the operation is quite simple and enables us to write generic handlers for providing surface wrappers. (And in the future, we should be able to write more esoteric wrappers, e.g. automatic 2x FSAA, trivially.) In brief, instead of the surface automatically applying the clip before calling the backend, the backend can call into a generic helper to apply clipping. For raster surfaces, clip regions are handled automatically as part of the composite interface. For vector surfaces, a clip helper is introduced to replay and callback into an intersect_clip_path() function as necessary. Whilst this is not primarily a performance related change (the change should just move the computation of the clip from the moment it is applied by the user to the moment it is required by the backend), it is important to track any potential regression: ppc: Speedups ======== image-rgba evolution-20090607-0 1026085.22 0.18% -> 672972.07 0.77%: 1.52x speedup ▌ image-rgba evolution-20090618-0 680579.98 0.12% -> 573237.66 0.16%: 1.19x speedup ▎ image-rgba swfdec-fill-rate-4xaa-0 460296.92 0.36% -> 407464.63 0.42%: 1.13x speedup ▏ image-rgba swfdec-fill-rate-2xaa-0 128431.95 0.47% -> 115051.86 0.42%: 1.12x speedup ▏ Slowdowns ========= image-rgba firefox-periodic-table-0 56837.61 0.78% -> 66055.17 3.20%: 1.09x slowdown ▏
2009-07-23 15:32:13 +01:00
string->deflate = 0;
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)
{
int type = csi_object_get_type (src);
switch (type) {
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);
}