blob: d6090d25b4e8580d95867b28864c24215b973cd7 [file] [log] [blame]
/* Copyright (C) 1989, 1990 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 "store.h"
#include "dict.h" /* interface definition */
/* Import the dictionary stack */
extern ref dstack[];
/* Import systemdict and userdict for the name value cache. */
#define systemdict (dstack[0])
#define userdict (dstack[1])
/*
* 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).
* 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) (((dct)->contents.size >> 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(1, sizeof(dict), "dict_create");
pair *pp;
if ( pdict == 0 ) return e_VMerror;
pp = (pair *)alloc(asize, sizeof(pair), "dict_create(pairs)");
if ( pp == 0 )
{ alloc_free((char *)pdict, 1, sizeof(dict), "dict_create");
return e_VMerror;
}
n_store_int(&pdict->count, 0);
n_store_tasv(&pdict->contents, t_array, a_all, asize * 2,
refs, (ref *)pp);
n_store_tav(pref, t_dictionary, a_all, pdict, pdict);
pp = pairs(pdict);
while ( asize-- )
pp->key.type_attrs = pp->value.type_attrs = null_type_attrs,
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, */
/* or e_dictfull if the dictionary is full and the key is missing. */
/* The caller is responsible for ensuring key is not a null. */
/* 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 */
/* Compute hash. The only types we bother with are strings */
/* and names. */
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, pkey->size, &nref, 1);
if ( code < 0 ) return code;
kpname = nref.value.pname;
} goto nh;
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 *));
printf("[d]");
debug_print_ref(pdref);
printf(":");
debug_print_ref(pkey);
printf("->");
}
#endif
#ifdef DEBUG
# define print_found()\
if ( gs_debug['d'] )\
{ extern void debug_print_ref(P1(ref *));\
debug_print_ref(&pp->value);\
printf("; ");\
}
#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 )
{ /* We might have hit the dummy entry at */
/* the beginning, in which case we should */
/* wrap around to the end. */
if ( pp == ppbot ) /* wrap */
{ if ( wrap++ ) /* wrapped twice */
{ if ( full > 0 )
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 = &pp->value;
#ifdef DEBUG
if ( gs_debug['d'] )
printf("0(%lx); ", (ulong)&pp->value);
#endif
full = 0;
}
next_dict: ;
}
while ( --pdref >= pdbot );
return full;
}
/* Enter a key-value pair in a dictionary. */
/* Return 0 or e_dictfull. */
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;
if ( pdict->count.value.intval == npairs(pdict) )
return e_dictfull;
pdict->count.value.intval++;
pvslot[-1] = *pkey; /* i.e. key of pair */
/* If the key is a name, update its 1-element cache. */
if ( r_type(pkey) == t_name )
{ name *pname = pkey->value.pname;
if ( pname->pvalue == pv_no_defn &&
(pdict == systemdict.value.pdict ||
pdict == userdict.value.pdict) )
{ /* Initialize the cache */
pname->pvalue = pvslot;
}
else /* The cache is worthless */
pname->pvalue = pv_other;
}
}
store_i(pvslot, pvalue);
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;
while ( count-- )
{ if ( r_type(&pp->key) != t_null )
if ( (code = dict_put(pdrto, &pp->key, &pp->value)) != 0 )
return code;
pp++;
}
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_type(&pp->key) != t_null )
{ eltp[0] = pp->key;
eltp[1] = pp->value;
return index;
}
}
return -1; /* no more elements */
}