1
/*
2
 * Copyright © 2008 Chris Wilson <chris@chris-wilson.co.uk>
3
 *
4
 * This library is free software; you can redistribute it and/or
5
 * modify it either under the terms of the GNU Lesser General Public
6
 * License version 2.1 as published by the Free Software Foundation
7
 * (the "LGPL") or, at your option, under the terms of the Mozilla
8
 * Public License Version 1.1 (the "MPL"). If you do not alter this
9
 * notice, a recipient may use your version of this file under either
10
 * the MPL or the LGPL.
11
 *
12
 * You should have received a copy of the LGPL along with this library
13
 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
15
 * You should have received a copy of the MPL along with this library
16
 * in the file COPYING-MPL-1.1
17
 *
18
 * The contents of this file are subject to the Mozilla Public License
19
 * Version 1.1 (the "License"); you may not use this file except in
20
 * compliance with the License. You may obtain a copy of the License at
21
 * http://www.mozilla.org/MPL/
22
 *
23
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
24
 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
25
 * the specific language governing rights and limitations.
26
 *
27
 * The Original Code is the cairo graphics library.
28
 *
29
 * The Initial Developer of the Original Code is Chris Wilson.
30
 *
31
 * Contributor(s):
32
 *	Chris Wilson <chris@chris-wilson.co.uk>
33
 */
34

            
35
#include "config.h"
36

            
37
#include "cairo.h"
38

            
39
#include "cairo-script-private.h"
40

            
41
#include <stdio.h>
42
#include <stdlib.h>
43
#include <string.h>
44
#include <math.h>
45
#include <assert.h>
46

            
47
#ifndef MAX
48
#define MAX(a,b) (((a)>=(b))?(a):(b))
49
#endif
50

            
51
csi_status_t
52
_csi_error (csi_status_t status)
53
{
54
    return status;
55
}
56

            
57
/* XXX track global/local memory, cap etc, mark/sweep GC */
58
void *
59
_csi_alloc (csi_t *ctx, int size)
60
{
61
    return malloc (size);
62
}
63

            
64
void *
65
_csi_alloc0 (csi_t *ctx, int size)
66
{
67
    void *ptr;
68

            
69
    ptr = _csi_alloc (ctx, size);
70
    if (_csi_likely (ptr != NULL))
71
	memset (ptr, 0, size);
72

            
73
    return ptr;
74
}
75

            
76
void *
77
_csi_realloc (csi_t *ctx, void *ptr, int size)
78
{
79
    return realloc (ptr, size);
80
}
81

            
82
void
83
_csi_free (csi_t *ctx, void *ptr)
84
{
85
    if (_csi_unlikely (ptr == NULL))
86
	return;
87

            
88
    free (ptr);
89
}
90

            
91
void *
92
_csi_perm_alloc (csi_t *ctx, int size)
93
{
94
    csi_chunk_t *chunk;
95
    void *ptr;
96

            
97
    size = (size + sizeof (void *)-1) & -sizeof (void *);
98

            
99
    chunk = ctx->perm_chunk;
100
    if (chunk == NULL || chunk->rem < size) {
101
	int chunk_size = (size + 8191) & -8192;
102
	chunk = _csi_alloc (ctx, sizeof (csi_chunk_t) + chunk_size);
103
	if (_csi_unlikely (chunk == NULL))
104
	    return NULL;
105

            
106
	chunk->rem = chunk_size;
107
	chunk->ptr = (char *) (chunk + 1);
108
	chunk->next = ctx->perm_chunk;
109
	ctx->perm_chunk = chunk;
110
    }
111

            
112
    ptr = chunk->ptr;
113
    chunk->ptr += size;
114
    chunk->rem -= size;
115

            
116
    return ptr;
117
}
118

            
119
void *
120
_csi_slab_alloc (csi_t *ctx, int size)
121
{
122
#if CSI_DEBUG_MALLOC
123
    return malloc (size);
124
#else
125
    int chunk_size;
126
    csi_chunk_t *chunk;
127
    void *ptr;
128

            
129
    chunk_size = 2 * sizeof (void *);
130
    chunk_size = (size + chunk_size - 1) / chunk_size;
131

            
132
    if (ctx->slabs[chunk_size].free_list) {
133
	ptr = ctx->slabs[chunk_size].free_list;
134
	ctx->slabs[chunk_size].free_list = *(void **) ptr;
135
	return ptr;
136
    }
137

            
138
    chunk = ctx->slabs[chunk_size].chunk;
139
    if (chunk == NULL || ! chunk->rem) {
140
	int cnt = MAX (128, 8192 / (chunk_size * 2 * sizeof (void *)));
141

            
142
	chunk = _csi_alloc (ctx,
143
			    sizeof (csi_chunk_t) +
144
			    cnt * chunk_size * 2 * sizeof (void *));
145
	if (_csi_unlikely (chunk == NULL))
146
	    return NULL;
147

            
148
	chunk->rem = cnt;
149
	chunk->ptr = (char *) (chunk + 1);
150
	chunk->next = ctx->slabs[chunk_size].chunk;
151
	ctx->slabs[chunk_size].chunk = chunk;
152
    }
153

            
154
    ptr = chunk->ptr;
155
    chunk->ptr += chunk_size * 2 * sizeof (void *);
156
    chunk->rem--;
157

            
158
    return ptr;
159
#endif
160
}
161

            
162
void
163
_csi_slab_free (csi_t *ctx, void *ptr, int size)
164
{
165
    int chunk_size;
166
    void **free_list;
167

            
168
    if (_csi_unlikely (ptr == NULL))
169
	return;
170

            
171
#if CSI_DEBUG_MALLOC
172
    free (ptr);
173
#else
174
    chunk_size = 2 * sizeof (void *);
175
    chunk_size = (size + chunk_size - 1) / chunk_size;
176

            
177
    free_list = ptr;
178
    *free_list = ctx->slabs[chunk_size].free_list;
179
    ctx->slabs[chunk_size].free_list = ptr;
180
#endif
181
}
182

            
183
csi_status_t
184
_csi_stack_push (csi_t *ctx, csi_stack_t *stack,
185
		 const csi_object_t *obj)
