Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
/* Copyright (C) 1989, 1995, 1997, 1998, 1999 Aladdin Enterprises.  All rights reserved.
2
 
3
  This software is provided AS-IS with no warranty, either express or
4
  implied.
5
 
6
  This software is distributed under license and may not be copied,
7
  modified or distributed except as expressly authorized under the terms
8
  of the license contained in the file LICENSE in this distribution.
9
 
10
  For more information about licensing, please refer to
11
  http://www.ghostscript.com/licensing/. For information on
12
  commercial licensing, go to http://www.artifex.com/licensing/ or
13
  contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14
  San Rafael, CA  94903, U.S.A., +1(415)492-9861.
15
*/
16
 
17
/* $Id: iname.c,v 1.7 2003/09/03 03:22:59 giles Exp $ */
18
/* Name lookup for Ghostscript interpreter */
19
#include "memory_.h"
20
#include "string_.h"
21
#include "ghost.h"
22
#include "gsstruct.h"
23
#include "gxobj.h"		/* for o_set_unmarked */
24
#include "ierrors.h"
25
#include "inamedef.h"
26
#include "imemory.h"		/* for isave.h */
27
#include "isave.h"
28
#include "store.h"
29
 
30
/* Public values */
31
const uint name_max_string = max_name_string;
32
 
33
/* Define the permutation table for name hashing. */
34
private const byte hash_permutation[256] = {
35
    NAME_HASH_PERMUTATION_DATA
36
};
37
 
38
/* Define the data for the 1-character names. */
39
private const byte nt_1char_names[NT_1CHAR_SIZE] = {
40
    NT_1CHAR_NAMES_DATA
41
};
42
 
43
/* Structure descriptors */
44
gs_private_st_simple(st_name_sub_table, name_sub_table, "name_sub_table");
45
gs_private_st_composite(st_name_string_sub_table, name_string_sub_table_t,
46
			"name_string_sub_table_t",
47
			name_string_sub_enum_ptrs, name_string_sub_reloc_ptrs);
48
gs_private_st_composite(st_name_table, name_table, "name_table",
49
			name_table_enum_ptrs, name_table_reloc_ptrs);
50
 
51
/* Forward references */
52
private int name_alloc_sub(name_table *);
53
private void name_free_sub(name_table *, uint);
54
private void name_scan_sub(name_table *, uint, bool);
55
 
56
/* Debugging printout */
57
#ifdef DEBUG
58
private void
59
name_print(const char *msg, const name_table *nt, uint nidx, const int *pflag)
60
{
61
    const name_string_t *pnstr = names_index_string_inline(nt, nidx);
62
    const name *pname = names_index_ptr_inline(nt, nidx);
63
    const byte *str = pnstr->string_bytes;
64
 
65
    dlprintf1("[n]%s", msg);
66
    if (pflag)
67
	dprintf1("(%d)", *pflag);
68
    dprintf2(" (0x%lx#%u)", (ulong)pname, nidx);
69
    debug_print_string(str, pnstr->string_size);
70
    dprintf2("(0x%lx,%u)\n", (ulong)str, pnstr->string_size);
71
}
72
#  define if_debug_name(msg, nt, nidx, pflag)\
73
     if ( gs_debug_c('n') ) name_print(msg, nt, nidx, pflag)
74
#else
75
#  define if_debug_name(msg, nt, nidx, pflag) DO_NOTHING
76
#endif
77
 
