cairo/util/cairo-script/cairo-script-scanner.c

1683 lines
38 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 <math.h> /* pow */
#include <stdio.h> /* EOF */
#include <string.h> /* memset */
#include <assert.h>
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
#include <zlib.h>
#define DEBUG_SCAN 0
/*
* whitespace:
* 0 - nul
* 9 - tab
* A - LF
* C - FF
* D - CR
*
* syntax delimiters
* ( = 28, ) = 29 - literal strings
* < = 3C, > = 3E - hex/base85 strings, dictionary name
* [ = 5B, ] = 5D - array
* { = 7B, } = 7C - procedure
* / = 5C - literal marker
* % = 25 - comment
*/
static void
fprintf_obj (FILE *stream, csi_t *ctx, const csi_object_t *obj)
{
switch (csi_object_get_type (obj)) {
case CSI_OBJECT_TYPE_NULL:
fprintf (stream, "NULL\n");
break;
/* atomics */
case CSI_OBJECT_TYPE_BOOLEAN:
fprintf (stream, "boolean: %s\n",
obj->datum.boolean ? "true" : "false");
break;
case CSI_OBJECT_TYPE_INTEGER:
fprintf (stream, "integer: %ld\n", obj->datum.integer);
break;
case CSI_OBJECT_TYPE_MARK:
fprintf (stream, "mark\n");
break;
case CSI_OBJECT_TYPE_NAME:
fprintf (stream, "name: %s\n", (char *) obj->datum.name);
break;
case CSI_OBJECT_TYPE_OPERATOR:
fprintf (stream, "operator: %p\n", obj->datum.ptr);
break;
case CSI_OBJECT_TYPE_REAL:
fprintf (stream, "real: %g\n", obj->datum.real);
break;
/* compound */
case CSI_OBJECT_TYPE_ARRAY:
fprintf (stream, "array\n");
break;
case CSI_OBJECT_TYPE_DICTIONARY:
fprintf (stream, "dictionary\n");
break;
case CSI_OBJECT_TYPE_FILE:
fprintf (stream, "file\n");
break;
case CSI_OBJECT_TYPE_MATRIX:
fprintf (stream, "matrix: [%g %g %g %g %g %g]\n",
obj->datum.matrix->matrix.xx,
obj->datum.matrix->matrix.yx,
obj->datum.matrix->matrix.xy,
obj->datum.matrix->matrix.yy,
obj->datum.matrix->matrix.x0,
obj->datum.matrix->matrix.y0);
break;
case CSI_OBJECT_TYPE_STRING:
fprintf (stream, "string: len=%ld\n", obj->datum.string->len);
break;
/* cairo */
case CSI_OBJECT_TYPE_CONTEXT:
fprintf (stream, "context\n");
break;
case CSI_OBJECT_TYPE_FONT:
fprintf (stream, "font\n");
break;
case CSI_OBJECT_TYPE_PATTERN:
fprintf (stream, "pattern\n");
break;
case CSI_OBJECT_TYPE_SCALED_FONT:
fprintf (stream, "scaled-font\n");
break;
case CSI_OBJECT_TYPE_SURFACE:
fprintf (stream, "surface\n");
break;
}
}
/* takes ownership of obj */
static inline csi_status_t
scan_push (csi_t *ctx, csi_object_t *obj)
{
return ctx->scanner.push (ctx, obj);
}
static inline csi_status_t
scan_execute (csi_t *ctx, csi_object_t *obj)
{
return ctx->scanner.execute (ctx, obj);
}
static cairo_status_t
buffer_init (csi_t *ctx, csi_buffer_t *buffer)
{
cairo_status_t status = CSI_STATUS_SUCCESS;
buffer->size = 16384;
buffer->base = _csi_alloc (ctx, buffer->size);
if (_csi_unlikely (buffer->base == NULL)) {
status = _csi_error (CSI_STATUS_NO_MEMORY);
buffer->size = 0;
}
buffer->ptr = buffer->base;
buffer->end = buffer->base + buffer->size;
return status;
}
static void
buffer_fini (csi_t *ctx, csi_buffer_t *buffer)
{
_csi_free (ctx, buffer->base);
}
static void
_buffer_grow (csi_t *ctx, csi_scanner_t *scan)
{
int newsize;
int offset;
char *base;
if (_csi_unlikely (scan->buffer.size > INT_MAX / 2))
longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_NO_MEMORY));
offset = scan->buffer.ptr - scan->buffer.base;
newsize = scan->buffer.size * 2;
base = _csi_realloc (ctx, scan->buffer.base, newsize);
if (_csi_unlikely (base == NULL))
longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_NO_MEMORY));
scan->buffer.base = base;
scan->buffer.ptr = base + offset;
scan->buffer.end = base + newsize;
scan->buffer.size = newsize;
}
static inline void
buffer_check (csi_t *ctx, csi_scanner_t *scan, int count)
{
if (_csi_unlikely (scan->buffer.ptr + count > scan->buffer.end))
_buffer_grow (ctx, scan);
}
static inline void
buffer_add (csi_buffer_t *buffer, int c)
{
*buffer->ptr++ = c;
}
static inline void
buffer_reset (csi_buffer_t *buffer)
{
buffer->ptr = buffer->base;
}
static void
token_start (csi_scanner_t *scan)
{
buffer_reset (&scan->buffer);
}
static void
token_add (csi_t *ctx, csi_scanner_t *scan, int c)
{
buffer_check (ctx, scan, 1);
buffer_add (&scan->buffer, c);
}
static void
token_add_unchecked (csi_scanner_t *scan, int c)
{
buffer_add (&scan->buffer, c);
}
csi_boolean_t
_csi_parse_number (csi_object_t *obj, const char *s, int len)
{
int radix = 0;
long long mantissa = 0;
int exponent = 0;
int sign = 1;
int decimal = -1;
int exponent_sign = 0;
const char * const end = s + len;
switch (*s) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
mantissa = *s - '0';
case '+':
break;
case '-':
sign = -1;
break;
case '.':
decimal = 0;
break;
default:
return FALSE;
}
while (++s < end) {
if (*s < '0') {
if (*s == '.') {
if (_csi_unlikely (radix))
return FALSE;
if (_csi_unlikely (decimal != -1))
return FALSE;
if (_csi_unlikely (exponent_sign))
return FALSE;
decimal = 0;
} else if (*s == '!') {
if (_csi_unlikely (radix))
return FALSE;
if (_csi_unlikely (decimal != -1))
return FALSE;
if (_csi_unlikely (exponent_sign))
return FALSE;
radix = mantissa;
mantissa = 0;
if (_csi_unlikely (radix < 2 || radix > 36))
return FALSE;
} else
return FALSE;
} else if (*s <= '9') {
int v = *s - '0';
if (_csi_unlikely (radix && v >= radix))
return FALSE;
if (exponent_sign) {
exponent = 10 * exponent + v;
} else {
if (radix)
mantissa = radix * mantissa + v;
else
mantissa = 10 * mantissa + v;
if (decimal != -1)
decimal++;
}
} else if (*s == 'E' || * s== 'e') {
if (radix == 0) {
if (_csi_unlikely (s + 1 == end))
return FALSE;
exponent_sign = 1;
if (s[1] == '-') {
exponent_sign = -1;
s++;
} else if (s[1] == '+')
s++;
} else {
int v = 0xe;
if (_csi_unlikely (v >= radix))
return FALSE;
mantissa = radix * mantissa + v;
}
} else if (*s < 'A') {
return FALSE;
} else if (*s <= 'Z') {
int v = *s - 'A' + 0xA;
if (_csi_unlikely (v >= radix))
return FALSE;
mantissa = radix * mantissa + v;
} else if (*s < 'a') {
return FALSE;
} else if (*s <= 'z') {
int v = *s - 'a' + 0xa;
if (_csi_unlikely (v >= radix))
return FALSE;
mantissa = radix * mantissa + v;
} else
return FALSE;
}
if (exponent_sign || decimal != -1) {
if (mantissa == 0) {
obj->type = CSI_OBJECT_TYPE_REAL;
obj->datum.real = 0.;
return TRUE;
} else {
int e;
double v;
v = mantissa;
e = exponent * exponent_sign;
if (decimal != -1)
e -= decimal;
switch (e) {
case -7: v *= 0.0000001; break;
case -6: v *= 0.000001; break;
case -5: v *= 0.00001; break;
case -4: v *= 0.0001; break;
case -3: v *= 0.001; break;
case -2: v *= 0.01; break;
case -1: v *= 0.1; break;
case 0: break;
case 1: v *= 10; break;
case 2: v *= 100; break;
case 3: v *= 1000; break;
case 4: v *= 10000; break;
case 5: v *= 100000; break;
case 6: v *= 1000000; break;
default:
v *= pow (10, e); /* XXX */
break;
}
obj->type = CSI_OBJECT_TYPE_REAL;
obj->datum.real = sign * v;
return TRUE;
}
} else {
obj->type = CSI_OBJECT_TYPE_INTEGER;
obj->datum.integer = sign * mantissa;
return TRUE;
}
}
static void
token_end (csi_t *ctx, csi_scanner_t *scan, csi_file_t *src)
{
cairo_status_t status;
char *s;
csi_object_t obj;
int len;
/*
* Any token that consists entirely of regular characters and
* cannot be interpreted as a number is treated as a name object
* (more precisely, an executable name). All characters except
* delimiters and white-space characters can appear in names,
* including characters ordinarily considered to be punctuation.
*/
if (_csi_unlikely (scan->buffer.ptr == scan->buffer.base))
return;
s = scan->buffer.base;
len = scan->buffer.ptr - scan->buffer.base;
if (_csi_likely (! scan->bind)) {
if (s[0] == '{') { /* special case procedures */
if (scan->build_procedure.type != CSI_OBJECT_TYPE_NULL) {
status = _csi_stack_push (ctx,
&scan->procedure_stack,
&scan->build_procedure);
if (_csi_unlikely (status))
longjmp (scan->jmpbuf, status);
}
status = csi_array_new (ctx, 0, &scan->build_procedure);
if (_csi_unlikely (status))
longjmp (scan->jmpbuf, status);
scan->build_procedure.type |= CSI_OBJECT_ATTR_EXECUTABLE;
return;
} else if (s[0] == '}') {
if (_csi_unlikely
(scan->build_procedure.type == CSI_OBJECT_TYPE_NULL))
{
longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));
}
if (scan->procedure_stack.len) {
csi_object_t *next;
next = _csi_stack_peek (&scan->procedure_stack, 0);
status = csi_array_append (ctx, next->datum.array,
&scan->build_procedure);
scan->build_procedure = *next;
scan->procedure_stack.len--;
} else {
status = scan_push (ctx, &scan->build_procedure);
scan->build_procedure.type = CSI_OBJECT_TYPE_NULL;
}
if (_csi_unlikely (status))
longjmp (scan->jmpbuf, status);
return;
}
}
if (s[0] == '/') {
if (len >= 2 && s[1] == '/') { /* substituted name */
status = csi_name_new (ctx, &obj, s + 2, len - 2);
if (_csi_unlikely (status))
longjmp (scan->jmpbuf, status);
status = _csi_name_lookup (ctx, obj.datum.name, &obj);
} else { /* literal name */
status = csi_name_new (ctx, &obj, s + 1, len - 1);
}
if (_csi_unlikely (status))
longjmp (scan->jmpbuf, status);
} else {
if (! _csi_parse_number (&obj, s, len)) {
status = csi_name_new (ctx, &obj, s, len);
if (_csi_unlikely (status))
longjmp (scan->jmpbuf, status);
obj.type |= CSI_OBJECT_ATTR_EXECUTABLE;
}
}
/* consume whitespace after token, before calling the interpreter */
if (scan->build_procedure.type != CSI_OBJECT_TYPE_NULL) {
status = csi_array_append (ctx,
scan->build_procedure.datum.array,
&obj);
} else if (obj.type & CSI_OBJECT_ATTR_EXECUTABLE) {
status = scan_execute (ctx, &obj);
csi_object_free (ctx, &obj);
} else {
status = scan_push (ctx, &obj);
}
if (_csi_unlikely (status))
longjmp (scan->jmpbuf, status);
}
static void
string_add (csi_t *ctx, csi_scanner_t *scan, int c)
{
buffer_check (ctx, scan, 1);
buffer_add (&scan->buffer, c);
}
static void
string_end (csi_t *ctx, csi_scanner_t *scan)
{
csi_object_t obj;
cairo_status_t status;
status = csi_string_new (ctx,
&obj,
scan->buffer.base,
scan->buffer.ptr - scan->buffer.base);
if (_csi_unlikely (status))
longjmp (scan->jmpbuf, status);
if (scan->build_procedure.type != CSI_OBJECT_TYPE_NULL)
status = csi_array_append (ctx,
scan->build_procedure.datum.array,
&obj);
else
status = scan_push (ctx, &obj);
if (_csi_unlikely (status))
longjmp (scan->jmpbuf, status);
}
static int
hex_value (int c)
{
if (c < '0')
return EOF;
if (c <= '9')
return c - '0';
c |= 32;
if (c < 'a')
return EOF;
if (c <= 'f')
return c - 'a' + 0xa;
return EOF;
}
static void
hex_add (csi_t *ctx, csi_scanner_t *scan, int c)
{
if (scan->accumulator_count == 0) {
scan->accumulator |= hex_value (c) << 4;
scan->accumulator_count = 1;
} else {
scan->accumulator |= hex_value (c) << 0;
buffer_check (ctx, scan, 1);
buffer_add (&scan->buffer, scan->accumulator);
scan->accumulator = 0;
scan->accumulator_count = 0;
}
}
static void
hex_end (csi_t *ctx, csi_scanner_t *scan)
{
csi_object_t obj;
cairo_status_t status;
if (scan->accumulator_count)
hex_add (ctx, scan, '0');
status = csi_string_new (ctx,
&obj,
scan->buffer.base,
scan->buffer.ptr - scan->buffer.base);
if (_csi_unlikely (status))
longjmp (scan->jmpbuf, status);
if (scan->build_procedure.type != CSI_OBJECT_TYPE_NULL)
status = csi_array_append (ctx,
scan->build_procedure.datum.array,
&obj);
else
status = scan_push (ctx, &obj);
if (_csi_unlikely (status))
longjmp (scan->jmpbuf, status);
}
static void
base85_add (csi_t *ctx, csi_scanner_t *scan, int c)
{
if (c == 'z') {
if (_csi_unlikely (scan->accumulator_count != 0))
longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));
buffer_check (ctx, scan, 4);
buffer_add (&scan->buffer, 0);
buffer_add (&scan->buffer, 0);
buffer_add (&scan->buffer, 0);
buffer_add (&scan->buffer, 0);
} else if (_csi_unlikely (c < '!' || c > 'u')) {
longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));
} else {
scan->accumulator = scan->accumulator*85 + c - '!';
if (++scan->accumulator_count == 5) {
buffer_check (ctx, scan, 4);
buffer_add (&scan->buffer, (scan->accumulator >> 24) & 0xff);
buffer_add (&scan->buffer, (scan->accumulator >> 16) & 0xff);
buffer_add (&scan->buffer, (scan->accumulator >> 8) & 0xff);
buffer_add (&scan->buffer, (scan->accumulator >> 0) & 0xff);
scan->accumulator = 0;
scan->accumulator_count = 0;
}
}
}
static void
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
base85_end (csi_t *ctx, csi_scanner_t *scan, cairo_bool_t deflate)
{
csi_object_t obj;
cairo_status_t status;
buffer_check (ctx, scan, 4);
switch (scan->accumulator_count) {
case 0:
break;
case 1:
longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));
break;
case 2:
scan->accumulator = scan->accumulator * (85*85*85) + 85*85*85 -1;
buffer_add (&scan->buffer, (scan->accumulator >> 24) & 0xff);
break;
case 3:
scan->accumulator = scan->accumulator * (85*85) + 85*85 -1;
buffer_add (&scan->buffer, (scan->accumulator >> 24) & 0xff);
buffer_add (&scan->buffer, (scan->accumulator >> 16) & 0xff);
break;
case 4:
scan->accumulator = scan->accumulator * 85 + 84;
buffer_add (&scan->buffer, (scan->accumulator >> 24) & 0xff);
buffer_add (&scan->buffer, (scan->accumulator >> 16) & 0xff);
buffer_add (&scan->buffer, (scan->accumulator >> 8) & 0xff);
break;
}
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
if (deflate) {
uLongf len = *(uint32_t *) scan->buffer.base;
Bytef *source = (Bytef *) (scan->buffer.base + sizeof (uint32_t));
status = csi_string_deflate_new (ctx, &obj,
source,
(Bytef *) scan->buffer.ptr - source,
len);
if (_csi_unlikely (status))
longjmp (scan->jmpbuf, status);
} else {
status = csi_string_new (ctx,
&obj,
scan->buffer.base,
scan->buffer.ptr - scan->buffer.base);
if (_csi_unlikely (status))
longjmp (scan->jmpbuf, status);
}
if (scan->build_procedure.type != CSI_OBJECT_TYPE_NULL)
status = csi_array_append (ctx,
scan->build_procedure.datum.array,
&obj);
else
status = scan_push (ctx, &obj);
if (_csi_unlikely (status))
longjmp (scan->jmpbuf, status);
}
static inline void
scan_read (csi_scanner_t *scan, csi_file_t *src, void *ptr, int len)
{
uint8_t *data = ptr;
do {
int ret = csi_file_read (src, data, len);
if (_csi_unlikely (ret == 0))
longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_READ_ERROR));
data += ret;
len -= ret;
} while (_csi_unlikely (len));
}
#if WORDS_BIGENDIAN
#define le16(x) bswap_16 (x)
#define le32(x) bswap_32 (x)
#define be16(x) x
#define be32(x) x
#define to_be32(x) x
#else
#define le16(x) x
#define le32(x) x
#define be16(x) bswap_16 (x)
#define be32(x) bswap_32 (x)
#define to_be32(x) bswap_32 (x)
#endif
static void
string_read (csi_t *ctx,
csi_scanner_t *scan,
csi_file_t *src,
int len,
int compressed,
csi_object_t *obj)
{
csi_status_t status;
status = csi_string_new (ctx, obj, NULL, len + 1);
if (_csi_unlikely (status))
longjmp (scan->jmpbuf, status);
if (compressed) {
uint32_t u32;
scan_read (scan, src, &u32, 4);
obj->datum.string->deflate = be32 (u32);
}
scan_read (scan, src, obj->datum.string->string, len);
obj->datum.string->string[len] = '\0';
}
static void
_scan_file (csi_t *ctx, csi_file_t *src)
{
csi_scanner_t *scan = &ctx->scanner;
int c, next;
union {
int8_t i8;
uint8_t u8;
int16_t i16;
uint16_t u16;
int32_t i32;
uint32_t u32;
float f;
} u;
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
int deflate = 0;
int string_p;
scan_none:
while ((c = csi_file_getc (src)) != EOF) {
csi_object_t obj = { CSI_OBJECT_TYPE_NULL };
switch (c) {
case 0xa:
scan->line_number++;
case 0x0:
case 0x9:
case 0xc:
case 0xd:
case 0x20: /* ignore whitespace */
break;
case '%':
goto scan_comment;
case '(':
goto scan_string;
case '[': /* needs special case */
case ']':
case '{':
case '}':
token_start (scan);
token_add_unchecked (scan, c);
token_end (ctx, scan, src);
goto scan_none;
case '<':
next = csi_file_getc (src);
switch (next) {
case EOF:
csi_file_putc (src, '<');
return;
case '<':
/* dictionary name */
token_start (scan);
token_add_unchecked (scan, '<');
token_add_unchecked (scan, '<');
token_end (ctx, scan, src);
goto scan_none;
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
case '|':
deflate = 1;
case '~':
goto scan_base85;
default:
csi_file_putc (src, next);
goto scan_hex;
}
break;
/* binary token */
#define MSB_INT8 128
#define MSB_UINT8 129
#define MSB_INT16 130
#define MSB_UINT16 131
#define MSB_INT32 132
#define LSB_INT8 MSB_INT8
#define LSB_UINT8 MSB_UINT8
#define LSB_INT16 133
#define LSB_UINT16 134
#define LSB_INT32 135
#define MSB_FLOAT32 140
#define LSB_FLOAT32 141
case MSB_INT8:
scan_read (scan, src, &u.i8, 1);
csi_integer_new (&obj, u.i8);
break;
case MSB_UINT8:
scan_read (scan, src, &u.u8, 1);
csi_integer_new (&obj, u.u8);
break;
case MSB_INT16:
scan_read (scan, src, &u.i16, 2);
csi_integer_new (&obj, be16 (u.i16));
break;
case LSB_INT16:
scan_read (scan, src, &u.i16, 2);
csi_integer_new (&obj, le16 (u.i16));
break;
case MSB_UINT16:
scan_read (scan, src, &u.u16, 2);
csi_integer_new (&obj, be16 (u.u16));
break;
case LSB_UINT16:
scan_read (scan, src, &u.u16, 2);
csi_integer_new (&obj, le16 (u.u16));
break;
case MSB_INT32:
scan_read (scan, src, &u.i32, 4);
csi_integer_new (&obj, be32 (u.i32));
break;
case LSB_INT32:
scan_read (scan, src, &u.i32, 4);
csi_integer_new (&obj, le32 (u.i32));
break;
case 136: /* 16.16 msb */
scan_read (scan, src, &u.i32, 4);
csi_real_new (&obj, be32 (u.i32) / 65536.);
break;
case 137: /* 16.16 lsb */
scan_read (scan, src, &u.i32, 4);
csi_real_new (&obj, le32 (u.i32) / 65536.);
break;
case 138: /* 24.8 msb */
scan_read (scan, src, &u.i32, 4);
csi_real_new (&obj, be32 (u.i32) / 256.);
break;
case 139: /* 24.8 lsb */
scan_read (scan, src, &u.i32, 4);
csi_real_new (&obj, le32 (u.i32) / 256.);
break;
case MSB_FLOAT32:
scan_read (scan, src, &u.i32, 4);
u.i32 = be32 (u.i32);
csi_real_new (&obj, u.f);
break;
case LSB_FLOAT32:
scan_read (scan, src, &u.i32, 4);
u.i32 = le32 (u.i32);
csi_real_new (&obj, u.f);
break;
#define STRING_1 142
#define STRING_2_MSB 144
#define STRING_2_LSB 146
#define STRING_4_MSB 148
#define STRING_4_LSB 150
#define STRING_DEFLATE 1
case STRING_1:
case STRING_1 | STRING_DEFLATE:
scan_read (scan, src, &u.u8, 1);
string_read (ctx, scan, src, u.u8, c & STRING_DEFLATE, &obj);
break;
case STRING_2_MSB:
case STRING_2_MSB | STRING_DEFLATE:
scan_read (scan, src, &u.u16, 2);
string_read (ctx, scan, src, be16 (u.u16), c & STRING_DEFLATE, &obj);
break;
case STRING_2_LSB:
case STRING_2_LSB | STRING_DEFLATE:
scan_read (scan, src, &u.u16, 2);
string_read (ctx, scan, src, le16 (u.u16), c & STRING_DEFLATE, &obj);
break;
case STRING_4_MSB:
case STRING_4_MSB | STRING_DEFLATE:
scan_read (scan, src, &u.u32, 4);
string_read (ctx, scan, src, be32 (u.u32), c & STRING_DEFLATE, &obj);
break;
case STRING_4_LSB:
case STRING_4_LSB | STRING_DEFLATE:
scan_read (scan, src, &u.u32, 4);
string_read (ctx, scan, src, le32 (u.u32), c & STRING_DEFLATE, &obj);
break;
#define OPCODE 152
case OPCODE:
scan_read (scan, src, &u.u8, 1);
csi_operator_new (&obj, ctx->opcode[u.u8]);
break;
case OPCODE | 1:
scan_read (scan, src, &u.u8, 1);
csi_operator_new (&obj, ctx->opcode[u.u8]);
obj.type &= ~CSI_OBJECT_ATTR_EXECUTABLE;
break;
/* unassigned */
case 154:
case 155:
case 156:
case 157:
case 158:
case 159:
longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));
case '#': /* PDF 1.2 escape code */
{
int c_hi = csi_file_getc (src);
int c_lo = csi_file_getc (src);
c = (hex_value (c_hi) << 4) | hex_value (c_lo);
}
/* fall-through */
default:
token_start (scan);
token_add_unchecked (scan, c);
goto scan_token;
}
if (obj.type != CSI_OBJECT_TYPE_NULL) {
cairo_status_t status;
if (scan->build_procedure.type != CSI_OBJECT_TYPE_NULL) {
status = csi_array_append (ctx,
scan->build_procedure.datum.array,
&obj);
} else if (obj.type & CSI_OBJECT_ATTR_EXECUTABLE) {
status = scan_execute (ctx, &obj);
csi_object_free (ctx, &obj);
} else {
status = scan_push (ctx, &obj);
}
if (_csi_unlikely (status))
longjmp (scan->jmpbuf, status);
}
}
return;
scan_token:
while ((c = csi_file_getc (src)) != EOF) {
switch (c) {
case 0xa:
scan->line_number++;
case 0x0:
case 0x9:
case 0xc:
case 0xd:
case 0x20:
token_end (ctx, scan, src);
goto scan_none;
/* syntax delimiters */
case '%':
token_end (ctx, scan, src);
goto scan_comment;
/* syntax error? */
case '(':
token_end (ctx, scan, src);
goto scan_string;
/* XXX syntax error? */
case ')':
token_end (ctx, scan, src);
goto scan_none;
case '/':
/* need to special case '^//?' */
if (scan->buffer.ptr > scan->buffer.base+1 ||
scan->buffer.base[0] != '/')
{
token_end (ctx, scan, src);
token_start (scan);
}
token_add_unchecked (scan, '/');
goto scan_token;
case '{':
case '}':
case ']':
token_end (ctx, scan, src);
token_start (scan);
token_add_unchecked (scan, c);
token_end (ctx, scan, src);
goto scan_none;
case '<':
csi_file_putc (src, '<');
token_end (ctx, scan, src);
goto scan_none;
case '#': /* PDF 1.2 escape code */
{
int c_hi = csi_file_getc (src);
int c_lo = csi_file_getc (src);
c = (hex_value (c_hi) << 4) | hex_value (c_lo);
}
/* fall-through */
default:
token_add (ctx, scan, c);
break;
}
}
token_end (ctx, scan, src);
return;
scan_comment:
/* discard until newline */
while ((c = csi_file_getc (src)) != EOF) {
switch (c) {
case 0xa:
scan->line_number++;
case 0xc:
goto scan_none;
}
}
return;
scan_string:
buffer_reset (&scan->buffer);
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_p = 1;
while ((c = csi_file_getc (src)) != EOF) {
switch (c) {
case '\\': /* escape */
next = csi_file_getc (src);
switch (next) {
case EOF:
longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));
case 'n':
string_add (ctx, scan, '\n');
break;
case 'r':
string_add (ctx, scan, '\r');
break;
case 't':
string_add (ctx, scan, '\t');
break;
case 'b':
string_add (ctx, scan, '\b');
break;
case 'f':
string_add (ctx, scan, '\f');
break;
case '\\':
string_add (ctx, scan, '\\');
break;
case '(':
string_add (ctx, scan, '(');
break;
case ')':
string_add (ctx, scan, ')');
break;
case '0': case '1': case '2': case '3':
case '4': case '5': case '6': case '7':
{ /* octal code: \d{1,3} */
int i;
c = next - '0';
for (i = 0; i < 2; i++) {
next = csi_file_getc (src);
switch (next) {
case EOF:
return;
case '0': case '1': case '2': case '3':
case '4': case '5': case '6': case '7':
c = 8*c + next-'0';
break;
default:
csi_file_putc (src, next);
goto octal_code_done;
}
}
octal_code_done:
string_add (ctx, scan, c);
}
break;
case 0xa:
/* skip the newline */
next = csi_file_getc (src); /* might be compound LFCR */
switch (next) {
case EOF:
return;
case 0xc:
break;
default:
csi_file_putc (src, next);
break;
}
scan->line_number++;
break;
case 0xc:
break;
default:
/* ignore the '\' */
break;
}
break;
case '(':
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_p++;
string_add (ctx, scan, c);
break;
case ')':
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
if (--string_p == 0) {
string_end (ctx, scan);
goto scan_none;
}
/* fall through */
default:
string_add (ctx, scan, c);
break;
}
}
longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));
scan_hex:
buffer_reset (&scan->buffer);
scan->accumulator_count = 0;
scan->accumulator = 0;
while ((c = csi_file_getc (src)) != EOF) {
switch (c) {
case 0xa:
scan->line_number++;
case 0x0:
case 0x9:
case 0xc:
case 0xd:
case 0x20: /* ignore whitespace */
break;
case '>':
hex_end (ctx, scan); /* fixup odd digit with '0' */
goto scan_none;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
hex_add (ctx, scan, c);
break;
default:
longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));
}
}
longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));
scan_base85:
buffer_reset (&scan->buffer);
scan->accumulator = 0;
scan->accumulator_count = 0;
while ((c = csi_file_getc (src)) != EOF) {
switch (c) {
case '~':
next = csi_file_getc (src);
switch (next) {
case EOF:
return;
case '>':
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
base85_end (ctx, scan, deflate);
deflate = 0;
goto scan_none;
}
csi_file_putc (src, next);
/* fall-through */
default:
base85_add (ctx, scan, c);
break;
}
}
longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));
}
static csi_status_t
_scan_push (csi_t *ctx, csi_object_t *obj)
{
if (DEBUG_SCAN) {
fprintf (stderr, "push ");
fprintf_obj (stderr, ctx, obj);
}
return _csi_push_ostack (ctx, obj);
}
static csi_status_t
_scan_execute (csi_t *ctx, csi_object_t *obj)
{
if (DEBUG_SCAN) {
fprintf (stderr, "exec ");
fprintf_obj (stderr, ctx, obj);
}
return csi_object_execute (ctx, obj);
}
csi_status_t
_csi_scanner_init (csi_t *ctx, csi_scanner_t *scanner)
{
csi_status_t status;
memset (scanner, 0, sizeof (csi_scanner_t));
status = buffer_init (ctx, &scanner->buffer);
if (status)
return status;
status = _csi_stack_init (ctx, &scanner->procedure_stack, 4);
if (status)
return status;
scanner->bind = 0;
scanner->push = _scan_push;
scanner->execute = _scan_execute;
return CSI_STATUS_SUCCESS;
}
void
_csi_scanner_fini (csi_t *ctx, csi_scanner_t *scanner)
{
buffer_fini (ctx, &scanner->buffer);
_csi_stack_fini (ctx, &scanner->procedure_stack);
if (scanner->build_procedure.type != CSI_OBJECT_TYPE_NULL)
csi_object_free (ctx, &scanner->build_procedure);
}
csi_status_t
_csi_scan_file (csi_t *ctx, csi_file_t *src)
{
csi_status_t status;
/* This function needs to be reentrant to handle recursive scanners.
* i.e. one script executes a second.
*/
if (ctx->scanner.depth++ == 0) {
if ((status = setjmp (ctx->scanner.jmpbuf))) {
ctx->scanner.depth = 0;
return status;
}
ctx->scanner.line_number = 0; /* XXX broken by recursive scanning */
}
_scan_file (ctx, src);
--ctx->scanner.depth;
return CSI_STATUS_SUCCESS;
}
struct _translate_closure {
csi_dictionary_t *opcodes;
cairo_write_func_t write_func;
void *closure;
};
static csi_status_t
_translate_name (csi_t *ctx,
csi_name_t name,
csi_boolean_t executable,
struct _translate_closure *closure)
{
if (executable) {
csi_dictionary_entry_t *entry;
uint16_t u16;
/* Bind executable names.
* XXX This may break some scripts that overload system operators.
*/
entry = _csi_hash_table_lookup (&closure->opcodes->hash_table,
(csi_hash_entry_t *) &name);
if (entry == NULL)
goto STRING;
u16 = entry->value.datum.integer;
u16 = be16 (u16);
closure->write_func (closure->closure, (unsigned char *) &u16, 2);
} else {
closure->write_func (closure->closure, (unsigned char *) "/", 1);
STRING:
closure->write_func (closure->closure,
(unsigned char *) name,
strlen ((char *) name));
closure->write_func (closure->closure, (unsigned char *) "\n", 1);
}
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_translate_operator (csi_t *ctx,
csi_operator_t op,
csi_boolean_t executable,
struct _translate_closure *closure)
{
csi_dictionary_entry_t *entry;
uint16_t u16;
entry = _csi_hash_table_lookup (&closure->opcodes->hash_table,
(csi_hash_entry_t *) &op);
if (entry == NULL)
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
u16 = entry->value.datum.integer;
if (! executable)
u16 += 1 << 8;
u16 = be16 (u16);
closure->write_func (closure->closure, (unsigned char *) &u16, 2);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_translate_integer (csi_t *ctx,
csi_integer_t i,
struct _translate_closure *closure)
{
uint8_t hdr;
union {
int8_t i8;
uint8_t u8;
int16_t i16;
uint16_t u16;
int32_t i32;
uint32_t u32;
} u;
int len;
#if WORDS_BIGENDIAN
if (i < INT16_MIN) {
hdr = MSB_INT32;
len = 4;
u.i32 = i;
} else if (i < INT8_MIN) {
hdr = MSB_INT16;
len = 2;
u.i16 = i;
} else if (i < 0) {
hdr = MSB_INT8;
len = 1;
u.i8 = i;
} else if (i <= UINT8_MAX) {
hdr = MSB_UINT8;
len = 1;
u.u8 = i;
} else if (i <= UINT16_MAX) {
hdr = MSB_UINT16;
len = 2;
u.u16 = i;
} else {
hdr = MSB_INT32;
len = 4;
u.u32 = i;
}
#else
if (i < INT16_MIN) {
hdr = LSB_INT32;
len = 4;
u.i32 = i;
} else if (i < INT8_MIN) {
hdr = LSB_INT16;
len = 2;
u.i16 = i;
} else if (i < 0) {
hdr = LSB_INT8;
len = 1;
u.i8 = i;
} else if (i <= UINT8_MAX) {
hdr = LSB_UINT8;
len = 1;
u.u8 = i;
} else if (i <= UINT16_MAX) {
hdr = LSB_UINT16;
len = 2;
u.u16 = i;
} else {
hdr = LSB_INT32;
len = 4;
u.u32 = i;
}
#endif
closure->write_func (closure->closure, (unsigned char *) &hdr, 1);
closure->write_func (closure->closure, (unsigned char *) &u, len);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_translate_real (csi_t *ctx,
csi_real_t real,
struct _translate_closure *closure)
{
uint8_t hdr;
if (real >= INT32_MIN && real <= INT32_MAX && (int) real == real)
return _translate_integer (ctx, real, closure);
#if WORDS_BIGENDIAN
hdr = MSB_FLOAT32;
#else
hdr = LSB_FLOAT32;
#endif
closure->write_func (closure->closure, (unsigned char *) &hdr, 1);
closure->write_func (closure->closure, (unsigned char *) &real, 4);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_translate_string (csi_t *ctx,
csi_string_t *string,
struct _translate_closure *closure)
{
uint8_t hdr;
union {
uint8_t u8;
uint16_t u16;
uint32_t u32;
} u;
int len;
#if WORDS_BIGENDIAN
if (string->len <= UINT8_MAX) {
hdr = STRING_1;
u.u8 = string->len;
len = 1;
} else if (string->len <= UINT16_MAX) {
hdr = STRING_2_MSB;
u.u16 = string->len;
len = 2;
} else {
hdr = STRING_4_MSB;
u.u32 = string->len;
len = 4;
}
#else
if (string->len <= UINT8_MAX) {
hdr = STRING_1;
u.u8 = string->len;
len = 1;
} else if (string->len <= UINT16_MAX) {
hdr = STRING_2_LSB;
u.u16 = string->len;
len = 2;
} else {
hdr = STRING_4_LSB;
u.u32 = string->len;
len = 4;
}
#endif
if (string->deflate)
hdr |= STRING_DEFLATE;
closure->write_func (closure->closure,
(unsigned char *) &hdr, 1);
closure->write_func (closure->closure,
(unsigned char *) &u, len);
if (string->deflate) {
uint32_t u32 = to_be32 (string->deflate);
closure->write_func (closure->closure,
(unsigned char *) &u32, 4);
}
closure->write_func (closure->closure,
(unsigned char *) string->string, string->len);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_translate_push (csi_t *ctx, csi_object_t *obj)
{
struct _translate_closure *closure = ctx->scanner.closure;
if (0) {
fprintf (stderr, "push ");
fprintf_obj (stderr, ctx, obj);
}
switch (csi_object_get_type (obj)) {
case CSI_OBJECT_TYPE_NAME:
return _translate_name (ctx, obj->datum.name, FALSE, closure);
case CSI_OBJECT_TYPE_OPERATOR:
return _translate_operator (ctx, obj->datum.op, FALSE, closure);
case CSI_OBJECT_TYPE_INTEGER:
return _translate_integer (ctx, obj->datum.integer, closure);
case CSI_OBJECT_TYPE_REAL:
return _translate_real (ctx, obj->datum.real, closure);
case CSI_OBJECT_TYPE_STRING:
return _translate_string (ctx, obj->datum.string, closure);
case CSI_OBJECT_TYPE_NULL:
case CSI_OBJECT_TYPE_BOOLEAN:
case CSI_OBJECT_TYPE_MARK:
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:
longjmp (ctx->scanner.jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));
break;
}
csi_object_free (ctx, obj);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_translate_execute (csi_t *ctx, csi_object_t *obj)
{
struct _translate_closure *closure = ctx->scanner.closure;
if (0) {
fprintf (stderr, "exec ");
fprintf_obj (stderr, ctx, obj);
}
switch (csi_object_get_type (obj)) {
case CSI_OBJECT_TYPE_NAME:
return _translate_name (ctx, obj->datum.name, TRUE, closure);
case CSI_OBJECT_TYPE_OPERATOR:
return _translate_operator (ctx, obj->datum.op, TRUE, closure);
case CSI_OBJECT_TYPE_INTEGER:
return _translate_integer (ctx, obj->datum.integer, closure);
case CSI_OBJECT_TYPE_REAL:
return _translate_real (ctx, obj->datum.real, closure);
case CSI_OBJECT_TYPE_STRING:
return _translate_string (ctx, obj->datum.string, closure);
case CSI_OBJECT_TYPE_NULL:
case CSI_OBJECT_TYPE_BOOLEAN:
case CSI_OBJECT_TYPE_MARK:
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:
longjmp (ctx->scanner.jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));
break;
}
return CSI_STATUS_SUCCESS;
}
static csi_status_t
build_opcodes (csi_t *ctx, csi_dictionary_t **out)
{
csi_object_t obj;
csi_dictionary_t *dict;
const csi_operator_def_t *def;
csi_status_t status;
int opcode = OPCODE << 8;
status = csi_dictionary_new (ctx, &obj);
if (_csi_unlikely (status))
return status;
dict = obj.datum.dictionary;
csi_integer_new (&obj, opcode++);
status = csi_dictionary_put (ctx, dict, 0, &obj);
if (_csi_unlikely (status))
goto FAIL;
for (def = _csi_operators (); def->name != NULL; def++) {
csi_object_t name;
csi_dictionary_entry_t *entry;
int code;
entry = _csi_hash_table_lookup (&dict->hash_table,
(csi_hash_entry_t *) &def->op);
if (entry == NULL) {
code = opcode++;
csi_integer_new (&obj, code);
status = csi_dictionary_put (ctx, dict, (csi_name_t) def->op, &obj);
if (_csi_unlikely (status))
goto FAIL;
} else {
code = entry->value.datum.integer;
csi_integer_new (&obj, code);
}
assert (ctx->opcode[code & 0xff] == def->op);
status = csi_name_new_static (ctx, &name, def->name);
if (_csi_unlikely (status))
goto FAIL;
status = csi_dictionary_put (ctx, dict, name.datum.name, &obj);
if (_csi_unlikely (status))
goto FAIL;
}
*out = dict;
return CSI_STATUS_SUCCESS;
FAIL:
csi_dictionary_free (ctx, dict);
return status;
}
csi_status_t
_csi_translate_file (csi_t *ctx,
csi_file_t *file,
cairo_write_func_t write_func,
void *closure)
{
csi_status_t status;
struct _translate_closure translator;
if ((status = setjmp (ctx->scanner.jmpbuf)))
return status;
status = build_opcodes (ctx, &translator.opcodes);
if (_csi_unlikely (status))
return status;
translator.write_func = write_func;
translator.closure = closure;
ctx->scanner.closure = &translator;
ctx->scanner.bind = 1;
ctx->scanner.push = _translate_push;
ctx->scanner.execute = _translate_execute;
_scan_file (ctx, file);
ctx->scanner.bind = 0;
ctx->scanner.push = _scan_push;
ctx->scanner.execute = _scan_execute;
csi_dictionary_free (ctx, translator.opcodes);
return CSI_STATUS_SUCCESS;
}