186
{
187
    if (_csi_unlikely (stack->len == stack->size))
188
	return _csi_stack_push_internal (ctx, stack, obj);
189

            
190
    stack->objects[stack->len++] = *obj;
191
    return CSI_STATUS_SUCCESS;
192
}
193

            
194
static void
195
_csi_perm_fini (csi_t *ctx)
196
{
197
    while (ctx->perm_chunk != NULL) {
198
	csi_chunk_t *chunk = ctx->perm_chunk;
199
	ctx->perm_chunk = chunk->next;
200
	_csi_free (ctx, chunk);
201
    }
202
}
203

            
204
static void
205
_csi_slab_fini (csi_t *ctx)
206
{
207
    unsigned int i;
208

            
209
    for (i = 0; i < sizeof (ctx->slabs) / sizeof (ctx->slabs[0]); i++) {
210
	while (ctx->slabs[i].chunk != NULL) {
211
	    csi_chunk_t *chunk = ctx->slabs[i].chunk;
212
	    ctx->slabs[i].chunk = chunk->next;
213
	    _csi_free (ctx, chunk);
214
	}
215
    }
216
}
217

            
218
static csi_status_t
219
_add_operator (csi_t *ctx,
220
	       csi_dictionary_t *dict,
221
	       const csi_operator_def_t *def)
222
{
223
    csi_object_t name;
224
    csi_object_t operator;
225
    csi_status_t status;
226

            
227
    status = csi_name_new_static (ctx, &name, def->name);
228
    if (status)
229
	return status;
230

            
231
    csi_operator_new (&operator, def->op);
232

            
233
    return csi_dictionary_put (ctx, dict, name.datum.name, &operator);
234
}
235

            
236
static csi_status_t
237
_add_integer_constant (csi_t *ctx,
238
		       csi_dictionary_t *dict,
239
		       const csi_integer_constant_def_t *def)