78
/* Initialize a name table */
79
name_table *
80
names_init(ulong count, gs_ref_memory_t *imem)
81
{
82
    gs_memory_t *mem = (gs_memory_t *)imem;
83
    name_table *nt;
84
    int i;
85
 
86
    if (count == 0)
87
	count = max_name_count + 1L;
88
    else if (count - 1 > max_name_count)
89
	return 0;
90
    nt = gs_alloc_struct(mem, name_table, &st_name_table, "name_init(nt)");
91
    if (nt == 0)
92
	return 0;
93
    memset(nt, 0, sizeof(name_table));
94
    nt->max_sub_count =
95
	((count - 1) | nt_sub_index_mask) >> nt_log2_sub_size;
96
    nt->name_string_attrs = imemory_space(imem) | a_readonly;
97
    nt->memory = mem;
98
    /* Initialize the one-character names. */
99
    /* Start by creating the necessary sub-tables. */
100
    for (i = 0; i < NT_1CHAR_FIRST + NT_1CHAR_SIZE; i += nt_sub_size) {
101
	int code = name_alloc_sub(nt);
102
 
103
	if (code < 0) {
104
	    while (nt->sub_next > 0)
105
		name_free_sub(nt, --(nt->sub_next));
106
	    gs_free_object(mem, nt, "name_init(nt)");
107
	    return 0;
108
	}
109
    }
110
    for (i = -1; i < NT_1CHAR_SIZE; i++) {
111
	uint ncnt = NT_1CHAR_FIRST + i;
112
	uint nidx = name_count_to_index(ncnt);
113
	name *pname = names_index_ptr_inline(nt, nidx);
114
	name_string_t *pnstr = names_index_string_inline(nt, nidx);
115
 
116
	if (i < 0)
117
	    pnstr->string_bytes = nt_1char_names,
118
		pnstr->string_size = 0;
119
	else
120
	    pnstr->string_bytes = nt_1char_names + i,
121
		pnstr->string_size = 1;
122
	pnstr->foreign_string = 1;
123
	pnstr->mark = 1;
124
	pname->pvalue = pv_no_defn;
125
    }
126
    nt->perm_count = NT_1CHAR_FIRST + NT_1CHAR_SIZE;
127
    /* Reconstruct the free list. */
128
    nt->free = 0;
129
    names_trace_finish(nt, NULL);
130
    return nt;
131
}
132
 
133
/* Get the allocator for the name table. */
134
gs_memory_t *
135
names_memory(const name_table * nt)
136
{
137
    return nt->memory;
138
}
139
 
140
/* Look up or enter a name in the table. */
141
/* Return 0 or an error code. */
142
/* The return may overlap the characters of the string! */
143
/* See iname.h for the meaning of enterflag. */
144
int
145
names_ref(name_table *nt, const byte *ptr, uint size, ref *pref, int enterflag)
146
{
147
    name *pname;
148
    name_string_t *pnstr;
149
    uint nidx;
150
    uint *phash;
151
 
152
    /* Compute a hash for the string. */
153
    /* Make a special check for 1-character names. */
154
    switch (size) {
155
    case 0:
156
	nidx = name_count_to_index(1);
157
	pname = names_index_ptr_inline(nt, nidx);
158
	goto mkn;
159
    case 1:
160
	if (*ptr < NT_1CHAR_SIZE) {
161
	    uint hash = *ptr + NT_1CHAR_FIRST;
162
 
163
	    nidx = name_count_to_index(hash);
164
	    pname = names_index_ptr_inline(nt, nidx);
165
	    goto mkn;
166
	}
167
	/* falls through */
168
    default: {
169
	uint hash;
170
 
171
	NAME_HASH(hash, hash_permutation, ptr, size);
172
	phash = nt->hash + (hash & (NT_HASH_SIZE - 1));
173
    }
174
    }
175
 
176
    for (nidx = *phash; nidx != 0;
177
	 nidx = name_next_index(nidx, pnstr)
178
	) {
179
	pnstr = names_index_string_inline(nt, nidx);
180
	if (pnstr->string_size == size &&
181
	    !memcmp_inline(ptr, pnstr->string_bytes, size)
182
	    ) {
183
	    pname = name_index_ptr_inline(nt, nidx);
184
	    goto mkn;
185
	}
186
    }
187
    /* Name was not in the table.  Make a new entry. */
188
    if (enterflag < 0)
189
	return_error(e_undefined);
190
    if (size > max_name_string)
191
	return_error(e_limitcheck);
192
    nidx = nt->free;
193
    if (nidx == 0) {
194
	int code = name_alloc_sub(nt);
195
 
196
	if (code < 0)
197
	    return code;
198
	nidx = nt->free;
199
    }
200
    pnstr = names_index_string_inline(nt, nidx);
201
    if (enterflag == 1) {
202
	byte *cptr = (byte *)gs_alloc_string(nt->memory, size,
203
					     "names_ref(string)");
204
 
205
	if (cptr == 0)
206
	    return_error(e_VMerror);
207
	memcpy(cptr, ptr, size);
208
	pnstr->string_bytes = cptr;
209
	pnstr->foreign_string = 0;
210
    } else {
211
	pnstr->string_bytes = ptr;
212
	pnstr->foreign_string = (enterflag == 0 ? 1 : 0);
213
    }
214
    pnstr->string_size = size;
215
    pname = name_index_ptr_inline(nt, nidx);
216
    pname->pvalue = pv_no_defn;
217
    nt->free = name_next_index(nidx, pnstr);
218
    set_name_next_index(nidx, pnstr, *phash);
219
    *phash = nidx;
220
    if_debug_name("new name", nt, nidx, &enterflag);
221
 mkn:
222
    make_name(pref, nidx, pname);
223
    return 0;
224
}
225
 
