[cairo-commit] util/cairo-script

Chris Wilson ickle at kemper.freedesktop.org
Sat Jul 4 07:29:30 PDT 2009


 util/cairo-script/Makefile.am                |    5 
 util/cairo-script/cairo-script-file.c        |   44 +
 util/cairo-script/cairo-script-interpreter.c |   85 +-
 util/cairo-script/cairo-script-interpreter.h |    9 
 util/cairo-script/cairo-script-objects.c     |    8 
 util/cairo-script/cairo-script-operators.c   |    2 
 util/cairo-script/cairo-script-private.h     |   27 
 util/cairo-script/cairo-script-scanner.c     | 1059 +++++++++++++++++++--------
 8 files changed, 906 insertions(+), 333 deletions(-)

New commits:
commit 23648e2fdfefba4df08bc854d725758209998e1f
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Jul 3 00:40:32 2009 +0100

    [script] Prototypical binary translator
    
    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...

diff --git a/util/cairo-script/Makefile.am b/util/cairo-script/Makefile.am
index 644185b..c742cda 100644
--- a/util/cairo-script/Makefile.am
+++ b/util/cairo-script/Makefile.am
@@ -1,7 +1,7 @@
 SUBDIRS = examples
 
 lib_LTLIBRARIES = libcairo-script-interpreter.la
-EXTRA_PROGRAMS = csi-replay csi-exec
+EXTRA_PROGRAMS = csi-replay csi-exec csi-bind
 
 AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_builddir)/src
 
@@ -27,5 +27,8 @@ csi_replay_LDADD = libcairo-script-interpreter.la $(top_builddir)/src/libcairo.l
 csi_exec_SOURCES = csi-exec.c
 csi_exec_LDADD = libcairo-script-interpreter.la $(top_builddir)/src/libcairo.la $(CAIRO_LIBS)
 
+csi_bind_SOURCES = csi-bind.c
+csi_bind_LDADD = libcairo-script-interpreter.la $(top_builddir)/src/libcairo.la $(CAIRO_LIBS)
+
 EXTRA_DIST = \
 	COPYING
diff --git a/util/cairo-script/cairo-script-file.c b/util/cairo-script/cairo-script-file.c
index a330d0b..18b5b86 100644
--- a/util/cairo-script/cairo-script-file.c
+++ b/util/cairo-script/cairo-script-file.c
@@ -40,6 +40,8 @@
 
 #define CHUNK_SIZE 32768
 