240
{
241
    csi_object_t name;
242
    csi_object_t constant;
243
    csi_status_t status;
244

            
245
    status = csi_name_new_static (ctx, &name, def->name);
246
    if (status)
247
	return status;
248

            
249
    csi_integer_new (&constant, def->value);
250

            
251
    return csi_dictionary_put (ctx, dict, name.datum.name, &constant);
252
}
253

            
254
static csi_status_t
255
_add_real_constant (csi_t *ctx,
256
		    csi_dictionary_t *dict,
257
		    const csi_real_constant_def_t *def)
258
{
259
    csi_object_t name;
260
    csi_object_t constant;
261
    csi_status_t status;
262

            
263
    status = csi_name_new_static (ctx, &name, def->name);
264
    if (status)
265
	return status;
266

            
267
    csi_real_new (&constant, def->value);
268

            
269
    return csi_dictionary_put (ctx, dict, name.datum.name, &constant);
270
}
271

            
272
static csi_status_t
273
_init_dictionaries (csi_t *ctx)
274
{
275
    csi_status_t status;
276
    csi_stack_t *stack;
277
    csi_object_t obj;
278
    csi_dictionary_t *dict, *opcodes;
279
    const csi_operator_def_t *odef;
280
    const csi_integer_constant_def_t *idef;
281
    const csi_real_constant_def_t *rdef;
282
    unsigned n;
283

            
284
    stack = &ctx->dstack;
285

            
286
    status = _csi_stack_init (ctx, stack, 4);
287
    if (_csi_unlikely (status))
288
	return status;
289

            
290
    /* systemdict */
291
    status = csi_dictionary_new (ctx, &obj);
292
    if (_csi_unlikely (status))
293
	return status;
294

            
295
    status = _csi_stack_push (ctx, stack, &obj);
296
    if (_csi_unlikely (status))
297
	return status;
298

            
299
    dict = obj.datum.dictionary;
300

            
301
    status = csi_dictionary_new (ctx, &obj);
302
    if (_csi_unlikely (status))
303
	return status;
304

            
305
    opcodes = obj.datum.dictionary;
306

            
307
    n = 0;
308
    csi_integer_new (&obj, n);
309
    status = csi_dictionary_put (ctx, opcodes, 0, &obj);
310
    if (_csi_unlikely (status))
311
	return status;
312
    ctx->opcode[n++] = NULL;
313

            
314
    /* fill systemdict with operators */
315
    for (odef = _csi_operators (); odef->name != NULL; odef++) {
316
	status = _add_operator (ctx, dict, odef);
317
	if (_csi_unlikely (status))
318
	    return status;
319

            
320
	if (! csi_dictionary_has (opcodes, (csi_name_t) odef->op)) {
321
	    csi_integer_new (&obj, n);
322
	    status = csi_dictionary_put (ctx,
323
		                         opcodes, (csi_name_t) odef->op, &obj);
324
	    if (_csi_unlikely (status))
325
		return status;
326

            
327
	    assert (n < sizeof (ctx->opcode) / sizeof (ctx->opcode[0]));
328
	    ctx->opcode[n++] = odef->op;
329
	}
330
    }
331
    csi_dictionary_free (ctx, opcodes);
332

            
333
    /* add constants */
334
    for (idef = _csi_integer_constants (); idef->name != NULL; idef++) {
335
	status = _add_integer_constant (ctx, dict, idef);
336
	if (_csi_unlikely (status))
337
	    return status;
338
    }
339
    for (rdef = _csi_real_constants (); rdef->name != NULL; rdef++) {
340
	status = _add_real_constant (ctx, dict, rdef);
341
	if (_csi_unlikely (status))
342
	    return status;
343
    }
344

            
345
    /* and seal */
346
    //dict.type &= ~CSI_OBJECT_ATTR_WRITABLE;
347

            
348

            
349
    /* globaldict */
350
    status = csi_dictionary_new (ctx, &obj);
351
    if (_csi_unlikely (status))
352
	return status;
353
    status = _csi_stack_push (ctx, stack, &obj);
354
    if (_csi_unlikely (status))
355
	return status;
356

            
357
    /* userdict */
358
    status = csi_dictionary_new (ctx, &obj);
359
    if (_csi_unlikely (status))
360
	return status;
361
    status = _csi_stack_push (ctx, stack, &obj);
362
    if (_csi_unlikely (status))
363
	return status;
364

            
365
    return CSI_STATUS_SUCCESS;
366
}
367

            
368
/* intern string */
369

            
370
typedef struct _cairo_intern_string {
371
    csi_hash_entry_t hash_entry;
372
    int len;
373
    char *string;
374
} csi_intern_string_t;
375

            
376
static unsigned long
377
_intern_string_hash (const char *str, int len)
378
{
379
    const signed char *p = (const signed char *) str;
380
    if (len > 0) {
381
	unsigned int h = *p;
382

            
383
	while (--len)
384
	    h = (h << 5) - h + *++p;
385

            
386
	return h;
387
    }
388
    return 0;
389
}
390

            
391
static cairo_bool_t
392
_intern_string_equal (const void *_a, const void *_b)
393
{
394
    const csi_intern_string_t *a = _a;
395
    const csi_intern_string_t *b = _b;
396

            
397
    if (a->len != b->len)
398
	return FALSE;
399

            
400
    return memcmp (a->string, b->string, a->len) == 0;
401
}
402

            
403
static void
404
_csi_init (csi_t *ctx)
405
{
406
    csi_status_t status;
407

            
408
    memset (ctx, 0, sizeof (*ctx));
409

            
410
    ctx->status = CSI_STATUS_SUCCESS;
411
    ctx->ref_count = 1;
412
    ctx->scanner.line_number = -1;
413

            
414
    status = _csi_hash_table_init (&ctx->strings, _intern_string_equal);
415
    if (status)
416
	goto FAIL;
417

            
418
    status = _csi_stack_init (ctx, &ctx->ostack, 2048);
419
    if (status)
420
	goto FAIL;
421
    status = _init_dictionaries (ctx);
422
    if (status)
423
	goto FAIL;
424

            
425
    status = _csi_scanner_init (ctx, &ctx->scanner);
426
    if (status)
427
	goto FAIL;
428

            
429
    return;
430

            
431
FAIL:
432
    if (ctx->status == CSI_STATUS_SUCCESS)
433
	ctx->status = status;
434
}
435

            
436
static void
437
_csi_finish (csi_t *ctx)
438
{
439
    _csi_stack_fini (ctx, &ctx->ostack);
440
    _csi_stack_fini (ctx, &ctx->dstack);
441
    _csi_scanner_fini (ctx, &ctx->scanner);
442

            
443
    _csi_hash_table_fini (&ctx->strings);
444
}
445

            
446
csi_status_t
447
_csi_name_define (csi_t *ctx, csi_name_t name, csi_object_t *obj)
448
{
449
    return csi_dictionary_put (ctx,
450
			ctx->dstack.objects[ctx->dstack.len-1].datum.dictionary,
451
			name,
452
			obj);
453
}
454

            
455
csi_status_t
456
_csi_name_lookup (csi_t *ctx, csi_name_t name, csi_object_t *obj)
457
{
458
    int i;
459

            
460
    for (i = ctx->dstack.len; i--; ) {
461
	csi_dictionary_t *dict;
462
	csi_dictionary_entry_t *entry;
463

            
464
	dict = ctx->dstack.objects[i].datum.dictionary;
465
	entry = _csi_hash_table_lookup (&dict->hash_table,
466
					(csi_hash_entry_t *) &name);
467
	if (entry != NULL) {
468
	    *obj = entry->value;
469
	    return CSI_STATUS_SUCCESS;
470
	}
471
    }
472

            
473
    return _csi_error (CSI_STATUS_INVALID_SCRIPT);
474
}
475

            
476
csi_status_t
477
_csi_name_undefine (csi_t *ctx, csi_name_t name)
478
{
479
    unsigned int i;
480

            
481
    for (i = ctx->dstack.len; --i; ) {
482
	if (csi_dictionary_has (ctx->dstack.objects[i].datum.dictionary,
483
				name))
484
	{
485
	    csi_dictionary_remove (ctx,
486
				   ctx->dstack.objects[i].datum.dictionary,
487
				   name);
488
	    return CSI_STATUS_SUCCESS;
489
	}
490
    }
491

            
492
    return _csi_error (CSI_STATUS_INVALID_SCRIPT);
493
}
494

            
495
csi_status_t
496
_csi_intern_string (csi_t *ctx, const char **str_inout, int len)
497
{
498
    char *str = (char *) *str_inout;
499
    csi_intern_string_t tmpl, *istring;
500
    csi_status_t status = CSI_STATUS_SUCCESS;
501

            
502
    tmpl.hash_entry.hash = _intern_string_hash (str, len);
503
    tmpl.len = len;
504
    tmpl.string = (char *) str;
505

            
506
    istring = _csi_hash_table_lookup (&ctx->strings, &tmpl.hash_entry);
507
    if (istring == NULL) {
508
	istring = _csi_perm_alloc (ctx,
509
				   sizeof (csi_intern_string_t) + len + 1);
510
	if (istring != NULL) {
511
	    istring->hash_entry.hash = tmpl.hash_entry.hash;
512
	    istring->len = tmpl.len;
513
	    istring->string = (char *) (istring + 1);
514
	    memcpy (istring->string, str, len);
515
	    istring->string[len] = '\0';
516

            
517
	    status = _csi_hash_table_insert (&ctx->strings,
518
					     &istring->hash_entry);
519
	    if (_csi_unlikely (status)) {
520
		_csi_free (ctx, istring);
521
		return status;
522
	    }
523
	} else
524
	    return _csi_error (CSI_STATUS_NO_MEMORY);
525
    }
526

            
527
    *str_inout = istring->string;
528
    return CSI_STATUS_SUCCESS;
529
}
530

            
531
/* Public */
532

            
533
static csi_t _csi_nil = { -1, CSI_STATUS_NO_MEMORY };
534

            
535
csi_t *
536
cairo_script_interpreter_create (void)
537
{
538
    csi_t *ctx;
539

            
540
    ctx = malloc (sizeof (csi_t));
541
    if (ctx == NULL)
542
	return (csi_t *) &_csi_nil;
543

            
544
    _csi_init (ctx);
545

            
546
    return ctx;
547
}
548

            
549
void
550
cairo_script_interpreter_install_hooks (csi_t *ctx,
551
					const csi_hooks_t *hooks)