226
/* Get the string for a name. */
227
void
228
names_string_ref(const name_table * nt, const ref * pnref /* t_name */ ,
229
		 ref * psref /* result, t_string */ )
230
{
231
    const name_string_t *pnstr = names_string_inline(nt, pnref);
232
 
233
    make_const_string(psref,
234
		      (pnstr->foreign_string ? avm_foreign | a_readonly :
235
		       nt->name_string_attrs),
236
		      pnstr->string_size,
237
		      (const byte *)pnstr->string_bytes);
238
}
239
 
240
/* Convert a t_string object to a name. */
241
/* Copy the executable attribute. */
242
int
243
names_from_string(name_table * nt, const ref * psref, ref * pnref)
244
{
245
    int exec = r_has_attr(psref, a_executable);
246
    int code = names_ref(nt, psref->value.bytes, r_size(psref), pnref, 1);
247
 
248
    if (code < 0)
249
	return code;
250
    if (exec)
251
	r_set_attrs(pnref, a_executable);
252
    return code;
253
}
254
 
255
/* Enter a (permanently allocated) C string as a name. */
256
int
257
names_enter_string(name_table * nt, const char *str, ref * pref)
258
{
259
    return names_ref(nt, (const byte *)str, strlen(str), pref, 0);
260
}
261
 
262
/* Invalidate the value cache for a name. */
263
void
264
names_invalidate_value_cache(name_table * nt, const ref * pnref)
265
{
266
    pnref->value.pname->pvalue = pv_other;
267
}
268
 
269
/* Convert between names and indices. */
270
#undef names_index
271
name_index_t
272
names_index(const name_table * nt, const ref * pnref)
273
{
274
    return names_index_inline(nt, pnref);
275
}
276
void
277
names_index_ref(const name_table * nt, name_index_t index, ref * pnref)
278
{
279
    names_index_ref_inline(nt, index, pnref);
280
}
281
name *
282
names_index_ptr(const name_table * nt, name_index_t index)
283
{
284
    return names_index_ptr_inline(nt, index);
285
}
286
 
287
/* Get the index of the next valid name. */
288
/* The argument is 0 or a valid index. */
289
/* Return 0 if there are no more. */
290
name_index_t
291
names_next_valid_index(name_table * nt, name_index_t nidx)
292
{
293
    const name_string_sub_table_t *ssub =
294
	nt->sub[nidx >> nt_log2_sub_size].strings;
295
    const name_string_t *pnstr;
296
 
297
    do {
298
	++nidx;
299
	if ((nidx & nt_sub_index_mask) == 0)
300
	    for (;; nidx += nt_sub_size) {
301
		if ((nidx >> nt_log2_sub_size) >= nt->sub_count)
302
		    return 0;
303
		ssub = nt->sub[nidx >> nt_log2_sub_size].strings;
304
		if (ssub != 0)
305
		    break;
306
	    }
307
	pnstr = &ssub->strings[nidx & nt_sub_index_mask];
308
    }
309
    while (pnstr->string_bytes == 0);
310
    return nidx;
311
}
312
 
313
/* ------ Garbage collection ------ */
314
 
315
/* Unmark all non-permanent names before a garbage collection. */
316
void
317
names_unmark_all(name_table * nt)
318
{
319
    uint si;
320
    name_string_sub_table_t *ssub;
321
 
322
    for (si = 0; si < nt->sub_count; ++si)
323
	if ((ssub = nt->sub[si].strings) != 0) {
324
	    uint i;
325
 
326
	    /* We can make the test much more efficient if we want.... */
327
	    for (i = 0; i < nt_sub_size; ++i)
328
		if (name_index_to_count((si << nt_log2_sub_size) + i) >=
329
		    nt->perm_count)
330
		    ssub->strings[i].mark = 0;
331
	}
332
}
333
 
