home *** CD-ROM | disk | FTP | other *** search
- /* Copyright (C) 1989, 1990, 1991 Aladdin Enterprises. All rights reserved.
- Distributed by Free Software Foundation, Inc.
-
- This file is part of Ghostscript.
-
- Ghostscript is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
- to anyone for the consequences of using it or for whether it serves any
- particular purpose or works at all, unless he says so in writing. Refer
- to the Ghostscript General Public License for full details.
-
- Everyone is granted permission to copy, modify and redistribute
- Ghostscript, but only under the conditions described in the Ghostscript
- General Public License. A copy of this license is supposed to have been
- given to you along with Ghostscript so you can know your rights and
- responsibilities. It should be in a file named COPYING. Among other
- things, the copyright notice and this notice must be preserved on all
- copies. */
-
- /* idict.c */
- /* Dictionaries for Ghostscript */
- #include "ghost.h"
- #include "alloc.h"
- #include "errors.h"
- #include "name.h"
- #include "save.h" /* for value cache in names */
- #include "store.h"
- #include "dict.h" /* interface definition */
-
- /* Imported procedures */
- extern int obj_eq(P2(ref *, ref *));
-
- /*
- * A dictionary is a structure of two elements (refs).
- * The first element is a t_integer whose value says how many
- * entries are occupied (N). The second element is a t_array
- * of 2N+2 elements, containing alternating keys and values.
- * Unused entries have null as the key. The first entry also
- * has null as the key (to avoid a special wrap-around check).
- * For now, deleted entries have an executable null as the key:
- * this degrades lookup time as entries are inserted and deleted,
- * and will probably have to be changed someday.
- * The access attributes for the dictionary are stored in
- * the contents ref.
- */
- struct dict_s {
- ref count; /* t_integer */
- ref contents; /* t_array */
- };
- struct pair_s {
- ref key;
- ref value;
- };
- typedef struct pair_s pair;
- #define pairs(dct) (pair *)((dct)->contents.value.refs)
- #define npairs(dct) ((r_size(&(dct)->contents) >> 1) - 1)
-
- /* Define the size of the largest valid dictionary. */
- /* This is limited by the size field of the contents ref. */
- uint dict_max_size = max_ushort / 2 - 1;
-
- /* Create a dictionary. */
- int
- dict_create(uint size, ref *pref)
- { uint asize = (size == 0 ? 1 : size) + 1;
- dict *pdict =
- (dict *)alloc_refs(sizeof(dict) / sizeof(ref), "dict_create");
- pair *pp;
- if ( pdict == 0 ) return e_VMerror;
- pp = (pair *)alloc_refs(asize * 2, "dict_create(pairs)");
- if ( pp == 0 )
- { alloc_free((char *)pdict, 1, sizeof(dict), "dict_create");
- return e_VMerror;
- }
- make_tv_new(&pdict->count, t_integer, intval, 0);
- make_tasv_new(&pdict->contents, t_array, a_all, asize * 2,
- refs, (ref *)pp);
- make_tav_new(pref, t_dictionary, a_all, pdict, pdict);
- pp = pairs(pdict);
- while ( asize-- )
- make_null_new(&pp->key),
- make_null_new(&pp->value),
- pp++;
- return 0;
- }
-
- /*
- * Return a pointer to a ref that holds the access attributes
- * for a dictionary.
- */
- ref *
- dict_access_ref(ref *pdref)
- { return &pdref->value.pdict->contents;
- }
-
- /*
- * Look up in a stack of dictionaries. Store a pointer to the value slot
- * where found, or to the (value) slot for inserting.
- * Return 1 if found, 0 if not and there is room for a new entry in
- * the top dictionary on the stack, or e_dictfull if the top dictionary
- * is full and the key is missing.
- * Note that pdbot <= pdtop, and the search starts at pdtop.
- */
- int
- dict_lookup(ref *pdbot, ref *pdtop, ref *pkey,
- ref **ppvalue /* result is stored here */)
- { ref *pdref = pdtop;
- uint hash;
- int ktype;
- name *kpname;
- int full = 1; /* gets set to 0 or e_dictfull */
- pair *pslot = 0;
- /* Compute hash. The only types we bother with are strings, */
- /* names, and (unlikely, but worth checking for) integers. */
- switch ( r_type(pkey) )
- {
- case t_name:
- kpname = pkey->value.pname;
- nh: hash = kpname->index * 40503;
- ktype = t_name;
- break;
- case t_string: /* convert to a name first */
- { ref nref;
- int code = name_ref(pkey->value.bytes,
- r_size(pkey), &nref, 1);
- if ( code < 0 ) return code;
- kpname = nref.value.pname;
- } goto nh;
- case t_integer:
- hash = (uint)pkey->value.intval * 40503;
- ktype = -1;
- break;
- default:
- hash = r_btype(pkey) * 99; /* yech */
- ktype = -1;
- }
- do
- { dict *pdict = pdref->value.pdict;
- uint size = npairs(pdict);
- pair *ppbot = pairs(pdict);
- register pair *pp; /* probe pointer */
- int wrap = 0;
- register int etype;
- /* Search the dictionary */
- #ifdef DEBUG
- if ( gs_debug['d'] )
- { extern void debug_print_ref(P1(ref *));
- dprintf("[d]");
- debug_print_ref(pdref);
- dprintf(":");
- debug_print_ref(pkey);
- dprintf("->");
- }
- #endif
- #ifdef DEBUG
- # define print_found()\
- if ( gs_debug['d'] )\
- { extern void debug_print_ref(P1(ref *));\
- debug_print_ref(&pp->value);\
- dprintf("; ");\
- }
- #else
- # define print_found()
- #endif
- for ( pp = ppbot + (hash % size) + 2; ; )
- { if ( (etype = r_type(&(--pp)->key)) == ktype )
- { /* Fast comparison if both keys are names */
- if ( pp->key.value.pname == kpname )
- { *ppvalue = &pp->value;
- print_found();
- return 1;
- }
- }
- else if ( etype == t_null )
- { if ( r_has_attrs(&pp->key, a_executable) )
- { /* Deleted entry, save the slot. */
- if ( pslot == 0 ) pslot = pp;
- }
- /* We might have hit the dummy entry at */
- /* the beginning, in which case we should */
- /* wrap around to the end. */
- else if ( pp == ppbot ) /* wrap */
- { if ( wrap++ ) /* wrapped twice */
- { if ( full > 0 )
- { if ( pslot != 0 )
- break;
- full = e_dictfull;
- }
- goto next_dict;
- }
- pp += size + 1;
- }
- else /* key not found */
- break;
- }
- else
- { if ( obj_eq(&pp->key, pkey) )
- { *ppvalue = &pp->value;
- print_found();
- return 1;
- }
- }
- }
- if ( full > 0 )
- { *ppvalue = (pslot != 0 ? &pslot->value : &pp->value);
- #ifdef DEBUG
- if ( gs_debug['d'] )
- dprintf1("0(%lx); ", (ulong)*ppvalue);
- #endif
- full = 0;
- }
- next_dict: ;
- }
- while ( --pdref >= pdbot );
- return full;
- }
-
- /*
- * Enter a key-value pair in a dictionary.
- * The caller is responsible for ensuring key is not a null.
- * Return 0, e_dictfull, or e_VMerror if the key was a string
- * and a VMerror occurred when converting it to a name.
- */
- int
- dict_put(ref *pdref /* t_dictionary */, ref *pkey, ref *pvalue)
- { ref *pvslot;
- if ( dict_find(pdref, pkey, &pvslot) <= 0 ) /* not found */
- { /* Check for overflow */
- dict *pdict = pdref->value.pdict;
- ref kname;
- if ( pdict->count.value.intval == npairs(pdict) )
- return e_dictfull;
- /* If the key is a string, convert it to a name. */
- if ( r_has_type(pkey, t_string) )
- { int code = name_from_string(pkey, &kname);
- if ( code < 0 ) return code;
- pkey = &kname;
- }
- ref_save(&pdict->count, "dict_put(count)");
- pdict->count.value.intval++;
- ref_assign_old(pvslot - 1, pkey, "dict_put(key)"); /* set key of pair */
- /* If the key is a name, update its 1-element cache. */
- if ( r_has_type(pkey, t_name) )
- { name *pname = pkey->value.pname;
- if ( pname->pvalue == pv_no_defn &&
- (pdict == systemdict.value.pdict ||
- pdict == userdict.value.pdict) &&
- /* Only set the cache if we aren't inside */
- /* a save. This way, we never have to */
- /* undo setting the cache. */
- alloc_save_level() == 0
- )
- { /* Set the cache */
- pname->pvalue = pvslot;
- }
- else /* The cache is worthless */
- pname->pvalue = pv_other;
- }
- }
- ref_assign_old(pvslot, pvalue, "dict_put(value)");
- return 0;
- }
-
- /* Remove an element from a dictionary. */
- int
- dict_undef(ref *pdref, ref *pkey)
- { ref *pvslot;
- dict *pdict;
- if ( dict_find(pdref, pkey, &pvslot) <= 0 )
- return e_undefined;
- /* Remove the entry from the dictionary. */
- pdict = pdref->value.pdict;
- pvslot--; /* point to key, not value */
- make_null_old(pvslot, "dict_undef(value)");
- /* Accumulating deleted entries slows down lookup. */
- /* Detect the easy case where we can use an empty entry */
- /* rather than a deleted one, namely, when the next entry */
- /* in the probe order is empty. */
- if ( r_has_type(pvslot - 2, t_null) &&
- !r_has_attrs(pvslot - 2, a_executable) &&
- pvslot - 2 != pdict->contents.value.refs /* not wraparound */
- )
- r_set_attrs(pvslot, a_executable); /* mark as deleted */
- ref_save(&pdict->count, "dict_undef(count)");
- pdict->count.value.intval--;
- /* If the key is a name, update its 1-element cache. */
- if ( r_has_type(pkey, t_name) )
- { name *pname = pkey->value.pname;
- if ( pv_valid(pname->pvalue) &&
- (pdict == systemdict.value.pdict ||
- pdict == userdict.value.pdict) )
- { /* Clear the cache */
- pname->pvalue = pv_no_defn;
- }
- }
- return 0;
- }
-
- /* Return the number of elements in a dictionary. */
- uint
- dict_length(ref *pdref /* t_dictionary */)
- { return (uint)(pdref->value.pdict->count.value.intval);
- }
-
- /* Return the capacity of a dictionary. */
- uint
- dict_maxlength(ref *pdref /* t_dictionary */)
- { return npairs(pdref->value.pdict);
- }
-
- /* Copy one dictionary into another. */
- int
- dict_copy(ref *pdrfrom /* t_dictionary */, ref *pdrto /* t_dictionary */)
- { dict *pdict = pdrfrom->value.pdict;
- int count = npairs(pdict) + 1; /* +1 for dummy first entry */
- pair *pp = pairs(pdict);
- int code;
- for ( ; count--; pp++ )
- if ( !r_has_type(&pp->key, t_null) )
- if ( (code = dict_put(pdrto, &pp->key, &pp->value)) != 0 )
- return code;
- return 0;
- }
-
- /* Resize a dictionary. */
- int
- dict_resize(ref *pdrfrom, uint new_size)
- { dict *pdict = pdrfrom->value.pdict;
- ref drto;
- int code;
- if ( (code = dict_create(new_size, &drto)) < 0 ) return code;
- dict_copy(pdrfrom, &drto); /* can't fail */
- /* Free the old dictionary */
- alloc_free((char *)pdict->contents.value.refs,
- dict_maxlength(pdrfrom), sizeof(pair), "dict_resize(old)");
- *pdict = *drto.value.pdict;
- /* Free the new dictionary header */
- alloc_free((char *)drto.value.pdict,
- 1, sizeof(dict), "dict_resize(new)");
- return 0;
- }
-
- /* Prepare to enumerate a dictionary. */
- int
- dict_first(ref *pdref)
- { return (int)(npairs(pdref->value.pdict) + 1); /* +1 for dummy */
- }
-
- /* Enumerate the next element of a dictionary. */
- int
- dict_next(ref *pdref, int index, ref *eltp /* ref eltp[2] */)
- { pair *pp = pairs(pdref->value.pdict) + index;
- while ( pp--, --index >= 0 )
- { if ( !r_has_type(&pp->key, t_null) )
- { eltp[0] = pp->key;
- eltp[1] = pp->value;
- return index;
- }
- }
- return -1; /* no more elements */
- }
-