+#define OWN_STREAM 0x1
+
 csi_status_t
 csi_file_new (csi_t *ctx,
 	      csi_object_t *obj,
@@ -56,6 +58,7 @@ csi_file_new (csi_t *ctx,
 
     file->data = NULL;
     file->type = STDIO;
+    file->flags = OWN_STREAM;
     file->src = fopen (path, mode);
     if (file->src == NULL) {
 	_csi_slab_free (ctx, file, sizeof (csi_file_t));
@@ -76,6 +79,42 @@ csi_file_new (csi_t *ctx,
 }
 
 csi_status_t
+csi_file_new_for_stream (csi_t *ctx,
+	                 csi_object_t *obj,
+			 FILE *stream)
+{
+    csi_file_t *file;
+
+    file = _csi_slab_alloc (ctx, sizeof (csi_file_t));
+    if (file == NULL)
+	return _csi_error (CAIRO_STATUS_NO_MEMORY);
+
+    file->base.type = CSI_OBJECT_TYPE_FILE;
+    file->base.ref = 1;
+
+    file->data = NULL;
+    file->type = STDIO;
+    file->flags = 0;
+    file->src = stream;
+    if (file->src == NULL) {
+	_csi_slab_free (ctx, file, sizeof (csi_file_t));
+	return _csi_error (CAIRO_STATUS_FILE_NOT_FOUND);
+    }
+
+    file->data = _csi_alloc (ctx, CHUNK_SIZE);
+    if (file->data == NULL) {
+	_csi_slab_free (ctx, file, sizeof (csi_file_t));
+	return _csi_error (CAIRO_STATUS_NO_MEMORY);
+    }
+    file->bp = file->data;
+    file->rem = 0;
+
+    obj->type = CSI_OBJECT_TYPE_FILE;
+    obj->datum.file = file;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+csi_status_t
 csi_file_new_for_bytes (csi_t *ctx,
 			csi_object_t *obj,
 			const char *bytes,
@@ -727,7 +766,7 @@ csi_file_new_decrypt (csi_t *ctx, csi_object_t *src, int salt, int discard)
 csi_status_t
 _csi_file_execute (csi_t *ctx, csi_file_t *obj)
 {
-    return _csi_scan_file (ctx, &ctx->scanner, obj);
+    return _csi_scan_file (ctx, obj);
 }
 
 int
@@ -917,7 +956,8 @@ csi_file_close (csi_t *ctx, csi_file_t *file)
 
     switch (file->type) {
     case STDIO:
-	fclose (file->src);
+	if (file->flags & OWN_STREAM)
+	    fclose (file->src);
 	break;
     case BYTES:
 	if (file->src != file->data) {
diff --git a/util/cairo-script/cairo-script-interpreter.c b/util/cairo-script/cairo-script-interpreter.c
index 16283e2..44c33f3 100644
--- a/util/cairo-script/cairo-script-interpreter.c
+++ b/util/cairo-script/cairo-script-interpreter.c
@@ -40,6 +40,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <math.h>
+#include <assert.h>
 
 #ifndef MAX
 #define MAX(a,b) (((a)>=(b))?(a):(b))
@@ -261,10 +262,11 @@ _init_dictionaries (csi_t *ctx)
     csi_status_t status;
     csi_stack_t *stack;
     csi_object_t obj;
-    csi_dictionary_t *dict;
+    csi_dictionary_t *dict, *opcodes;
     const csi_operator_def_t *odef;
     const csi_integer_constant_def_t *idef;
     const csi_real_constant_def_t *rdef;
+    unsigned n;
 
     stack = &ctx->dstack;
 
@@ -283,12 +285,37 @@ _init_dictionaries (csi_t *ctx)
 
     dict = obj.datum.dictionary;
 
+    status = csi_dictionary_new (ctx, &obj);
+    if (_csi_unlikely (status))
+	return status;
+
+    opcodes = obj.datum.dictionary;
+
+    n = 0;
+    csi_integer_new (&obj, n);
+    status = csi_dictionary_put (ctx, opcodes, 0, &obj);
+    if (_csi_unlikely (status))
+	return status;
+    ctx->opcode[n++] = NULL;
+
     /* fill systemdict with operators */
     for (odef = _csi_operators (); odef->name != NULL; odef++) {
 	status = _add_operator (ctx, dict, odef);
 	if (_csi_unlikely (status))
 	    return status;
+
+	if (! csi_dictionary_has (opcodes, (csi_name_t) odef->op)) {
+	    csi_integer_new (&obj, n);
+	    status = csi_dictionary_put (ctx,
+		                         opcodes, (csi_name_t) odef->op, &obj);
+	    if (_csi_unlikely (status))
+		return status;
+
+	    assert (n < sizeof (ctx->opcode) / sizeof (ctx->opcode[0]));
+	    ctx->opcode[n++] = odef->op;
+	}
     }
+    csi_dictionary_free (ctx, opcodes);
 
     /* add constants */
     for (idef = _csi_integer_constants (); idef->name != NULL; idef++) {
@@ -365,6 +392,8 @@ _csi_init (csi_t *ctx)
 {
     csi_status_t status;
 
+    memset (ctx, 0, sizeof (*ctx));
+
     ctx->status = CSI_STATUS_SUCCESS;
     ctx->ref_count = 1;
 
@@ -391,7 +420,7 @@ FAIL:
 }
 
 static void
-_csi_fini (csi_t *ctx)
+_csi_finish (csi_t *ctx)
 {
     _csi_stack_fini (ctx, &ctx->ostack);
     _csi_stack_fini (ctx, &ctx->dstack);
@@ -494,7 +523,7 @@ cairo_script_interpreter_create (void)
 {
     csi_t *ctx;
 
-    ctx = calloc (1, sizeof (csi_t));
+    ctx = malloc (sizeof (csi_t));
     if (ctx == NULL)
 	return (csi_t *) &_csi_nil;
 
@@ -580,7 +609,7 @@ cairo_script_interpreter_finish (csi_t *ctx)
 
     status = ctx->status;
     if (! ctx->finished) {
-	_csi_fini (ctx);
+	_csi_finish (ctx);
 	ctx->finished = 1;
     } else if (status == CAIRO_STATUS_SUCCESS) {
 	status = ctx->status = CSI_STATUS_INTERPRETER_FINISHED;
@@ -589,17 +618,11 @@ cairo_script_interpreter_finish (csi_t *ctx)
     return status;
 }
 
-cairo_status_t
-cairo_script_interpreter_destroy (csi_t *ctx)
+static void
+_csi_fini (csi_t *ctx)
 {
-    csi_status_t status;
-
-    status = ctx->status;
-    if (--ctx->ref_count)
-	return status;
-
     if (! ctx->finished)
-	_csi_fini (ctx);
+	_csi_finish (ctx);
 
     if (ctx->free_array != NULL)
 	csi_array_free (ctx, ctx->free_array);
@@ -610,8 +633,44 @@ cairo_script_interpreter_destroy (csi_t *ctx)
 
     _csi_slab_fini (ctx);
     _csi_perm_fini (ctx);
+}
+
+cairo_status_t
+cairo_script_interpreter_destroy (csi_t *ctx)
+{
+    csi_status_t status;
+
+    status = ctx->status;
+    if (--ctx->ref_count)
+	return status;
+
+    _csi_fini (ctx);
     free (ctx);
 
     return status;
 }
 slim_hidden_def (cairo_script_interpreter_destroy);
+
+cairo_status_t
+cairo_script_interpreter_translate_stream (FILE *stream,
+	                                   cairo_write_func_t write_func,
+					   void *closure)
+{
+    csi_t ctx;
+    csi_object_t src;
+    csi_status_t status;
+
+    _csi_init (&ctx);
+
+    status = csi_file_new_for_stream (&ctx, &src, stream);
+    if (status)
+	goto BAIL;
+
+    status = _csi_translate_file (&ctx, src.datum.file, write_func, closure);
+
+BAIL:
+    csi_object_free (&ctx, &src);
+    _csi_fini (&ctx);
+
+    return status;
+}
diff --git a/util/cairo-script/cairo-script-interpreter.h b/util/cairo-script/cairo-script-interpreter.h
index a500467..b7950aa 100644
--- a/util/cairo-script/cairo-script-interpreter.h
+++ b/util/cairo-script/cairo-script-interpreter.h
@@ -37,6 +37,7 @@
 #define CAIRO_SCRIPT_INTERPRETER_H
 
 #include <cairo.h>
+#include <stdio.h>
 
 CAIRO_BEGIN_DECLS
 
@@ -45,7 +46,8 @@ typedef struct _cairo_script_interpreter cairo_script_interpreter_t;
 /* XXX expose csi_dictionary_t and pass to hooks */
 typedef void
 (*csi_destroy_func_t) (void *closure,
-			       void *ptr);
+		       void *ptr);
+
 typedef cairo_surface_t *
 (*csi_surface_create_func_t) (void *closure,
 			      cairo_content_t content,
@@ -100,6 +102,11 @@ cairo_script_interpreter_finish (cairo_script_interpreter_t *ctx);
 cairo_public cairo_status_t
 cairo_script_interpreter_destroy (cairo_script_interpreter_t *ctx);
 
+cairo_public cairo_status_t
+cairo_script_interpreter_translate_stream (FILE *stream,
+	                                   cairo_write_func_t write_func,
+					   void *closure);
+
 CAIRO_END_DECLS
 
 #endif /*CAIRO_SCRIPT_INTERPRETER_H*/
diff --git a/util/cairo-script/cairo-script-objects.c b/util/cairo-script/cairo-script-objects.c
index c48f720..784376b 100644
--- a/util/cairo-script/cairo-script-objects.c
+++ b/util/cairo-script/cairo-script-objects.c
@@ -502,8 +502,10 @@ csi_string_new (csi_t *ctx,
 	ctx->free_string = NULL;
     }
 
-    memcpy (string->string, str, len);
-    string->string[len] = '\0';
+    if (str != NULL) {
+	memcpy (string->string, str, len);
+	string->string[len] = '\0';
+    }
     string->len = len;
 
     string->base.type = CSI_OBJECT_TYPE_STRING;
@@ -555,7 +557,7 @@ _csi_string_execute (csi_t *ctx, csi_string_t *string)
     if (_csi_unlikely (status))
 	return status;
 
-    status = _csi_scan_file (ctx, &ctx->scanner, obj.datum.file);
+    status = _csi_scan_file (ctx, obj.datum.file);
     csi_object_free (ctx, &obj);
 
     return status;
diff --git a/util/cairo-script/cairo-script-operators.c b/util/cairo-script/cairo-script-operators.c
index 407afae..da68b1b 100644
--- a/util/cairo-script/cairo-script-operators.c
+++ b/util/cairo-script/cairo-script-operators.c
@@ -5440,7 +5440,7 @@ _defs[] = {
     { "arc", _arc },
     { "arc-negative", _arc_negative },
     { "arc-", _arc_negative },
-    //{ "arc-to", NULL },
+    { "arc-to", NULL },
     { "array", _array },
     { "astore", NULL },
     { "atan", NULL },
diff --git a/util/cairo-script/cairo-script-private.h b/util/cairo-script/cairo-script-private.h
index c5c3b59..504218d 100644
--- a/util/cairo-script/cairo-script-private.h
+++ b/util/cairo-script/cairo-script-private.h
@@ -404,6 +404,7 @@ struct _csi_file {
 	PROCEDURE,
 	FILTER
     } type;
+    unsigned int flags;
     void *src;
     void *data;
     uint8_t *bp;
@@ -426,14 +427,9 @@ struct _csi_scanner {
     jmp_buf jmpbuf;
     int depth;
 
-    enum {
-	NONE,
-	TOKEN,
-	COMMENT,
-	STRING,
-	HEX,
-	BASE85
-    } state;
+    csi_status_t (*push) (csi_t *ctx, csi_object_t *obj);
+    csi_status_t (*execute) (csi_t *ctx, csi_object_t *obj);
+    void *closure;
 
     csi_buffer_t buffer;
     csi_stack_t procedure_stack;
@@ -478,6 +474,8 @@ struct _cairo_script_interpreter {
     csi_dictionary_t *free_dictionary;
     csi_string_t *free_string;
 
+    csi_operator_t opcode[256];
+
     /* caches of live data */
     csi_list_t *_images;
     csi_list_t *_faces;
@@ -506,6 +504,11 @@ csi_file_new (csi_t *ctx,
 	      const char *path, const char *mode);
 
 csi_private csi_status_t
+csi_file_new_for_stream (csi_t *ctx,
+	                 csi_object_t *obj,
+			 FILE *stream);
+
+csi_private csi_status_t
 csi_file_new_for_bytes (csi_t *ctx,
 			csi_object_t *obj,
 			const char *bytes,
@@ -803,7 +806,13 @@ csi_private csi_status_t
 _csi_scanner_init (csi_t *ctx, csi_scanner_t *scanner);
 
 csi_private csi_status_t
-_csi_scan_file (csi_t *ctx, csi_scanner_t *scan, csi_file_t *src);
+_csi_scan_file (csi_t *ctx, csi_file_t *src);
+
+csi_private csi_status_t
+_csi_translate_file (csi_t *ctx,
+	             csi_file_t *file,
+		     cairo_write_func_t write_func,
+		     void *closure);
 
 csi_private void
 _csi_scanner_fini (csi_t *ctx, csi_scanner_t *scanner);
diff --git a/util/cairo-script/cairo-script-scanner.c b/util/cairo-script/cairo-script-scanner.c
index 9cc2ce9..dd89826 100644
--- a/util/cairo-script/cairo-script-scanner.c
+++ b/util/cairo-script/cairo-script-scanner.c
@@ -38,6 +38,7 @@
 #include <math.h> /* pow */
 #include <stdio.h> /* EOF */
 #include <string.h> /* memset */
+#include <assert.h>
 
 /*
  * whitespace:
@@ -56,6 +57,90 @@
  * % = 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)
 {
@@ -80,7 +165,7 @@ buffer_fini (csi_t *ctx, csi_buffer_t *buffer)
     _csi_free (ctx, buffer->base);
 }
 
-static inline void
+static void
 _buffer_grow (csi_t *ctx, csi_scanner_t *scan)
 {
     int newsize;
@@ -121,16 +206,9 @@ buffer_reset (csi_buffer_t *buffer)
     buffer->ptr = buffer->base;
 }
 
-static inline void
-reset (csi_scanner_t *scan)
-{
-    scan->state = NONE;
-}
-
 static void
 token_start (csi_scanner_t *scan)
 {
-    scan->state = TOKEN;
     buffer_reset (&scan->buffer);
 }
 
@@ -344,7 +422,6 @@ token_end (csi_t *ctx, csi_scanner_t *scan, csi_file_t *src)
 	    longjmp (scan->jmpbuf, status);
 
 	scan->build_procedure.type |= CSI_OBJECT_ATTR_EXECUTABLE;
-	reset (scan);
 	return;
     } else if (s[0] == '}') {
 	if (_csi_unlikely
@@ -362,13 +439,12 @@ token_end (csi_t *ctx, csi_scanner_t *scan, csi_file_t *src)
 	    scan->build_procedure = *next;
 	    scan->procedure_stack.len--;
 	} else {
-	    status = _csi_push_ostack (ctx, &scan->build_procedure);
+	    status = scan_push (ctx, &scan->build_procedure);
 	    scan->build_procedure.type = CSI_OBJECT_TYPE_NULL;
 	}
 	if (_csi_unlikely (status))
 	    longjmp (scan->jmpbuf, status);
 
-	reset (scan);
 	return;
     }
 
@@ -395,43 +471,21 @@ token_end (csi_t *ctx, csi_scanner_t *scan, csi_file_t *src)
     }
 
     /* consume whitespace after token, before calling the interpreter */
-    reset (scan);
-
     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 = csi_object_execute (ctx, &obj);
+	status = scan_execute (ctx, &obj);
+	csi_object_free (ctx, &obj);
     } else {
-	status = _csi_push_ostack (ctx, &obj);
+	status = scan_push (ctx, &obj);
     }
     if (_csi_unlikely (status))
 	longjmp (scan->jmpbuf, status);
 }
 
 static void
-comment_start (csi_scanner_t *scan)
-{
-    /* XXX check for '!' interpreter mode?, '%' dsc setup? */
-    scan->state = COMMENT;
-}
-
-static void
-comment_end (csi_scanner_t *scan)
-{
-    reset (scan);
-}
-
-static void
-string_start (csi_scanner_t *scan)
-{
-    scan->state = STRING;
-    scan->string_p = 1;
-    buffer_reset (&scan->buffer);
-}
-
-static void
 string_inc_p (csi_scanner_t *scan)
 {
     scan->string_p++;
@@ -468,21 +522,9 @@ string_end (csi_t *ctx, csi_scanner_t *scan)
 				   scan->build_procedure.datum.array,
 				   &obj);
     else
-	status = _csi_push_ostack (ctx, &obj);
+	status = scan_push (ctx, &obj);
     if (_csi_unlikely (status))
 	longjmp (scan->jmpbuf, status);
-
-    reset (scan);
-}
-
-static void
-hex_start (csi_scanner_t *scan)
-{
-    scan->state = HEX;
-    scan->accumulator_count = 0;
-    scan->accumulator = 0;
-
-    buffer_reset (&scan->buffer);
 }
 
 static int
@@ -537,21 +579,9 @@ hex_end (csi_t *ctx, csi_scanner_t *scan)
 				   scan->build_procedure.datum.array,
 				   &obj);
     else
-	status = _csi_push_ostack (ctx, &obj);
+	status = scan_push (ctx, &obj);
     if (_csi_unlikely (status))
 	longjmp (scan->jmpbuf, status);
-
-    reset (scan);
-}
-
-static void
-base85_start (csi_scanner_t *scan)
-{
-    scan->state = BASE85;
-    scan->accumulator = 0;
-    scan->accumulator_count = 0;
-
-    buffer_reset (&scan->buffer);
 }
 
 static void
@@ -627,24 +657,63 @@ base85_end (csi_t *ctx, csi_scanner_t *scan)
 				   scan->build_procedure.datum.array,
 				   &obj);
     else
-	status = _csi_push_ostack (ctx, &obj);
+	status = scan_push (ctx, &obj);
     if (_csi_unlikely (status))
 	longjmp (scan->jmpbuf, status);
+}
 
-    reset (scan);
+static void
+scan_read (csi_scanner_t *scan, csi_file_t *src, void *ptr, int len)
+{
+    uint8_t *data = ptr;
+    while (len) {
+	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;
+    }
 }
 
-static int
-scan_none (csi_t *ctx,
-	   csi_scanner_t *scan,
-	   csi_file_t *src)
+static void
+string_read (csi_t *ctx, csi_scanner_t *scan, csi_file_t *src, int len, csi_object_t *obj)
 {
+    csi_status_t status;
+
+    status = csi_string_new (ctx, obj, NULL, len);
+    if (_csi_unlikely (status))
+	longjmp (scan->jmpbuf, status);
+
+    scan_read (scan, src, obj->datum.string->string, len);
+}
+
+#if WORDS_BIGENDIAN
+#define le16(x) bswap_16 (x)
+#define le32(x) bswap_32 (x)
+#define be16(x) x
+#define be32(x) x
+#else
+#define le16(x) x
+#define le32(x) x
+#define be16(x) bswap_16 (x)
+#define be32(x) bswap_32 (x)
+#endif
+static void
+_scan_file (csi_t *ctx, csi_file_t *src)
+{
+    csi_scanner_t *scan = &ctx->scanner;
     int c, next;
     union {
-	int i;
+	int8_t i8;
+	uint8_t u8;
+	int16_t i16;
+	uint16_t u16;
+	int32_t i32;
+	uint32_t u32;
 	float f;
     } u;
 
+scan_none:
     while ((c = csi_file_getc (src)) != EOF) {
 	csi_object_t obj = { CSI_OBJECT_TYPE_NULL };
 
@@ -659,12 +728,10 @@ scan_none (csi_t *ctx,
 	    break;
 
 	case '%':
-	    comment_start (scan);
-	    return 1;
+	    goto scan_comment;
 
 	case '(':
-	    string_start (scan);
-	    return 1;
+	    goto scan_string;
 
 	case '[': /* needs special case */
 	case ']':
@@ -673,88 +740,141 @@ scan_none (csi_t *ctx,
 	    token_start (scan);
 	    token_add_unchecked (scan, c);
 	    token_end (ctx, scan, src);
-	    return 1;
+	    goto scan_none;
 
 	case '<':
 	    next = csi_file_getc (src);
 	    switch (next) {
 	    case EOF:
 		csi_file_putc (src, '<');
-		return 0;
+		return;
 	    case '<':
 		/* dictionary name */
 		token_start (scan);
 		token_add_unchecked (scan, '<');
 		token_add_unchecked (scan, '<');
 		token_end (ctx, scan, src);
-		return 1;
+		goto scan_none;
 	    case '~':
-		base85_start (scan);
-		return 1;
+		goto scan_base85;
 	    default:
 		csi_file_putc (src, next);
-		hex_start (scan);
-		return 1;
+		goto scan_hex;
 	    }
 	    break;
 
 	    /* binary token */
-	case 128:
-	case 129:
-	case 130:
-	case 131:
-	    /* binary object sequence */
+#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 132: /* 32-bit integer, MSB */
+	case MSB_UINT8:
+	    scan_read (scan, src, &u.u8, 1);
+	    csi_integer_new (&obj, u.u8);
 	    break;
-	case 133: /* 32-bit integer, LSB */
+	case MSB_INT16:
+	    scan_read (scan, src, &u.i16, 2);
+	    csi_integer_new (&obj, be16 (u.i16));
 	    break;
-	case 134: /* 16-bit integer, MSB */
+	case LSB_INT16:
+	    scan_read (scan, src, &u.i16, 2);
+	    csi_integer_new (&obj, le16 (u.i16));
 	    break;
-	case 135: /* 16-bit integer, LSB */
+	case MSB_UINT16:
+	    scan_read (scan, src, &u.u16, 2);
+	    csi_integer_new (&obj, be16 (u.u16));
 	    break;
-	case 136: /* 8-bit integer */
+	case LSB_UINT16:
+	    scan_read (scan, src, &u.u16, 2);
+	    csi_integer_new (&obj, le16 (u.u16));
 	    break;
-	case 137: /* 16/32-bit fixed point */
+	case MSB_INT32:
+	    scan_read (scan, src, &u.i32, 4);
+	    csi_integer_new (&obj, be32 (u.i32));
 	    break;
-	case 138: /* 32-bit real, MSB */
-	    csi_file_read (src, &u.i, 4);
-#if ! WORDS_BIGENDIAN
-	    u.i = bswap_32 (u.i);
-#endif
-	    csi_real_new (&obj, u.f);
+	case LSB_INT32:
+	    scan_read (scan, src, &u.i32, 4);
+	    csi_integer_new (&obj, le32 (u.i32));
 	    break;
-	case 139: /* 32-bit real, LSB */
-	    csi_file_read (src, &u.f, 4);
-#if WORDS_BIGENDIAN
-	    u.i = bswap_32 (u.i);
-#endif
-	    csi_real_new (&obj, u.f);
+
+	case 136: /* 16.16 msb */
+	    scan_read (scan, src, &u.i32, 4);
+	    csi_real_new (&obj, be32 (u.i32) / 65536.);
 	    break;
-	case 140: /* 32-bit real, native */
-	    csi_file_read (src, &u.f, 4);
-	    csi_real_new (&obj, u.f);
+	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 141: /* boolean */
+	case 139: /* 24.8 lsb */
+	    scan_read (scan, src, &u.i32, 4);
+	    csi_real_new (&obj, le32 (u.i32) / 256.);
 	    break;
-	case 142: /* string of length 1n */
+	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;
-	case 143: /* string of length 2n (MSB) */
+
+#define STRING_1 142
+#define STRING_2_MSB 143
+#define STRING_2_LSB 144
+#define STRING_4_MSB 145
+#define STRING_4_LSB 146
+	case STRING_1:
+	    scan_read (scan, src, &u.u8, 1);
+	    string_read (ctx, scan, src, u.u8, &obj);
 	    break;
-	case 144: /* string of length 2n (LSB) */
+	case STRING_2_MSB:
+	    scan_read (scan, src, &u.u16, 2);
+	    string_read (ctx, scan, src, be16 (u.u16), &obj);
 	    break;
-	case 145: /* literal system name */
+	case STRING_2_LSB:
+	    scan_read (scan, src, &u.u16, 2);
+	    string_read (ctx, scan, src, le16 (u.u16), &obj);
 	    break;
-	case 146: /* executable system name */
+	case STRING_4_MSB:
+	    scan_read (scan, src, &u.u32, 4);
+	    string_read (ctx, scan, src, be32 (u.u32), &obj);
 	    break;
-	case 147: /* reserved */
+	case STRING_4_LSB:
+	    scan_read (scan, src, &u.u32, 4);
+	    string_read (ctx, scan, src, le32 (u.u32), &obj);
 	    break;
-	case 148: /* reserved */
+
+	case 147:
+	    scan_read (scan, src, &u.u8, 1);
+	    csi_operator_new (&obj, ctx->opcode[u.u8]);
 	    break;
-	case 149: /* homogeneous array */
+
+	case 148:
+	    scan_read (scan, src, &u.u8, 1);
+	    csi_operator_new (&obj, ctx->opcode[u.u8]);
+	    obj.type &= ~CSI_OBJECT_ATTR_EXECUTABLE;
 	    break;
 
 	    /* unassigned */
+	case 149:
 	case 150:
 	case 151:
 	case 152:
@@ -766,7 +886,6 @@ scan_none (csi_t *ctx,
 	case 158:
 	case 159:
 	    longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));
-	    return 0;
 
 	case '#': /* PDF 1.2 escape code */
 	    {
@@ -778,31 +897,29 @@ scan_none (csi_t *ctx,
 	default:
 	    token_start (scan);
 	    token_add_unchecked (scan, c);
-	    return 1;
+	    goto scan_token;
 	}
 
 	if (obj.type != CSI_OBJECT_TYPE_NULL) {
 	    cairo_status_t status;
 
-	    if (scan->build_procedure.type != CSI_OBJECT_TYPE_NULL)
+	    if (scan->build_procedure.type != CSI_OBJECT_TYPE_NULL) {
 		status = csi_array_append (ctx,
 					   scan->build_procedure.datum.array,
 					   &obj);
-	    else
-		status = csi_object_execute (ctx, &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;
 
-    return 0;
-}
-
-static int
-scan_token (csi_t *ctx, csi_scanner_t *scan, csi_file_t *src)
-{
-    int c;
-
+scan_token:
     while ((c = csi_file_getc (src)) != EOF) {
 	switch (c) {
 	case 0xa:
@@ -813,22 +930,20 @@ scan_token (csi_t *ctx, csi_scanner_t *scan, csi_file_t *src)
 	case 0xd:
 	case 0x20:
 	    token_end (ctx, scan, src);
-	    return 1;
+	    goto scan_none;
 
 	    /* syntax delimiters */
 	case '%':
 	    token_end (ctx, scan, src);
-	    comment_start (scan);
-	    return 1;
+	    goto scan_comment;
 	    /* syntax error? */
 	case '(':
 	    token_end (ctx, scan, src);
-	    string_start (scan);
-	    return 1;
+	    goto scan_string;
 	    /* XXX syntax error? */
 	case ')':
 	    token_end (ctx, scan, src);
-	    return 1;
+	    goto scan_none;
 	case '/':
 	    /* need to special case '^//?' */
 	    if (scan->buffer.ptr > scan->buffer.base+1 ||
@@ -838,7 +953,7 @@ scan_token (csi_t *ctx, csi_scanner_t *scan, csi_file_t *src)
 		token_start (scan);
 	    }
 	    token_add_unchecked (scan, '/');
-	    return 1;
+	    goto scan_token;
 
 	case '{':
 	case '}':
@@ -847,12 +962,12 @@ scan_token (csi_t *ctx, csi_scanner_t *scan, csi_file_t *src)
 	    token_start (scan);
 	    token_add_unchecked (scan, c);
 	    token_end (ctx, scan, src);
-	    return 1;
+	    goto scan_none;
 
 	case '<':
 	    csi_file_putc (src, '<');
 	    token_end (ctx, scan, src);
-	    return 1;
+	    goto scan_none;
 
 	case '#': /* PDF 1.2 escape code */
 	    {
@@ -867,100 +982,23 @@ scan_token (csi_t *ctx, csi_scanner_t *scan, csi_file_t *src)
 	}
     }
     token_end (ctx, scan, src);
+    return;
 
-    return 0;
-}
-
-static int
-scan_hex (csi_t *ctx, csi_scanner_t *scan, csi_file_t *src)
-{
-    int c;
-
+scan_comment:
+    /* discard until newline */
     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' */
-	    return 1;
-
-	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));
-	    return 0;
+	    goto scan_none;
 	}
     }
+    return;
 
-    longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));
-    return 0;
-}
-
-static int
-scan_base85 (csi_t *ctx, csi_scanner_t *scan, csi_file_t *src)
-{
-    int c, next;
-
-    while ((c = csi_file_getc (src)) != EOF) {
-	switch (c) {
-	case '~':
-	    next = csi_file_getc (src);
-	    switch (next) {
-	    case EOF:
-		return 0;
-
-	    case '>':
-		base85_end (ctx, scan);
-		return 1;
-	    }
-	    csi_file_putc (src, next);
-
-	    /* fall-through */
-	default:
-	    base85_add (ctx, scan, c);
-	    break;
-	}
-    }
-
-    longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));
-    return 0;
-}
-
-static int
-scan_string (csi_t *ctx, csi_scanner_t *scan, csi_file_t *src)
-{
-    int c, next;
-
+scan_string:
+    buffer_reset (&scan->buffer);
+    scan->string_p = 1;
     while ((c = csi_file_getc (src)) != EOF) {
 	switch (c) {
 	case '\\': /* escape */
@@ -968,7 +1006,6 @@ scan_string (csi_t *ctx, csi_scanner_t *scan, csi_file_t *src)
 	    switch (next) {
 	    case EOF:
 		longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));
-		return 0;
 
 	    case 'n':
 		string_add (ctx, scan, '\n');
@@ -1006,7 +1043,7 @@ scan_string (csi_t *ctx, csi_scanner_t *scan, csi_file_t *src)
 			next = csi_file_getc (src);
 			switch (next) {
 			case EOF:
-			    return 0;
+			    return;
 
 			case '0': case '1': case '2': case '3':
 			case '4': case '5': case '6': case '7':
@@ -1028,7 +1065,7 @@ scan_string (csi_t *ctx, csi_scanner_t *scan, csi_file_t *src)
 		next = csi_file_getc (src); /* might be compound LFCR */
 		switch (next) {
 		case EOF:
-		    return 0;
+		    return;
 		case 0xc:
 		    break;
 		default:
@@ -1054,139 +1091,555 @@ scan_string (csi_t *ctx, csi_scanner_t *scan, csi_file_t *src)
 	case ')':
 	    if (string_dec_p (scan)) {
 		string_end (ctx, scan);
-		return 1;
-	    } else
-		string_add (ctx, scan, c);
-	    break;
-
+		goto scan_none;
+	    }
+	    /* fall through */
 	default:
 	    string_add (ctx, scan, c);
 	    break;
 	}
     }
-
     longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));
-    return 0;
-}
-
-static int
-scan_comment (csi_t *ctx, csi_scanner_t *scan, csi_file_t *src)
-{
-    int c;
 
-    /* discard until newline */
+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:
-	    comment_end (scan);
-	    return 1;
+	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));
 
-    return 0;
+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 '>':
+		base85_end (ctx, scan);
+		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 (0) {
+	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 (0) {
+	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->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_scanner_t *scan, csi_file_t *src)
+_csi_scan_file (csi_t *ctx, csi_file_t *src)
 {
-    static int (* const func[]) (csi_t *, csi_scanner_t *, csi_file_t *) = {
-	scan_none,
-	scan_token,
-	scan_comment,
-	scan_string,
-	scan_hex,
-	scan_base85,
-    };
     csi_status_t status;
 
     /* This function needs to be reentrant to handle recursive scanners.
      * i.e. one script executes a second.
      */
 
-    if (scan->depth++ == 0) {
-	if ((status = setjmp (scan->jmpbuf))) {
-	    scan->depth = 0;
+    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->line_number = 0; /* XXX broken by recursive scanning */
-    while (func[scan->state] (ctx, scan, src))
-	;
+    _scan_file (ctx, src);
 
-    --scan->depth;
+    --ctx->scanner.depth;
     return CSI_STATUS_SUCCESS;
 }
 
-#if 0
-cairo_status_t
-_csi_tokenize_string (csi_t *ctx,
-		      const char *code, int len,
-		      csi_object_t **array_out)
+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)
 {
-    csi_scanner_t scan;
-    csi_object_t *src;
-    cairo_status_t status;
+    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);
+    }
 
-    status = _csi_scanner_init (&scan, ctx);
-    if (status)
-	return status;
+    return CSI_STATUS_SUCCESS;
+}
 
-    scan.build_procedure = csi_array_new (ctx, 0);
-    if (scan.build_procedure == NULL)
-	goto CLEANUP_SCAN;
-    csi_object_set_literal (scan.build_procedure, FALSE);
+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;
 
-    src = csi_file_new_for_string (ctx, (const uint8_t *) code, len);
-    if (src == NULL) {
-	status = _csi_error (CSI_STATUS_NO_MEMORY);
-	goto CLEANUP_SCAN;
+    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
 
-    status = _csi_scan_object (&scan, src);
-    if (status)
-	goto CLEANUP_SRC;
+    closure->write_func (closure->closure, (unsigned char *) &hdr, 1);
+    closure->write_func (closure->closure, (unsigned char *) &u, len);
 
-    *array_out = scan.build_procedure;
-    scan.build_procedure = NULL;
+    return CSI_STATUS_SUCCESS;
+}
 
-CLEANUP_SRC:
-    csi_object_free (src);
+static csi_status_t
+_translate_real (csi_t *ctx,
+	         csi_real_t real,
+		 struct _translate_closure *closure)
+{
+    uint8_t hdr;
 
-CLEANUP_SCAN:
-    _csi_scanner_fini (&scan);
+    if (real >= INT32_MIN && real <= INT32_MAX && (int) real == real)
+	return _translate_integer (ctx, real, closure);
 
-    return status;
+#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
 
-csi_status_t
-_csi_scanner_init (csi_t *ctx, csi_scanner_t *scanner)
+    closure->write_func (closure->closure,
+	                 (unsigned char *) &hdr, 1);
+    closure->write_func (closure->closure,
+	                 (unsigned char *) &u, len);
+    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)
 {
-    csi_status_t status;
+    struct _translate_closure *closure = ctx->scanner.closure;
 
-    memset (scanner, 0, sizeof (csi_scanner_t));
+    if (0) {
+	fprintf (stderr, "push ");
+	fprintf_obj (stderr, ctx, obj);
+    }
 
-    status = buffer_init (ctx, &scanner->buffer);
-    if (status)
-	return status;
+    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;
+    }
 
-    status = _csi_stack_init (ctx, &scanner->procedure_stack, 4);
-    if (status)
+    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 = 147 << 8;
+
+    status = csi_dictionary_new (ctx, &obj);
+    if (_csi_unlikely (status))
 	return status;
 
-    reset (scanner);
+    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;
 }
 
-void
-_csi_scanner_fini (csi_t *ctx, csi_scanner_t *scanner)
+csi_status_t
+_csi_translate_file (csi_t *ctx,
+	             csi_file_t *file,
+		     cairo_write_func_t write_func,
+		     void *closure)
 {
-    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 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.push = _translate_push;
+    ctx->scanner.execute = _translate_execute;
+
+    _scan_file (ctx, file);
+
+    ctx->scanner.push = _scan_push;
+    ctx->scanner.execute = _scan_execute;
+
+    csi_dictionary_free (ctx, translator.opcodes);
+
+    return CSI_STATUS_SUCCESS;
 }


More information about the cairo-commit mailing list