334
/* Mark a name.  Return true if new mark.  We export this so we can mark */
335
/* character names in the character cache. */
336
bool
337
names_mark_index(name_table * nt, name_index_t nidx)
338
{
339
    name_string_t *pnstr = names_index_string_inline(nt, nidx);
340
 
341
    if (pnstr->mark)
342
	return false;
343
    pnstr->mark = 1;
344
    return true;
345
}
346
 
347
/* Get the object (sub-table) containing a name. */
348
/* The garbage collector needs this so it can relocate pointers to names. */
349
void /*obj_header_t */ *
350
names_ref_sub_table(name_table * nt, const ref * pnref)
351
{
352
    /* When this procedure is called, the pointers from the name table */
353
    /* to the sub-tables may or may not have been relocated already, */
354
    /* so we can't use them.  Instead, we have to work backwards from */
355
    /* the name pointer itself. */
356
    return pnref->value.pname - (r_size(pnref) & nt_sub_index_mask);
357
}
358
void /*obj_header_t */ *
359
names_index_sub_table(name_table * nt, name_index_t index)
360
{
361
    return nt->sub[index >> nt_log2_sub_size].names;
362
}
363
void /*obj_header_t */ *
364
names_index_string_sub_table(name_table * nt, name_index_t index)
365
{
366
    return nt->sub[index >> nt_log2_sub_size].strings;
367
}
368
 
369
/*
370
 * Clean up the name table after the trace/mark phase of a garbage
371
 * collection, by removing names that aren't marked.  gcst == NULL indicates
372
 * we're doing this for initialization or restore rather than for a GC.
373
 */
374
void
375
names_trace_finish(name_table * nt, gc_state_t * gcst)
376
{
377
    uint *phash = &nt->hash[0];
378
    uint i;
379
 
380
    for (i = 0; i < NT_HASH_SIZE; phash++, i++) {
381
	name_index_t prev = 0;
382
	/*
383
	 * The following initialization is only to pacify compilers:
384
	 * pnprev is only referenced if prev has been set in the loop,
385
	 * in which case pnprev is also set.
386
	 */
387
	name_string_t *pnprev = 0;
388
	name_index_t nidx = *phash;
389
 
390
	while (nidx != 0) {
391
	    name_string_t *pnstr = names_index_string_inline(nt, nidx);
392
	    name_index_t next = name_next_index(nidx, pnstr);
393
 
394
	    if (pnstr->mark) {
395
		prev = nidx;
396
		pnprev = pnstr;
397
	    } else {
398
		if_debug_name("GC remove name", nt, nidx, NULL);
399
		/* Zero out the string data for the GC. */
400
		pnstr->string_bytes = 0;
401
		pnstr->string_size = 0;
402
		if (prev == 0)
403
		    *phash = next;
404
		else
405
		    set_name_next_index(prev, pnprev, next);
406
	    }
407
	    nidx = next;
408
	}
409
    }
410
    /* Reconstruct the free list. */
411
    nt->free = 0;
412
    for (i = nt->sub_count; i--;) {
413
	name_sub_table *sub = nt->sub[i].names;
414
	name_string_sub_table_t *ssub = nt->sub[i].strings;
415
 
416
	if (sub != 0) {
417
	    name_scan_sub(nt, i, true);
418
	    if (nt->sub[i].names == 0 && gcst != 0) {
419
		/* Mark the just-freed sub-table as unmarked. */
420
		o_set_unmarked((obj_header_t *)sub - 1);
421
		o_set_unmarked((obj_header_t *)ssub - 1);
422
	    }
423
	}
424
	if (i == 0)
425
	    break;
426
    }
427
    nt->sub_next = 0;
428
}
429
 
430
/* ------ Save/restore ------ */
431
 
