From: William F. Jolitz Date: Sun, 19 Apr 1992 19:45:30 +0000 (-0800) Subject: 386BSD 0.1 development X-Git-Tag: 386BSD-0.1~466 X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/commitdiff_plain/ce8f7a44dd406821aad07edd54ca8af6652f3320?hp=df53e7863d7af3c577d736cace108455c8df259a 386BSD 0.1 development Work on file usr/othersrc/public/ghostscript-2.4.1/idict.c Co-Authored-By: Lynne Greer Jolitz Synthesized-from: 386BSD-0.1 --- diff --git a/usr/othersrc/public/ghostscript-2.4.1/idict.c b/usr/othersrc/public/ghostscript-2.4.1/idict.c new file mode 100644 index 0000000000..067eb6d1ac --- /dev/null +++ b/usr/othersrc/public/ghostscript-2.4.1/idict.c @@ -0,0 +1,609 @@ +/* Copyright (C) 1989, 1992 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 "packed.h" +#include "save.h" /* for value cache in names */ +#include "store.h" +#include "iutil.h" /* for obj_eq */ +#include "dict.h" /* interface definition */ +#include "dstack.h" /* ditto */ + +/* + * A dictionary is a structure of three elements (refs): + * + * count - a t_integer whose value says how many entries are + * occupied (N), and whose size says how many elements the client + * thinks the dictionary can hold (C). C may be less than M (see below). + * + * keys - a t_shortarray or t_array of M+1 elements, containing + * the keys. + * + * values - a t_array of M+1 elements, containing the values. + * + * C < M is possible because on 32-bit systems, we round up M so that + * M is a power of 2; this allows us to use masking rather than division + * for computing the initial hash probe. However, C is always the + * maxlength specified by the client, so clients get a consistent story. + */ +#define dict_round_size (!arch_ints_are_short) +#if dict_round_size +# define hash_mod(hash, size) ((hash) & ((size) - 1)) +#else +# define hash_mod(hash, size) ((hash) % (size)) +#endif +/* + * In the packed form, unused or deleted entries contain packed_key_empty + * or packed_key_deleted respectively; in the unpacked form, unused + * or deleted entries contain a literal or executable null respectively. + * The first entry is always marked as a deleted entry, to avoid a + * special wrap-around check. + * + * Note that if the keys slot in the dictionary is new, + * all the key slots are new (more recent than the last save). + * We use this fact to avoid saving stores into packed keys + * for newly created dictionaries. + */ +#define dict_is_packed(dct) r_has_type(&(dct)->keys, t_shortarray) +#define packed_key_empty (pt_tag(pt_integer) + 0) +#define packed_key_deleted (pt_tag(pt_integer) + 1) +#define packed_key_impossible pt_tag(pt_full_ref) /* never matches */ +#define packed_name_key(nidx)\ + ((nidx) <= packed_max_name_index ? pt_tag(pt_literal_name) + (nidx) :\ + packed_key_impossible) +/* + * Using a special mark for deleted entries causes lookup time to degrade + * as entries are inserted and deleted. This is not a problem, because + * entries are almost never deleted. + */ +#define d_maxlength(dct) r_size(&(dct)->count) +#define d_set_maxlength(dct,siz) r_set_size(&(dct)->count,siz) +#define nslots(dct) r_size(&(dct)->values) +#define npairs(dct) (nslots(dct) - 1) +#define d_length(dct) ((uint)((dct)->count.value.intval)) + +/* Define the size of the largest valid dictionary. */ +/* This is limited by the size field of the keys and values refs, */ +/* and by the enumeration interface, which requires the size to */ +/* fit in an int. */ +const uint dict_max_size = max_ushort / 2 - 2; + +/* Define whether dictionaries expand automatically when full. */ +int dict_auto_expand = 1; + +/* 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 whether dictionaries are packed by default. */ +#define default_pack 1 + +/* Forward references */ +private int dict_create_contents(P3(uint size, dict *pdict, int pack)); + +/* Create a dictionary. */ +int +dict_create(uint size, ref *pref) +{ dict *pdict = + (dict *)alloc_refs(sizeof(dict) / sizeof(ref), "dict_create"); + int code; + if ( pdict == 0 ) return e_VMerror; + code = dict_create_contents(size, pdict, default_pack); + if ( code < 0 ) return code; + make_tav_new(pref, t_dictionary, a_all, pdict, pdict); + return 0; +} +private int +dict_create_unpacked_keys(uint asize, dict *pdict) +{ ref *kp = alloc_refs(asize, "dict_create(keys)"); + ref *zp; + register uint i; + if ( kp == 0 ) return e_VMerror; + make_tasv_new(&pdict->keys, t_array, a_all, asize, + refs, kp); + for ( zp = kp, i = asize; i; zp++, i-- ) + make_null_new(zp); + r_set_attrs(kp, a_executable); /* wraparound entry */ + return 0; +} +private int +dict_create_contents(uint size, dict *pdict, int pack) +{ uint csize = (size == 0 ? 1 : size); /* client-specified size */ + uint asize = csize; + ref *vp; + register uint i; + ref *zp; +#if dict_round_size + /* Round up the actual allocated size to the next higher */ + /* power of 2, so we can use & instead of %. */ + while ( asize & (asize - 1) ) asize = (asize | (asize >> 1)) + 1; +#endif + asize++; /* allow room for wraparound entry */ + vp = alloc_refs(asize, "dict_create(values)"); + if ( vp == 0 ) return e_VMerror; + make_tasv_new(&pdict->values, t_array, a_all, asize, refs, vp); + for ( zp = vp, i = asize; i; zp++, i-- ) + make_null_new(zp); + if ( pack ) + { uint ksize = (asize + packed_per_ref - 1) / packed_per_ref; + ref_packed *pkp = (ref_packed *)alloc_refs(ksize, "dict_create(packed keys)"); + ref_packed *pzp; + make_tasv_new(&pdict->keys, t_shortarray, a_all, asize, + packed, pkp); + for ( pzp = pkp, i = 0; i < asize || i % packed_per_ref; pzp++, i++ ) + *pzp = packed_key_empty; + *pkp = packed_key_deleted; /* wraparound entry */ + } + else /* not packed */ + { int code = dict_create_unpacked_keys(asize, pdict); + if ( code < 0 ) return code; + } + make_tv_new(&pdict->count, t_integer, intval, 0); + d_set_maxlength(pdict, csize); + return 0; +} + +/* + * Define a macro for searching a packed dictionary. Free variables: + * ref_packed kpack - holds the packed key. + * uint hash - holds the hash of the name. + * dict *pdict - points to the dictionary. + * uint size - holds npairs(pdict). + * int wrap - counts wraparounds, initialized to 0. + * Note that the macro is *not* enclosed in {}, so that we can access + * the values of kbot and kp after leaving the loop. + */ +#define packed_search(del,pre,post)\ + int wrap = 0;\ + ref_packed *kbot = pdict->keys.value.packed;\ + register ref_packed *kp;\ + for ( kp = kbot + hash_mod(hash, size) + 2; ; )\ + { if ( *--kp == kpack )\ + { pre (pdict->values.value.refs + (kp - kbot));\ + post;\ + }\ + else if ( !packed_ref_is_name(kp) )\ + { /* Empty, deleted, or wraparound. Figure out which. */\ + if ( *kp == packed_key_empty ) break;\ + if ( kp == kbot ) /* wrap */\ + { if ( wrap++ ) break; /* 2 wraps */\ + kp += size + 1;\ + }\ + else { del; }\ + }\ + } + +/* + * 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(const ref *pdbot, const ref *pdtop, const ref *pkey, + ref **ppvalue /* result is stored here */) +{ const ref *pdref = pdtop; + name *kpname; + uint nidx; + ref_packed kpack; + uint hash; + int ktype; + int full = 1; /* gets set to 0 or e_dictfull */ + /* 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: nidx = kpname->index; + hash = dict_name_index_hash(nidx); + kpack = packed_name_key(nidx); + 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 * 30503; + kpack = packed_key_impossible; + ktype = -1; + break; + default: + hash = r_btype(pkey) * 99; /* yech */ + kpack = packed_key_impossible; + ktype = -1; + } + do + { dict *pdict = pdref->value.pdict; + uint size = npairs(pdict); + int wrap = 0; + register int etype; + /* Search the dictionary */ + if ( dict_is_packed(pdict) ) + { ref_packed *pslot = 0; + packed_search(if ( pslot == 0 ) pslot = kp, + *ppvalue =, return 1); + if ( full > 0 ) /* first dictionary */ + { if ( pslot == 0 ) + { if ( wrap == 2 ) + full = e_dictfull; + else + *ppvalue = pdict->values.value.refs + + (kp - kbot), + full = 0; + } + else + *ppvalue = pdict->values.value.refs + + (pslot - kbot), + full = 0; + } + } + else + { ref *kbot = pdict->keys.value.refs; + register ref *kp; + ref *pslot = 0; + for ( kp = kbot + hash_mod(hash, size) + 2; ; ) + { --kp; + if ( (etype = r_type(kp)) == ktype ) + { /* Fast comparison if both keys are names */ + if ( kp->value.pname == kpname ) + { *ppvalue = pdict->values.value.refs + (kp - kbot); + return 1; + } + } + else if ( etype == t_null ) + { /* Empty, deleted, or wraparound. */ + /* Figure out which. */ + if ( kp == kbot ) /* wrap */ + { if ( wrap++ ) /* wrapped twice */ + { if ( full > 0 ) + { if ( pslot != 0 ) + break; + full = e_dictfull; + } + goto next_dict; + } + kp += size + 1; + } + else if ( r_has_attr(kp, a_executable) ) + { /* Deleted entry, save the slot. */ + if ( pslot == 0 ) pslot = kp; + } + else /* key not found */ + break; + } + else + { if ( obj_eq(kp, pkey) ) + { *ppvalue = pdict->values.value.refs + (kp - kbot); + return 1; + } + } + } + if ( full > 0 ) + { *ppvalue = pdict->values.value.refs + + ((pslot != 0 ? pslot : kp) - kbot); + full = 0; + } + } +next_dict: ; + } + while ( --pdref >= pdbot ); + return full; +} + +/* + * Look up a name on the dictionary stack. + * Return the pointer to the value if found, 0 if not. + * This is just an optimization of dict_lookup with a different interface. + */ +ref * +dict_find_name(ref *pname) +{ ds_ptr pdref = dsp; + name *kpname = pname->value.pname; + uint nidx = kpname->index; + uint hash = dict_name_index_hash(nidx); + ref_packed kpack = packed_name_key(nidx); + int wrap = 0; + do + { dict *pdict = pdref->value.pdict; + uint size = npairs(pdict); + if ( dict_is_packed(pdict) ) + { packed_search(0, return, 0); + } + else + { ref *kbot = pdict->keys.value.refs; + register ref *kp; + /* Search the dictionary */ + for ( kp = kbot + hash_mod(hash, size) + 2; ; ) + { --kp; + if ( r_has_type(kp, t_name) ) + { if ( kp->value.pname == kpname ) + return pdict->values.value.refs + + (kp - kbot); + } + else if ( r_has_type(kp, t_null) ) + { /* Empty, deleted, or wraparound. */ + /* Figure out which. */ + if ( !r_has_attr(kp, a_executable) ) + break; + if ( kp == kbot ) /* wrap */ + { if ( wrap++ ) + break; /* 2 wraps */ + kp += size + 1; + } + } + } + } + } + while ( --pdref >= dstack ); + return (ref *)0; +} + +/* + * 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 */, const ref *pkey, const ref *pvalue) +{ ref *pvslot; +top: if ( dict_find(pdref, pkey, &pvslot) <= 0 ) /* not found */ + { /* Check for overflow */ + dict *pdict = pdref->value.pdict; + ref kname; + uint index = pvslot - pdict->values.value.refs; + if ( d_length(pdict) == npairs(pdict) ) + { int code; + ulong new_size; + if ( !dict_auto_expand ) + return e_dictfull; + new_size = (ulong)npairs(pdict) * 3 / 2 + 2; + if ( new_size > dict_max_size ) + { if ( npairs(pdict) >= dict_max_size ) + return e_dictfull; + new_size = dict_max_size; + } + code = dict_resize(pdref, (uint)new_size); + if ( code < 0 ) return code; + goto top; /* keep things simple */ + } + /* 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; + } + if ( dict_is_packed(pdict) ) + { ref_packed *kp; + if ( !r_has_type(pkey, t_name) || + name_index(pkey) > packed_max_name_index + ) + { /* Change to unpacked representation. */ + /* We can't just use dict_resize, */ + /* because the values slots mustn't move. */ + uint count = nslots(pdict); + ref_packed *old_keys = pdict->keys.value.packed; + int code; + ref *nkp; + if ( alloc_save_new_mask ) + alloc_save_change(&pdict->keys, "dict_unpack(keys)"); + code = dict_create_unpacked_keys(count, pdict); + if ( code < 0 ) return code; + for ( kp = old_keys, nkp = pdict->keys.value.refs; count--; kp++, nkp++ ) + if ( packed_ref_is_name(kp) ) + packed_get(kp, nkp); + alloc_free_refs((ref *)old_keys, + (count + packed_per_ref - 1) / packed_per_ref, + "dict_unpack(old keys)"); + return dict_put(pdref, pkey, pvalue); + } + kp = pdict->keys.value.packed + index; + if ( alloc_save_new_mask && + !r_has_attr(&pdict->keys, l_new) + ) + { /* See initial comment for why it is safe */ + /* not to save the change if the keys */ + /* array itself is new. */ + alloc_save_change(pdict->keys.value.refs + (index / packed_per_ref), "dict_put(key)"); + } + *kp = pt_tag(pt_literal_name) + name_index(pkey); + } + else + { ref *kp = pdict->keys.value.refs + index; + if_debug2('d', "[d]%lx fill key %lx\n", + (ulong)pdict, (ulong)kp); + ref_assign_old(kp, pkey, "dict_put(key)"); /* set key of pair */ + } + ref_save(&pdict->count, "dict_put(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 ( 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; + } + } + if_debug6('d', "[d]in %lx put %lx: %lx %lx -> %lx %lx\n", + (ulong)pdref->value.pdict, (ulong)pvslot, + ((ulong *)pvslot)[0], ((ulong *)pvslot)[1], + ((ulong *)pvalue)[0], ((ulong *)pvalue)[1]); + ref_assign_old(pvslot, pvalue, "dict_put(value)"); + return 0; +} + +/* Remove an element from a dictionary. */ +int +dict_undef(ref *pdref, const ref *pkey) +{ ref *pvslot; + dict *pdict; + uint index; + if ( dict_find(pdref, pkey, &pvslot) <= 0 ) + return e_undefined; + /* Remove the entry from the dictionary. */ + pdict = pdref->value.pdict; + index = pvslot - pdict->values.value.refs; + if ( dict_is_packed(pdict) ) + { ref_packed *pkp = pdict->keys.value.packed + index; + /* Since packed arrays don't have room for a saved bit, */ + /* always save the entire ref containing this key. */ + /* This wastes a little space, but undef is rare. */ + /* See the initial comment for why it is safe not to save */ + /* the change if the keys array itself is new. */ + if ( alloc_save_new_mask && !r_has_attr(&pdict->keys, l_new) ) + alloc_save_change(pdict->keys.value.refs + (index / packed_per_ref), "dict_undef(key)"); + /* 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 ( pkp[-1] == packed_key_empty ) + *pkp = packed_key_empty; + else + *pkp = packed_key_deleted; + } + else /* not packed */ + { ref *kp = pdict->keys.value.refs + index; + make_null_old(kp, "dict_undef(key)"); + /* 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(kp - 1, t_null) || /* full entry */ + r_has_attr(kp - 1, a_executable) /* deleted or wraparound */ + ) + r_set_attrs(kp, 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; + } + } + make_null_old(pvslot, "dict_undef(value)"); + return 0; +} + +/* Return the number of elements in a dictionary. */ +uint +dict_length(const ref *pdref /* t_dictionary */) +{ return d_length(pdref->value.pdict); +} + +/* Return the capacity of a dictionary. */ +uint +dict_maxlength(const ref *pdref /* t_dictionary */) +{ return d_maxlength(pdref->value.pdict); +} + +/* Copy one dictionary into another. */ +int +dict_copy(const ref *pdrfrom /* t_dictionary */, ref *pdrto /* t_dictionary */) +{ int index = dict_first(pdrfrom); + ref elt[2]; + int code; + while ( (index = dict_next(pdrfrom, index, elt)) >= 0 ) + if ( (code = dict_put(pdrto, &elt[0], &elt[1])) < 0 ) + return code; + return 0; +} + +/* Resize a dictionary. */ +int +dict_resize(ref *pdrfrom, uint new_size) +{ dict *pdict = pdrfrom->value.pdict; + uint count = nslots(pdict); + dict dnew; + ref drto; + int code; + if ( new_size < d_length(pdict) ) + { if ( !dict_auto_expand ) + return e_dictfull; + new_size = d_length(pdict); + } + if ( (code = dict_create_contents(new_size, &dnew, dict_is_packed(pdict))) < 0 ) + return code; + make_tav_new(&drto, t_dictionary, a_all, pdict, &dnew); + dict_copy(pdrfrom, &drto); /* can't fail */ + /* Free the old dictionary */ + alloc_free_refs(pdict->values.value.refs, count, + "dict_resize(old values)"); + alloc_free_refs(pdict->keys.value.refs, + (dict_is_packed(pdict) ? + (count + packed_per_ref - 1) / packed_per_ref : + count), + "dict_resize(old keys)"); + ref_assign_old(&pdict->keys, &dnew.keys, "dict_resize(keys)"); + ref_assign_old(&pdict->values, &dnew.values, "dict_resize(values)"); + ref_save(&pdict->count, "dict_resize(size)"); + d_set_maxlength(pdict, new_size); + return 0; +} + +/* Prepare to enumerate a dictionary. */ +int +dict_first(const ref *pdref) +{ return (int)nslots(pdref->value.pdict); +} + +/* Enumerate the next element of a dictionary. */ +int +dict_next(const ref *pdref, int index, ref *eltp /* ref eltp[2] */) +{ dict *pdict = pdref->value.pdict; + ref *vp = pdict->values.value.refs + index; + while ( vp--, --index >= 0 ) + { array_get(&pdict->keys, (long)index, eltp); + /* Make sure this is a valid entry. */ + if ( r_has_type(eltp, t_name) || + (!dict_is_packed(pdict) && !r_has_type(eltp, t_null)) + ) + { eltp[1] = *vp; + return index; + } + } + return -1; /* no more elements */ +}