diff options
author | Henry Stiles <henry.stiles@artifex.com> | 1998-08-08 05:57:31 +0000 |
---|---|---|
committer | Henry Stiles <henry.stiles@artifex.com> | 1998-08-08 05:57:31 +0000 |
commit | b8cb922d73b866149ca3da2288f1edcf959c45c9 (patch) | |
tree | c1cab95009d6c0aca63dfb810a5e0ff5858fc2e1 /gs/src/dstack.h | |
parent | 049831bbe312e8a7ba47fa1e3b583a21052464de (diff) | |
download | ghostpdl-b8cb922d73b866149ca3da2288f1edcf959c45c9.tar.gz |
This commit was generated by cvs2svn to compensate for changes in r274,
which included commits to RCS files with non-trunk default branches.
git-svn-id: http://svn.ghostscript.com/ghostpcl/trunk/ghostpcl@275 06663e23-700e-0410-b217-a244a6096597
Diffstat (limited to 'gs/src/dstack.h')
-rw-r--r-- | gs/src/dstack.h | 457 |
1 files changed, 204 insertions, 253 deletions
diff --git a/gs/src/dstack.h b/gs/src/dstack.h index 51e3340a4..8ca87eb3c 100644 --- a/gs/src/dstack.h +++ b/gs/src/dstack.h @@ -1,33 +1,49 @@ -/* Copyright (C) 1992, 1996, 1997 Aladdin Enterprises. All rights reserved. - - This file is part of Aladdin Ghostscript. - - Aladdin Ghostscript is distributed with NO WARRANTY OF ANY KIND. No author - or distributor accepts any responsibility for the consequences of using it, - or for whether it serves any particular purpose or works at all, unless he - or she says so in writing. Refer to the Aladdin Ghostscript Free Public - License (the "License") for full details. - - Every copy of Aladdin Ghostscript must include a copy of the License, - normally in a plain ASCII text file named PUBLIC. The License grants you - the right to copy, modify and redistribute Aladdin Ghostscript, but only - under certain conditions described in the License. Among other things, the - License requires that the copyright notice and this notice be preserved on - all copies. -*/ +/* Copyright (C) 1992, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin Ghostscript is distributed with NO WARRANTY OF ANY KIND. No author + or distributor accepts any responsibility for the consequences of using it, + or for whether it serves any particular purpose or works at all, unless he + or she says so in writing. Refer to the Aladdin Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin Ghostscript must include a copy of the License, + normally in a plain ASCII text file named PUBLIC. The License grants you + the right to copy, modify and redistribute Aladdin Ghostscript, but only + under certain conditions described in the License. Among other things, the + License requires that the copyright notice and this notice be preserved on + all copies. + */ + +/*Id: dstack.h */ +/* Definitions for the interpreter's dictionary stack */ + +#ifndef dstack_INCLUDED +# define dstack_INCLUDED -/* dstack.h */ -/* Definitions for the dictionary stack */ -#include "istack.h" +#include "idstack.h" -/* Define the dictionary stack and systemdict. */ -extern ref_stack d_stack; -extern ref ref_systemdict; -#define systemdict (&ref_systemdict) +/* Define the (currently static) dictionary stack instance. */ +extern dict_stack_t idict_stack; + +#define d_stack (idict_stack.stack) + +/* Define the interpreter-specific versions of the generic dstack API. */ +#define min_dstack_size (idict_stack.min_size) +#define dstack_userdict_index (idict_stack.userdict_index) +#define dsspace (idict_stack.def_space) +#define dtop_can_store(pvalue) ((int)r_space(pvalue) <= dsspace) +#define dtop_keys (idict_stack.top_keys) +#define dtop_npairs (idict_stack.top_npairs) +#define dtop_values (idict_stack.top_values) +#define dict_set_top() dstack_set_top(&idict_stack); +#define dict_is_permanent_on_dstack(pdict)\ + dstack_dict_is_permanent(&idict_stack, pdict) +#define dicts_gc_cleanup() dstack_gc_cleanup(&idict_stack) +#define systemdict (&idict_stack.system_dict) /* Define the dictionary stack pointers. */ -typedef s_ptr ds_ptr; -typedef const_s_ptr const_ds_ptr; #define dsbot (d_stack.bot) #define dsp (d_stack.p) #define dstop (d_stack.top) @@ -37,243 +53,178 @@ typedef const_s_ptr const_ds_ptr; if ( dstop - dsp < (n) )\ { d_stack.requested = (n); return_error(e_dictstackoverflow); } -/* Check whether a dictionary is one of the permanent ones on the d-stack. */ -bool dict_is_permanent_on_dstack(P1(const ref *)); - -/* - * Switching between Level 1 and Level 2 involves inserting and removing - * globaldict on the dictionary stack. Instead of truly inserting and - * removing entries, we replace globaldict by a copy of systemdict in - * Level 1 mode. min_dstack_size, the minimum number of entries, does not - * change depending on language level; the countdictstack and dictstack - * operators must take this into account. - */ -extern uint min_dstack_size; - /* * The dictionary stack is implemented as a linked list of blocks; * operators that access the entire d-stack must take this into account. * These are: - * countdictstack dictstack + * countdictstack dictstack * In addition, name lookup requires searching the entire stack, not just * the top block, and the underflow check for the dictionary stack * (`end' operator) is not just a check for underflowing the top block. */ -/* - * Cache a value for fast checking of def operations. - * If the top entry on the dictionary stack is a writable dictionary, - * dsspace is the space of the dictionary; if it is a non-writable - * dictionary, dsspace = -1. Then def is legal precisely if - * r_space(pvalue) <= dsspace. Note that in order for this trick to work, - * the result of r_space must be a signed integer; some compilers treat - * enums as unsigned, probably in violation of the ANSI standard. - */ -extern int dsspace; -#define dtop_can_store(pvalue) ((int)r_space(pvalue) <= dsspace) -/* - * Cache values for fast name lookup. If the top entry on the dictionary - * stack is a readable dictionary with packed keys, dtop_keys, dtop_npairs, - * and dtop_values are keys.value.packed, npairs, and values.value.refs - * for that dictionary; otherwise, these variables point to a dummy - * empty dictionary. - */ -extern const ref_packed *dtop_keys; -extern uint dtop_npairs; -extern ref *dtop_values; -/* - * Reset the cached top values. Every routine that alters the - * dictionary stack (including changing the protection or size of the - * top dictionary on the stack) must call this. - */ -void dict_set_top(P0()); - -/* - * Define a special fast entry for name lookup in the interpreter. - * The key is known to be a name; search the entire dict stack. - * Return the pointer to the value slot. - * If the name isn't found, just return 0. - */ -ref *dict_find_name_by_index(P1(uint nidx)); +/* Name lookup */ +#define dict_find_name_by_index(nidx)\ + dstack_find_name_by_index(&idict_stack, nidx) #define dict_find_name(pnref) dict_find_name_by_index(name_index(pnref)) - -/* Define the hashing function for names. */ -/* We don't have to scramble the index, because */ -/* indices are assigned in a scattered order (see name_ref in iname.c). */ -#define dict_name_index_hash(nidx) (nidx) - -/* - * Define an extra-fast macro for name lookup, optimized for - * a single-probe lookup in the top dictionary on the stack. - * Amazingly enough, this seems to hit over 90% of the time - * (aside from operators, of course, which are handled either with - * the special cache pointer or with 'bind'). - */ -#define dict_find_name_by_index_inline(nidx,htemp)\ - (dtop_keys[htemp = dict_hash_mod_inline(dict_name_index_hash(nidx),\ - dtop_npairs) + 1] == pt_tag(pt_literal_name) + (nidx) ?\ - dtop_values + htemp : dict_find_name_by_index(nidx)) -/* - * Define a similar macro that only checks the top dictionary on the stack. - */ -#define if_dict_find_name_by_index_top(nidx,htemp,pvslot)\ - if ( ((dtop_keys[htemp = dict_hash_mod_inline(dict_name_index_hash(nidx),\ - dtop_npairs) + 1] == pt_tag(pt_literal_name) + (nidx)) ?\ - ((pvslot) = dtop_values + (htemp), 1) :\ - 0)\ - ) +#define dict_find_name_by_index_inline(nidx, htemp)\ + dstack_find_name_by_index_inline(&idict_stack, nidx, htemp) +#define if_dict_find_name_by_index_top(nidx, htemp, pvslot)\ + if_dstack_find_name_by_index_top(&idict_stack, nidx, htemp, pvslot) /* -Notes on dictionary lookup performance --------------------------------------- - -We mark heavily used operations with a * below; moderately heavily used -operations with a +. - -The following operations change the dictionary stack: - +begin, +end - readonly (on a dictionary that is on the stack) - noaccess (on a dictionary that is on the stack) -We implement cleardictstack as a series of ends. - -The following operations change the contents of dictionaries: - *def, +put - undef - restore - .setmaxlength -We implement store in PostScript, and copy as a series of puts. Many -other operators also do puts (e.g., ScaleMatrix in makefont, -Implementation in makepattern, ...). Note that put can do an implicit -.setmaxlength (if it has to grow the dictionary). - -The following operations look up keys on the dictionary stack: - *(interpreter name lookup) - load - where - -Current design --------------- - -Each name has a pointer that has one of 3 states: - - This name has no definitions. - - This name has exactly one definition, in systemdict or userdict. - In this case, the pointer points to the value slot. - - This name has some other status. - -We cache some pointers to the top dictionary on the stack if it is a -readable dictionary with packed keys, which allows us to do fast, -single-probe lookups in this dictionary. We also cache a value that -allows us to do a fast check for stores into the top dictionary -(writability + space check). - -Full shallow binding --------------------- - -We implement shallow binding with a pointer in each name that points to -the value slot that holds the name's definition. If the name is -undefined, or if we don't know where the slot is, the binding pointer -points to a ref with a special type t__invalid, which cannot occur -anywhere else. "Clearing" the pointer means setting it to point to this -ref. - -We also maintain a pair of pointers that bracket the value region of the -top dictionary on the stack, for fast checking in def. If the top -dictionary is readonly or noaccess, the pointers designate an empty area. -We call this the "def region" cache. - -We implement the above operations as follows: - begin - push the dictionary on the stack; set the pointers of - all name keys to point to the corresponding value slots. - end - pop the stack; clear the pointers of all name keys. - readonly - if the dictionary is the top one on the stack, - reset the def region cache. - noaccess - clear the pointers of all name keys. (This is overly - conservative, but this is a very rare operation.) - Also reset the def region cache if the dictionary is - the top one on the stack. - def - if the key is a name and its pointer points within the cached - def region, store the value through the pointer; otherwise, - look up the key in the top dictionary, store the value, - and if the key is a name, set its pointer to the value slot. - put - if the key is a name and wasn't in the dictionary before, - clear its pointer. (Conservative, but rare.) - undef - if the key is a name, clear its pointer. (Overly - conservative, but rare.) - restore - if either the old or the new value of a change is a name - (possibly in a packed array), clear its pointer. This is - conservative, but easy to detect, and probably not *too* - conservative. - .setmaxlength - clear all the pointers, like noaccess. - (name lookup) - fetch the value through the pointer and dispatch - on its type; if the type is t__invalid, do a full search - and set the pointer. This avoids a separate check for a - clear pointer in the usual case where the pointer is valid. - load - if the pointer is clear, do a search and set the pointer; - then fetch the value. - where - always do a full search and set the pointer. - (Conservative, but rare.) - -One place where shallow binding will result in major new overhead is the -extra push of systemdict for loading fonts. This probably isn't a problem -in real life. - -Adaptive shallow binding ------------------------- - -We do validity checking for the name value cache using an epoch counter. -For each dictionary D, we keep an on-stack flag F. Each dictionary stack -entry is <D,M,F,E> where D is the actual dictionary, M is a mark vector of -V bits (V is a system constant, probably 64), F is D's former on-stack -flag, and E is the epoch at which the entry was made. For each name K, we -keep a cache <P,E> where P is a pointer to the dictionary value slot that -holds the current value of K, and E is an epoch value; the cache is valid -if K->E >= dsp->E. Here is what happens for each operation: - -****** Still need to handle names defined only in systemdict or userdict? + Notes on dictionary lookup performance + -------------------------------------- + + We mark heavily used operations with a * below; moderately heavily used + operations with a +. + + The following operations change the dictionary stack: + +begin, +end + readonly (on a dictionary that is on the stack) + noaccess (on a dictionary that is on the stack) + We implement cleardictstack as a series of ends. + + The following operations change the contents of dictionaries: + *def, +put + undef + restore + .setmaxlength + We implement store in PostScript, and copy as a series of puts. Many + other operators also do puts (e.g., ScaleMatrix in makefont, + Implementation in makepattern, ...). Note that put can do an implicit + .setmaxlength (if it has to grow the dictionary). + + The following operations look up keys on the dictionary stack: + *(interpreter name lookup) + load + where + + Current design + -------------- + + Each name has a pointer that has one of 3 states: + - This name has no definitions. + - This name has exactly one definition, in systemdict or userdict. + In this case, the pointer points to the value slot. + - This name has some other status. + + We cache some pointers to the top dictionary on the stack if it is a + readable dictionary with packed keys, which allows us to do fast, + single-probe lookups in this dictionary. We also cache a value that + allows us to do a fast check for stores into the top dictionary + (writability + space check). + + Full shallow binding + -------------------- + + We implement shallow binding with a pointer in each name that points to + the value slot that holds the name's definition. If the name is + undefined, or if we don't know where the slot is, the binding pointer + points to a ref with a special type t__invalid, which cannot occur + anywhere else. "Clearing" the pointer means setting it to point to this + ref. + + We also maintain a pair of pointers that bracket the value region of the + top dictionary on the stack, for fast checking in def. If the top + dictionary is readonly or noaccess, the pointers designate an empty area. + We call this the "def region" cache. + + We implement the above operations as follows: + begin - push the dictionary on the stack; set the pointers of + all name keys to point to the corresponding value slots. + end - pop the stack; clear the pointers of all name keys. + readonly - if the dictionary is the top one on the stack, + reset the def region cache. + noaccess - clear the pointers of all name keys. (This is overly + conservative, but this is a very rare operation.) + Also reset the def region cache if the dictionary is + the top one on the stack. + def - if the key is a name and its pointer points within the cached + def region, store the value through the pointer; otherwise, + look up the key in the top dictionary, store the value, + and if the key is a name, set its pointer to the value slot. + put - if the key is a name and wasn't in the dictionary before, + clear its pointer. (Conservative, but rare.) + undef - if the key is a name, clear its pointer. (Overly + conservative, but rare.) + restore - if either the old or the new value of a change is a name + (possibly in a packed array), clear its pointer. This is + conservative, but easy to detect, and probably not *too* + conservative. + .setmaxlength - clear all the pointers, like noaccess. + (name lookup) - fetch the value through the pointer and dispatch + on its type; if the type is t__invalid, do a full search + and set the pointer. This avoids a separate check for a + clear pointer in the usual case where the pointer is valid. + load - if the pointer is clear, do a search and set the pointer; + then fetch the value. + where - always do a full search and set the pointer. + (Conservative, but rare.) + + One place where shallow binding will result in major new overhead is the + extra push of systemdict for loading fonts. This probably isn't a problem + in real life. + + Adaptive shallow binding + ------------------------ + + We do validity checking for the name value cache using an epoch counter. + For each dictionary D, we keep an on-stack flag F. Each dictionary stack + entry is <D,M,F,E> where D is the actual dictionary, M is a mark vector of + V bits (V is a system constant, probably 64), F is D's former on-stack + flag, and E is the epoch at which the entry was made. For each name K, we + keep a cache <P,E> where P is a pointer to the dictionary value slot that + holds the current value of K, and E is an epoch value; the cache is valid + if K->E >= dsp->E. Here is what happens for each operation: + + ****** Still need to handle names defined only in systemdict or userdict? + + To initialize: + Epoch = 0 + To clear the cache entry for K: + *K = <ptr to invalid value, 0> + begin(D): + *++dsp = <D, {0...}, D->F, ++Epoch> + set D->F + value = lookup(K): + if K->E >= dsp->E + value = *K->P + else + do lookup as usual + *K = <ptr to value, Epoch> + set dp->M[i mod V] where dp is the dstack slot of the dictionary + where K was found and i is the index within that dictionary + end: + for each i such that dsp->M[i] is set, + clear the cache entry for dsp->D->keys[i, i+V, ...] + dsp->D->F = dsp->F + --dsp + noaccess(D): + if D->F is set, + clear the cache entries for all name keys of D + readonly(D): + << nothing >> + .setmaxlength(D,N): + same as noaccess + restore: + If either the old or the new value of a change is a name + (possibly in a packed array), clear its cache entry. This is + conservative, but easy to detect, and probably not *too* + conservative. + def(K,V): + if K->P points into dsp->D + *K->P = V + else + put the new value in dsp->D + set *K and dsp->M[i mod V] as for a lookup + put(D,K,V): + if K is already defined in D, do nothing special + otherwise, if D->F isn't set, do nothing special + otherwise, clear K's cache entry + undef(D,K): + if D->F is set, + clear K's cache entry + */ -To initialize: - Epoch = 0 -To clear the cache entry for K: - *K = <ptr to invalid value, 0> -begin(D): - *++dsp = <D, {0...}, D->F, ++Epoch> - set D->F -value = lookup(K): - if K->E >= dsp->E - value = *K->P - else - do lookup as usual - *K = <ptr to value, Epoch> - set dp->M[i mod V] where dp is the dstack slot of the dictionary - where K was found and i is the index within that dictionary -end: - for each i such that dsp->M[i] is set, - clear the cache entry for dsp->D->keys[i, i+V, ...] - dsp->D->F = dsp->F - --dsp -noaccess(D): - if D->F is set, - clear the cache entries for all name keys of D -readonly(D): - << nothing >> -.setmaxlength(D,N): - same as noaccess -restore: - If either the old or the new value of a change is a name - (possibly in a packed array), clear its cache entry. This is - conservative, but easy to detect, and probably not *too* - conservative. -def(K,V): - if K->P points into dsp->D - *K->P = V - else - put the new value in dsp->D - set *K and dsp->M[i mod V] as for a lookup -put(D,K,V): - if K is already defined in D, do nothing special - otherwise, if D->F isn't set, do nothing special - otherwise, clear K's cache entry -undef(D,K): - if D->F is set, - clear K's cache entry -*/ +#endif /* dstack_INCLUDED */ |