432
/* Clean up the name table before a restore. */
433
/* Currently, this is never called, because the name table is allocated */
434
/* in system VM.  However, for a Level 1 system, we might choose to */
435
/* allocate the name table in global VM; in this case, this routine */
436
/* would be called before doing the global part of a top-level restore. */
437
/* Currently we don't make any attempt to optimize this. */
438
void
439
names_restore(name_table * nt, alloc_save_t * save)
440
{
441
    /* We simply mark all names older than the save, */
442
    /* and let names_trace_finish sort everything out. */
443
    uint si;
444
 
445
    for (si = 0; si < nt->sub_count; ++si)
446
	if (nt->sub[si].strings != 0) {
447
	    uint i;
448
 
449
	    for (i = 0; i < nt_sub_size; ++i) {
450
		name_string_t *pnstr =
451
		    names_index_string_inline(nt, (si << nt_log2_sub_size) + i);
452
 
453
		if (pnstr->string_bytes == 0)
454
		    pnstr->mark = 0;
455
		else if (pnstr->foreign_string) {
456
		    /* Avoid storing into a read-only name string. */
457
		    if (!pnstr->mark)
458
			pnstr->mark = 1;
459
		} else
460
		    pnstr->mark =
461
			!alloc_is_since_save(pnstr->string_bytes, save);
462
	    }
463
	}
464
    names_trace_finish(nt, NULL);
465
}
466
 
467
/* ------ Internal procedures ------ */
468
 
469
/* Allocate the next sub-table. */
470
private int
471
name_alloc_sub(name_table * nt)
472
{
473
    gs_memory_t *mem = nt->memory;
474
    uint sub_index = nt->sub_next;
475
    name_sub_table *sub;
476
    name_string_sub_table_t *ssub;
477
 
478
    for (;; ++sub_index) {
479
	if (sub_index > nt->max_sub_count)
480
	    return_error(e_limitcheck);
481
	if (nt->sub[sub_index].names == 0)
482
	    break;
483
    }
484
    nt->sub_next = sub_index + 1;
485
    if (nt->sub_next > nt->sub_count)
486
	nt->sub_count = nt->sub_next;
487
    sub = gs_alloc_struct(mem, name_sub_table, &st_name_sub_table,
488
			  "name_alloc_sub(sub-table)");
489
    ssub = gs_alloc_struct(mem, name_string_sub_table_t,
490
			   &st_name_string_sub_table,
491
			  "name_alloc_sub(string sub-table)");
492
    if (sub == 0 || ssub == 0) {
493
	gs_free_object(mem, ssub, "name_alloc_sub(string sub-table)");
494
	gs_free_object(mem, sub, "name_alloc_sub(sub-table)");
495
	return_error(e_VMerror);
496
    }
497
    memset(sub, 0, sizeof(name_sub_table));
498
    memset(ssub, 0, sizeof(name_string_sub_table_t));
499
    /* The following code is only used if EXTEND_NAMES is non-zero. */
500
#if name_extension_bits > 0
501
    sub->high_index = (sub_index >> (16 - nt_log2_sub_size)) << 16;
502
#endif
503
    nt->sub[sub_index].names = sub;
504
    nt->sub[sub_index].strings = ssub;
505
    /* Add the newly allocated entries to the free list. */
506
    /* Note that the free list will only be properly sorted if */
507
    /* it was empty initially. */
508
    name_scan_sub(nt, sub_index, false);
509
#ifdef DEBUG
510
    if (gs_debug_c('n')) {	/* Print the lengths of the hash chains. */
511
	int i0;
512
 
513
	for (i0 = 0; i0 < NT_HASH_SIZE; i0 += 16) {
514
	    int i;
515
 
516
	    dlprintf1("[n]chain %d:", i0);
517
	    for (i = i0; i < i0 + 16; i++) {
518
		int n = 0;
519
		uint nidx;
520
 
521
		for (nidx = nt->hash[i]; nidx != 0;
522
		     nidx = name_next_index(nidx,
523
					    names_index_string_inline(nt, nidx))
524
		    )
525
		    n++;
526
		dprintf1(" %d", n);
527
	    }
528
	    dputc('\n');
529
	}
530
    }
531
#endif
532
    return 0;
533
}
534
 
535
/* Free a sub-table. */
536
private void
537
name_free_sub(name_table * nt, uint sub_index)
538
{
539
    gs_free_object(nt->memory, nt->sub[sub_index].strings,
540
		   "name_free_sub(string sub-table)");
541
    gs_free_object(nt->memory, nt->sub[sub_index].names,
542
		   "name_free_sub(sub-table)");
543
    nt->sub[sub_index].names = 0;
544
    nt->sub[sub_index].strings = 0;
545
}
546
 