552
{
553
    if (ctx->status)
554
	return;
555

            
556
    ctx->hooks = *hooks;
557
}
558

            
559
cairo_status_t
560
cairo_script_interpreter_run (csi_t *ctx, const char *filename)
561
{
562
    csi_object_t file;
563

            
564
    if (ctx->status)
565
	return ctx->status;
566
    if (ctx->finished)
567
	return ctx->status = CSI_STATUS_INTERPRETER_FINISHED;
568

            
569
    ctx->status = csi_file_new (ctx, &file, filename, "r");
570
    if (ctx->status)
571
	return ctx->status;
572

            
573
    file.type |= CSI_OBJECT_ATTR_EXECUTABLE;
574

            
575
    ctx->status = csi_object_execute (ctx, &file);
576
    csi_object_free (ctx, &file);
577

            
578
    return ctx->status;
579
}
580

            
581
cairo_status_t
582
cairo_script_interpreter_feed_stream (csi_t *ctx, FILE *stream)
583
{
584
    csi_object_t file;
585

            
586
    if (ctx->status)
587
	return ctx->status;
588
    if (ctx->finished)
589
	return ctx->status = CSI_STATUS_INTERPRETER_FINISHED;
590

            
591
    ctx->status = csi_file_new_for_stream (ctx, &file, stream);
592
    if (ctx->status)
593
	return ctx->status;
594

            
595
    file.type |= CSI_OBJECT_ATTR_EXECUTABLE;
596

            
597
    ctx->status = csi_object_execute (ctx, &file);
598
    csi_object_free (ctx, &file);
599

            
600
    return ctx->status;
601
}
602

            
603
cairo_status_t
604
cairo_script_interpreter_feed_string (csi_t *ctx, const char *line, int len)
605
{
606
    csi_object_t file;
607

            
608
    if (ctx->status)
609
	return ctx->status;
610
    if (ctx->finished)
611
	return ctx->status = CSI_STATUS_INTERPRETER_FINISHED;
612

            
613
    if (len < 0)
614
	len = strlen (line);
615
    ctx->status = csi_file_new_for_bytes (ctx, &file, line, len);
616
    if (ctx->status)
617
	return ctx->status;
618

            
619
    file.type |= CSI_OBJECT_ATTR_EXECUTABLE;
620

            
621
    ctx->status = csi_object_execute (ctx, &file);
622
    csi_object_free (ctx, &file);
623

            
624
    return ctx->status;
625
}
626

            
627
unsigned int
628
cairo_script_interpreter_get_line_number (csi_t *ctx)
629
{
630
    return ctx->scanner.line_number + 1; /* 1 index based */
631
}
632

            
633
csi_t *
634
cairo_script_interpreter_reference (csi_t *ctx)
635
{
636
    ctx->ref_count++;
637
    return ctx;
638
}
639

            
640
cairo_status_t
641
cairo_script_interpreter_finish (csi_t *ctx)
642
{
643
    csi_status_t status;
644

            
645
    status = ctx->status;
646
    if (! ctx->finished) {
647
	_csi_finish (ctx);
648
	ctx->finished = 1;
649
    } else if (status == CSI_STATUS_SUCCESS) {
650
	status = ctx->status = CSI_STATUS_INTERPRETER_FINISHED;
651
    }
652

            
653
    return status;
654
}
655

            
656
static void
657
_csi_fini (csi_t *ctx)
658
{
659
    if (! ctx->finished)
660
	_csi_finish (ctx);
661

            
662
    if (ctx->free_array != NULL)
663
	csi_array_free (ctx, ctx->free_array);
664
    if (ctx->free_dictionary != NULL)
665
	csi_dictionary_free (ctx, ctx->free_dictionary);
666
    if (ctx->free_string != NULL)
667
	csi_string_free (ctx, ctx->free_string);
668

            
669
    _csi_slab_fini (ctx);
670
    _csi_perm_fini (ctx);
671
}
672

            
673
cairo_status_t
674
cairo_script_interpreter_destroy (csi_t *ctx)
675
{
676
    csi_status_t status;
677

            
678
    status = ctx->status;
679
    if (--ctx->ref_count)
680
	return status;
681

            
682
    _csi_fini (ctx);
683
    free (ctx);
684

            
685
    return status;
686
}
687

            
688
cairo_status_t
689
cairo_script_interpreter_translate_stream (FILE *stream,
690
	                                   cairo_write_func_t write_func,
691
					   void *closure)
692
{
693
    csi_t ctx;
694
    csi_object_t src;
695
    csi_status_t status;
696

            
697
    _csi_init (&ctx);
698

            
699
    status = csi_file_new_for_stream (&ctx, &src, stream);
700
    if (status)
701
	goto BAIL;
702

            
703
    status = _csi_translate_file (&ctx, src.datum.file, write_func, closure);
704

            
705
BAIL:
706
    csi_object_free (&ctx, &src);
707
    _csi_fini (&ctx);
708

            
709
    return status;
710
}