547
/* Scan a sub-table and add unmarked entries to the free list. */
548
/* We add the entries in decreasing count order, so the free list */
549
/* will stay sorted.  If all entries are unmarked and free_empty is true, */
550
/* free the sub-table. */
551
private void
552
name_scan_sub(name_table * nt, uint sub_index, bool free_empty)
553
{
554
    name_string_sub_table_t *ssub = nt->sub[sub_index].strings;
555
    uint free = nt->free;
556
    uint nbase = sub_index << nt_log2_sub_size;
557
    uint ncnt = nbase + (nt_sub_size - 1);
558
    bool keep = !free_empty;
559
 
560
    if (ssub == 0)
561
	return;
562
    if (nbase == 0)
563
	nbase = 1, keep = true;	/* don't free name 0 */
564
    for (;; --ncnt) {
565
	uint nidx = name_count_to_index(ncnt);
566
	name_string_t *pnstr = &ssub->strings[nidx & nt_sub_index_mask];
567
 
568
	if (pnstr->mark)
569
	    keep = true;
570
	else {
571
	    set_name_next_index(nidx, pnstr, free);
572
	    free = nidx;
573
	}
574
	if (ncnt == nbase)
575
	    break;
576
    }
577
    if (keep)
578
	nt->free = free;
579
    else {
580
	/* No marked entries, free the sub-table. */
581
	name_free_sub(nt, sub_index);
582
	if (sub_index == nt->sub_count - 1) {
583
	    /* Back up over a final run of deleted sub-tables. */
584
	    do {
585
		--sub_index;
586
	    } while (nt->sub[sub_index].names == 0);
587
	    nt->sub_count = sub_index + 1;
588
	    if (nt->sub_next > sub_index)
589
		nt->sub_next = sub_index;
590
	} else if (nt->sub_next == sub_index)
591
	    nt->sub_next--;
592
    }
593
}
594
 
595
/* Garbage collector enumeration and relocation procedures. */
596
private 
597
ENUM_PTRS_BEGIN_PROC(name_table_enum_ptrs)
598
{
599
    EV_CONST name_table *const nt = vptr;
600
    uint i = index >> 1;
601
 
602
    if (i >= nt->sub_count)
603
	return 0;
604
    if (index & 1)
605
	ENUM_RETURN(nt->sub[i].strings);
606
    else
607
	ENUM_RETURN(nt->sub[i].names);
608
}
609
ENUM_PTRS_END_PROC
610
private RELOC_PTRS_WITH(name_table_reloc_ptrs, name_table *nt)
611
{
612
    uint sub_count = nt->sub_count;
613
    uint i;
614
 
615
    /* Now we can relocate the sub-table pointers. */
616
    for (i = 0; i < sub_count; i++) {
617
	RELOC_VAR(nt->sub[i].names);
618
	RELOC_VAR(nt->sub[i].strings);
619
    }
620
    /*
621
     * We also need to relocate the cached value pointers.
622
     * We don't do this here, but in a separate scan over the
623
     * permanent dictionaries, at the very end of garbage collection.
624
     */
625
}
626
RELOC_PTRS_END
627
 
628
private ENUM_PTRS_BEGIN_PROC(name_string_sub_enum_ptrs)
629
{
630
    return 0;
631
}
632
ENUM_PTRS_END_PROC
633
private RELOC_PTRS_BEGIN(name_string_sub_reloc_ptrs)
634
{
635
    name_string_t *pnstr = ((name_string_sub_table_t *)vptr)->strings;
636
    uint i;
637
 
638
    for (i = 0; i < nt_sub_size; ++pnstr, ++i) {
639
	if (pnstr->string_bytes != 0 && !pnstr->foreign_string) {
640
	    gs_const_string nstr;
641
 
642
	    nstr.data = pnstr->string_bytes;
643
	    nstr.size = pnstr->string_size;
644
	    RELOC_CONST_STRING_VAR(nstr);
645
	    pnstr->string_bytes = nstr.data;
646
	}
647
    }
648
}
649
RELOC_PTRS_END