changeset 121:86d71ae0c85a v7.0042

updated for version 7.0042
author vimboss
date Wed, 19 Jan 2005 22:24:34 +0000
parents 34423a71d203
children 56eb9755174b
files runtime/doc/todo.txt src/Make_djg.mak src/Make_manx.mak src/Make_morph.mak src/eval.c src/testdir/Make_vms.mms
diffstat 6 files changed, 414 insertions(+), 329 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/todo.txt
+++ b/runtime/doc/todo.txt
@@ -1,4 +1,4 @@
-*todo.txt*      For Vim version 7.0aa.  Last change: 2005 Jan 17
+*todo.txt*      For Vim version 7.0aa.  Last change: 2005 Jan 19
 
 
 		  VIM REFERENCE MANUAL	  by Bram Moolenaar
@@ -30,6 +30,12 @@ be worked on, but only if you sponsor Vi
 							*known-bugs*
 -------------------- Known bugs and current work -----------------------
 
+Hashtable implementation:
+- Use hashtable for variables and syntax keywords.
+
+":grep": display progress (filename, every second or so)
+Can ":grep" made faster somehow?  Do profiling.
+
 Sanity check of eval.c:
 - Go through the code for magic braces.
 
@@ -104,6 +110,7 @@ PLANNED FOR VERSION 7.0:
 -   REFACTORING: The main() function is very long.  Move parts to separate
     functions, especially loops.  Ideas from Walter Briscoe (2003 Apr 3, 2004
     Feb 9).
+    Move the printing stuff to hardcopy.c.
 -   Improve the interface between the generic GUI code and the system-specific
     code.  Generic code handles text window with scrollbars, system-specific
     code menu, toolbar, etc.
@@ -1576,7 +1583,6 @@ 7   Add "n" flag to search() function, j
 7   Add argument to winwidth() to subtract the space taken by 'foldcolumn',
     signs and/or 'number'.
 8   Add functions:
-	search()		Add optional offset argument.
 	realname()		Get user name (first, last, full)
 				user_fullname() patch by Nikolai Weibull, Nov
 				3 2002
@@ -2417,6 +2423,8 @@ 8   When formatting C comments that are 
     like it's done when there is no code.  And there is no automatic wrapping.
     Recognize comments that come after code.  Should insert the comment leader
     when it's "#" or "//".
+    Other way around: when a C command starts with "* 4" the "*" is repeated
+    while it should not.  Use syntax HL comment recognition?
 7   When using "comments=fg:--", Vim inserts three spaces for a new line.
     When hitting a TAB, these spaces could be removed.
 7   The 'n'esting flag doesn't do the indenting of the last (rightmost) item.
@@ -3236,11 +3244,11 @@ 7   Should ":cd" for MS-DOS go to $HOME,
 8   findmatchlimit() should be able to skip comments.  Solves problem of
     matching the '{' in /* if (foo) { */ (Fiveash)
 -   Add more redirecting of Ex commands:
-	:redir @> register  (append)
-	:redir #  bufname
-	:redir #> bufname   (append)
-	:redir =  variable
-	:redir => variable  (append)
+	:redir @r> register  (append)
+	:redir #>  bufname
+	:redir #>> bufname   (append)
+	:redir =>  variable
+	:redir =>> variable  (append)
 -   Setting of options, specifically for a buffer or window, with
     ":set window.option" or ":set buffer.option=val".  Or use ":buffer.set".
     Also: "buffer.map <F1> quit".
--- a/src/Make_djg.mak
+++ b/src/Make_djg.mak
@@ -33,6 +33,7 @@ OBJ = \
 	obj/fileio.o \
 	obj/fold.o \
 	obj/getchar.o \
+	obj/hashtable.o \
 	obj/main.o \
 	obj/mark.o \
 	obj/memfile.o \
--- a/src/Make_manx.mak
+++ b/src/Make_manx.mak
@@ -48,6 +48,7 @@ SRC =	buffer.c \
 	fileio.c \
 	fold.c \
 	getchar.c \
+	hashtable.c \
 	main.c \
 	mark.c \
 	memfile.c \
@@ -90,6 +91,7 @@ OBJ =	obj/buffer.o \
 	obj/fileio.o \
 	obj/fold.o \
 	obj/getchar.o \
+	obj/hashtable.o \
 	obj/main.o \
 	obj/mark.o \
 	obj/memfile.o \
@@ -130,6 +132,7 @@ PRO =	proto/buffer.pro \
 	proto/fileio.pro \
 	proto/fold.pro \
 	proto/getchar.pro \
+	proto/hashtable.pro \
 	proto/main.pro \
 	proto/mark.pro \
 	proto/memfile.pro \
@@ -243,6 +246,9 @@ obj/fold.o:	fold.c
 obj/getchar.o:	getchar.c
 	$(CCSYM) $@ getchar.c
 
+obj/hashtable.o:	hashtable.c
+	$(CCSYM) $@ hashtable.c
+
 # Don't use $(SYMS) here, because main.c defines EXTERN
 obj/main.o:	main.c option.h globals.h
 	$(CCNOSYM) $@ main.c
--- a/src/Make_morph.mak
+++ b/src/Make_morph.mak
@@ -38,6 +38,7 @@ SRC =	buffer.c						\
 	fileio.c						\
 	fold.c							\
 	getchar.c						\
+	hashtable.c						\
 	main.c							\
 	mark.c							\
 	mbyte.c							\
--- a/src/eval.c
+++ b/src/eval.c
@@ -109,23 +109,35 @@ typedef struct listvar_S listvar;
 
 /*
  * Structure to hold an item of a Dictionary.
+ * The key is copied into "di_key" to avoid an extra alloc/free for it.
  */
 struct dictitem_S
 {
-    struct dictitem_S	*di_next;	/* next item in list */
-    char_u		*di_key;	/* key (never NULL!) */
     typeval		di_tv;		/* type and value of the variable */
+    char_u		di_key[1];	/* key (actually longer!) */
 };
 
 typedef struct dictitem_S dictitem;
 
 /*
+ * In a hashtable item "hi_key" points to "di_key" in a dictitem.
+ * This avoids adding a pointer to the hashtable item.
+ * DI2HIKEY() converts a dictitem pointer to a hashitem key pointer.
+ * HIKEY2DI() converts a hashitem key pointer to a dictitem pointer.
+ * HI2DI() converts a hashitem pointer to a dictitem pointer.
+ */
+static dictitem dumdi;
+#define DI2HIKEY(p) ((p)->di_key)
+#define HIKEY2DI(p) ((dictitem *)(p - (dumdi.di_key - (char_u *)&dumdi.di_tv)))
+#define HI2DI(p)    HIKEY2DI((p)->hi_key)
+
+/*
  * Structure to hold info about a Dictionary.
  */
 struct dictvar_S
 {
     int		dv_refcount;	/* reference count */
-    dictitem	*dv_first;	/* first item, NULL if none */
+    hashtable	dv_hashtable;	/* hashtable that refers to the items */
 };
 
 typedef struct dictvar_S dictvar;
@@ -173,7 +185,6 @@ typedef struct lval_S
     dictvar	*ll_dict;	/* The Dictionary or NULL */
     dictitem	*ll_di;		/* The dictitem or NULL */
     char_u	*ll_newkey;	/* New key for Dict in alloc. mem or NULL. */
-    dictitem	**ll_pdi;	/* di_next field pointing to found dictitem */
 } lval;
 
 
@@ -181,10 +192,9 @@ static char *e_letunexp	= N_("E18: Unexp
 static char *e_listidx = N_("E684: list index out of range: %ld");
 static char *e_undefvar = N_("E121: Undefined variable: %s");
 static char *e_missbrac = N_("E111: Missing ']'");
-static char *e_intern2 = N_("E685: Internal error: %s");
 static char *e_listarg = N_("E686: Argument of %s must be a List");
 static char *e_listdictarg = N_("E712: Argument of %s must be a List or Dictionaary");
-static char *e_emptykey = N_("E713: Empty key in Dictionary");
+static char *e_emptykey = N_("E713: Cannot use empty key for Dictionary");
 static char *e_listreq = N_("E714: List required");
 static char *e_dictreq = N_("E715: Dictionary required");
 static char *e_toomanyarg = N_("E118: Too many arguments for function: %s");
@@ -289,7 +299,6 @@ typedef struct
     dictvar	*fd_dict;	/* Dictionary used */
     char_u	*fd_newkey;	/* new key in "dict" */
     dictitem	*fd_di;		/* Dictionary item used */
-    dictitem	**fd_pdi;	/* field that points to "fd_di" */
 } funcdict;
 
 /*
@@ -451,12 +460,13 @@ static void list_join __ARGS((garray_T *
 static dictvar *dict_alloc __ARGS((void));
 static void dict_unref __ARGS((dictvar *d));
 static void dict_free __ARGS((dictvar *d));
-static dictitem *dictitem_alloc __ARGS((void));
+static dictitem *dictitem_alloc __ARGS((char_u *key));
 static dictitem *dictitem_copy __ARGS((dictitem *org));
+static void dictitem_remove __ARGS((dictvar *dict, dictitem *item));
 static void dictitem_free __ARGS((dictitem *item));
-static void dict_add __ARGS((dictvar *d, dictitem *item));
+static int dict_add __ARGS((dictvar *d, dictitem *item));
 static long dict_len __ARGS((dictvar *d));
-static dictitem *dict_find __ARGS((dictvar *d, char_u *key, int len, dictitem ***pdi));
+static dictitem *dict_find __ARGS((dictvar *d, char_u *key, int len));
 static char_u *dict2string __ARGS((typeval *tv));
 static int get_dict_tv __ARGS((char_u **arg, typeval *rettv, int evaluate));
 
@@ -1829,8 +1839,7 @@ get_lval(name, rettv, lp, unlet, skip, q
 	     * aborting error, an interrupt, or an exception. */
 	    if (!aborting() && !quiet)
 	    {
-		if (unlet)
-		    emsg_severe = TRUE;
+		emsg_severe = TRUE;
 		EMSG2(_(e_invarg2), name);
 		return NULL;
 	    }
@@ -1970,10 +1979,10 @@ get_lval(name, rettv, lp, unlet, skip, q
 	    }
 	    lp->ll_list = NULL;
 	    lp->ll_dict = lp->ll_tv->vval.v_dict;
-	    lp->ll_di = dict_find(lp->ll_dict, key, len, &lp->ll_pdi);
+	    lp->ll_di = dict_find(lp->ll_dict, key, len);
 	    if (lp->ll_di == NULL)
 	    {
-		/* Key does not exist in dict: may need toadd it. */
+		/* Key does not exist in dict: may need to add it. */
 		if (*p == '[' || *p == '.' || unlet)
 		{
 		    if (!quiet)
@@ -2165,12 +2174,14 @@ set_var_lval(lp, endp, rettv, copy, op)
 	    }
 
 	    /* Need to add an item to the Dictionary. */
-	    di = dictitem_alloc();
+	    di = dictitem_alloc(lp->ll_newkey);
 	    if (di == NULL)
 		return;
-	    di->di_key = lp->ll_newkey;
-	    lp->ll_newkey = NULL;
-	    dict_add(lp->ll_tv->vval.v_dict, di);
+	    if (dict_add(lp->ll_tv->vval.v_dict, di) == FAIL)
+	    {
+		vim_free(di);
+		return;
+	    }
 	    lp->ll_tv = &di->di_tv;
 	}
 	else if (op != NULL && *op != '=')
@@ -2532,11 +2543,18 @@ ex_call(eap)
     linenr_T	lnum;
     int		doesrange;
     int		failed = FALSE;
-
-    tofree = trans_function_name(&arg, eap->skip, TFN_INT, NULL);
+    funcdict	fudi;
+
+    tofree = trans_function_name(&arg, eap->skip, TFN_INT, &fudi);
+    vim_free(fudi.fd_newkey);
     if (tofree == NULL)
 	return;
 
+    /* Increase refcount on dictionary, it could get deleted when evaluating
+     * the arguments. */
+    if (fudi.fd_dict != NULL)
+	++fudi.fd_dict->dv_refcount;
+
     /* If it is the name of a variable of type VAR_FUNC use its contents. */
     len = STRLEN(tofree);
     name = deref_func_name(tofree, &len);
@@ -2572,7 +2590,8 @@ ex_call(eap)
 	}
 	arg = startarg;
 	if (get_func_tv(name, STRLEN(name), &rettv, &arg,
-		eap->line1, eap->line2, &doesrange, !eap->skip, NULL) == FAIL)
+		    eap->line1, eap->line2, &doesrange,
+					    !eap->skip, fudi.fd_dict) == FAIL)
 	{
 	    failed = TRUE;
 	    break;
@@ -2603,6 +2622,7 @@ ex_call(eap)
     }
 
 end:
+    dict_unref(fudi.fd_dict);
     vim_free(tofree);
 }
 
@@ -2691,18 +2711,12 @@ do_unlet_var(lp, name_end, forceit)
     }
     else
     {
-	clear_tv(lp->ll_tv);
 	if (lp->ll_list != NULL)
-	{
 	    /* unlet a List item. */
 	    listitem_remove(lp->ll_list, lp->ll_li);
-	}
-	else
-	{
+	else
 	    /* unlet a Dictionary item. */
-	    *lp->ll_pdi = lp->ll_di->di_next;
-	    dictitem_free(lp->ll_di);
-	}
+	    dictitem_remove(lp->ll_dict, lp->ll_di);
     }
 
     return ret;
@@ -3761,7 +3775,7 @@ eval7(arg, rettv, evaluate)
     /*
      * Handle expr[expr], expr[expr:expr] subscript and .name lookup.
      * Also handle function call with Funcref variable: func(expr)
-     * Can all be combined: dict.func(expr)[idx].func(expr)
+     * Can all be combined: dict.func(expr)[idx]['func'](expr)
      */
     selfdict = NULL;
     while (ret == OK
@@ -3779,21 +3793,27 @@ eval7(arg, rettv, evaluate)
 		    curwin->w_cursor.lnum, curwin->w_cursor.lnum,
 		    &len, evaluate, selfdict);
 
-	    /* Stop the expression evaluation when immediately
-	     * aborting on error, or when an interrupt occurred or
-	     * an exception was thrown but not caught. */
+	    /* Stop the expression evaluation when immediately aborting on
+	     * error, or when an interrupt occurred or an exception was thrown
+	     * but not caught. */
 	    if (aborting())
 	    {
 		if (ret == OK)
 		    clear_tv(rettv);
 		ret = FAIL;
 	    }
+	    dict_unref(selfdict);
 	    selfdict = NULL;
 	}
-	else
-	{
+	else /* **arg == '[' || **arg == '.' */
+	{
+	    dict_unref(selfdict);
 	    if (rettv->v_type == VAR_DICT)
+	    {
 		selfdict = rettv->vval.v_dict;
+		if (selfdict != NULL)
+		    ++selfdict->dv_refcount;
+	    }
 	    else
 		selfdict = NULL;
 	    if (eval_index(arg, rettv, evaluate) == FAIL)
@@ -3803,6 +3823,7 @@ eval7(arg, rettv, evaluate)
 	    }
 	}
     }
+    dict_unref(selfdict);
 
     /*
      * Apply logical NOT and unary '-', from right to left, ignore '+'.
@@ -4033,7 +4054,7 @@ eval_index(arg, rettv, evaluate)
 			}
 		    }
 
-		    item = dict_find(rettv->vval.v_dict, key, (int)len, NULL);
+		    item = dict_find(rettv->vval.v_dict, key, (int)len);
 
 		    if (item == NULL)
 			EMSG2(_(e_dictkey), key);
@@ -4371,6 +4392,8 @@ get_list_tv(arg, rettv, evaluate)
 		item->li_tv = tv;
 		list_append(l, item);
 	    }
+	    else
+		clear_tv(&tv);
 	}
 
 	if (**arg == ']')
@@ -4523,18 +4546,25 @@ dict_equal(d1, d2, ic)
     dictvar	*d2;
     int		ic;	/* ignore case for strings */
 {
-    dictitem	*item1, *item2;
+    hashitem	*hi;
+    dictitem	*item2;
+    int		todo;
 
     if (dict_len(d1) != dict_len(d2))
 	return FALSE;
 
-    for (item1 = d1->dv_first; item1 != NULL; item1 = item1->di_next)
-    {
-	item2 = dict_find(d2, item1->di_key, -1, NULL);
-	if (item2 == NULL)
-	    return FALSE;
-	if (!tv_equal(&item1->di_tv, &item2->di_tv, ic))
-	    return FALSE;
+    todo = d1->dv_hashtable.ht_used;
+    for (hi = d1->dv_hashtable.ht_array; todo > 0; ++hi)
+    {
+	if (!HASHITEM_EMPTY(hi))
+	{
+	    item2 = dict_find(d2, hi->hi_key, -1);
+	    if (item2 == NULL)
+		return FALSE;
+	    if (!tv_equal(&HI2DI(hi)->di_tv, &item2->di_tv, ic))
+		return FALSE;
+	    --todo;
+	}
     }
     return TRUE;
 }
@@ -4558,6 +4588,13 @@ tv_equal(tv1, tv2, ic)
 		   || !list_equal(tv1->vval.v_list, tv2->vval.v_list, ic))
 	    return FALSE;
     }
+    else if (tv1->v_type == VAR_DICT || tv2->v_type == VAR_DICT)
+    {
+	/* recursive! */
+	if (tv1->v_type != tv2->v_type
+		   || !dict_equal(tv1->vval.v_dict, tv2->vval.v_dict, ic))
+	    return FALSE;
+    }
     else if (tv1->v_type == VAR_FUNC || tv2->v_type == VAR_FUNC)
     {
 	if (tv1->v_type != tv2->v_type
@@ -4931,7 +4968,12 @@ list_join(gap, l, sep, echo)
     static dictvar *
 dict_alloc()
 {
-    return (dictvar *)alloc_clear(sizeof(dictvar));
+    dictvar *d;
+
+    d = (dictvar *)alloc(sizeof(dictvar));
+    if (d != NULL)
+	hash_init(&d->dv_hashtable);
+    return d;
 }
 
 /*
@@ -4954,24 +4996,39 @@ dict_unref(d)
 dict_free(d)
     dictvar *d;
 {
-    dictitem *item;
-    dictitem *next;
-
-    for (item = d->dv_first; item != NULL; item = next)
-    {
-	next = item->di_next;
-	dictitem_free(item);
+    int		todo;
+    hashitem	*hi;
+
+    /* Careful: we free the dictitems while they still appear in the
+     * hashtable.  Must not try to resize the hashtable! */
+    todo = d->dv_hashtable.ht_used;
+    for (hi = d->dv_hashtable.ht_array; todo > 0; ++hi)
+    {
+	if (!HASHITEM_EMPTY(hi))
+	{
+	    dictitem_free(HI2DI(hi));
+	    --todo;
+	}
     }
     vim_free(d);
 }
 
 /*
  * Allocate a Dictionary item.
+ * The "key" is copied to the new item.
+ * Note that the value of the item "di_tv" still needs to be initialized!
+ * Returns NULL when out of memory.
  */
     static dictitem *
-dictitem_alloc()
-{
-    return (dictitem *)alloc(sizeof(dictitem));
+dictitem_alloc(key)
+    char_u	*key;
+{
+    dictitem *di;
+
+    di = (dictitem *)alloc(sizeof(dictitem) + STRLEN(key));
+    if (di != NULL)
+	STRCPY(di->di_key, key);
+    return di;
 }
 
 /*
@@ -4983,28 +5040,40 @@ dictitem_copy(org)
 {
     dictitem *di;
 
-    di = (dictitem *)alloc(sizeof(dictitem));
+    di = (dictitem *)alloc(sizeof(dictitem) + STRLEN(org->di_key));
     if (di != NULL)
     {
-	di->di_key = vim_strsave(org->di_key);
-	if (di->di_key == NULL)
-	{
-	    vim_free(di);
-	    return NULL;
-	}
+	STRCPY(di->di_key, org->di_key);
 	copy_tv(&org->di_tv, &di->di_tv);
     }
     return di;
 }
 
 /*
+ * Remove item "item" from Dictionary "dict" and free it.
+ */
+    static void
+dictitem_remove(dict, item)
+    dictvar	*dict;
+    dictitem	*item;
+{
+    hashitem	*hi;
+
+    hi = hash_find(&dict->dv_hashtable, item->di_key);
+    if (HASHITEM_EMPTY(hi))
+	EMSG2(_(e_intern2), "dictitem_remove()");
+    else
+	hash_remove(&dict->dv_hashtable, hi);
+    dictitem_free(item);
+}
+
+/*
  * Free a dict item.  Also clears the value.
  */
     static void
 dictitem_free(item)
     dictitem *item;
 {
-    vim_free(item->di_key);
     clear_tv(&item->di_tv);
     vim_free(item);
 }
@@ -5020,8 +5089,9 @@ dict_copy(orig, deep)
     int		deep;
 {
     dictvar	*copy;
-    dictitem	*item;
     dictitem	*di;
+    int		todo;
+    hashitem	*hi;
 
     if (orig == NULL)
 	return NULL;
@@ -5029,23 +5099,28 @@ dict_copy(orig, deep)
     copy = dict_alloc();
     if (copy != NULL)
     {
-	for (item = orig->dv_first; item != NULL; item = item->di_next)
-	{
-	    di = dictitem_alloc();
-	    if (di == NULL)
-		break;
-	    di->di_key = vim_strsave(item->di_key);
-	    if (di->di_key == NULL)
-	    {
-		vim_free(di);
-		break;
-	    }
-	    if (deep)
-		item_copy(&item->di_tv, &di->di_tv, deep);
-	    else
-		copy_tv(&item->di_tv, &di->di_tv);
-	    dict_add(copy, di);
-	}
+	todo = orig->dv_hashtable.ht_used;
+	for (hi = orig->dv_hashtable.ht_array; todo > 0; ++hi)
+	{
+	    if (!HASHITEM_EMPTY(hi))
+	    {
+		--todo;
+
+		di = dictitem_alloc(hi->hi_key);
+		if (di == NULL)
+		    break;
+		if (deep)
+		    item_copy(&HI2DI(hi)->di_tv, &di->di_tv, deep);
+		else
+		    copy_tv(&HI2DI(hi)->di_tv, &di->di_tv);
+		if (dict_add(copy, di) == FAIL)
+		{
+		    dictitem_free(di);
+		    break;
+		}
+	    }
+	}
+
 	++copy->dv_refcount;
     }
 
@@ -5054,69 +5129,15 @@ dict_copy(orig, deep)
 
 /*
  * Add item "item" to Dictionary "d".
- */
-    static void
+ * Returns FAIL when out of memory and when key already existed.
+ */
+    static int
 dict_add(d, item)
     dictvar	*d;
     dictitem	*item;
 {
-    item->di_next = d->dv_first;
-    d->dv_first = item;
-}
-
-#if 0	/* not currently used */
-static void dict_set_item __ARGS((dictvar *d, int type, char *key, void *val));
-
-/*
- * Add an item to Dictionary "d" with type "type", key "key" and value "val".
- * If it already exists it is overwritten.
- * The key and value are copied to allocated memory.
- */
-    static void
-dict_set_item(d, type, key, val)
-    dictvar	*d;
-    int		type;
-    char	*key;
-    void	*val;
-{
-    dictitem	*di;
-    char_u	*dkey;
-
-    di = dict_find(d, (char_u *)key, -1, NULL);
-    if (di == NULL)
-    {
-	dkey = vim_strsave((char_u *)key);
-	if (dkey != NULL)
-	{
-	    di = dictitem_alloc();
-	    if (di == NULL)
-		vim_free(dkey);
-	    else
-		di->di_key = dkey;
-	}
-    }
-    else
-	clear_tv(&di->di_tv);
-
-    if (di != NULL)
-    {
-	di->di_tv.v_type = type;
-	switch (type)
-	{
-	    case VAR_NUMBER:
-		di->di_tv.vval.v_number = (varnumber_T)val;
-		break;
-	    case VAR_FUNC:
-	    case VAR_STRING:
-		di->di_tv.vval.v_string = vim_strsave((char_u *)val);
-		break;
-	    default:
-		EMSG2(_(e_intern2), "dict_set_item()");
-	}
-	dict_add(d, di);
-    }
-}
-#endif
+    return hash_add(&d->dv_hashtable, item->di_key);
+}
 
 /*
  * Get the number of items in a Dictionary.
@@ -5125,43 +5146,49 @@ dict_set_item(d, type, key, val)
 dict_len(d)
     dictvar	*d;
 {
-    dictitem	*item;
-    long	len = 0;
-
     if (d == NULL)
 	return 0L;
-    for (item = d->dv_first; item != NULL; item = item->di_next)
-	++len;
-    return len;
+    return d->dv_hashtable.ht_used;
 }
 
 /*
  * Find item "key[len]" in Dictionary "d".
  * If "len" is negative use strlen(key).
- * Sets "*pdi" to pointer to found item, unless "pdi" is NULL.
  * Returns NULL when not found.
  */
     static dictitem *
-dict_find(d, key, len, pdi)
+dict_find(d, key, len)
     dictvar	*d;
     char_u	*key;
     int		len;
-    dictitem	***pdi;
-{
-    static dictitem *di;
-
-    if (pdi != NULL)
-	*pdi = &d->dv_first;
-    for (di = d->dv_first; di != NULL; di = di->di_next)
-    {
-	if (len < 0
-		? STRCMP(di->di_key, key) == 0
-		: STRNCMP(di->di_key, key, len) == 0 && di->di_key[len] == NUL)
-	    return di;
-	if (pdi != NULL)
-	    *pdi = &di->di_next;
-    }
-    return NULL;
+{
+#define AKEYLEN 200
+    char_u	buf[AKEYLEN];
+    char_u	*akey;
+    char_u	*tofree = NULL;
+    hashitem	*hi;
+
+    if (len < 0)
+	akey = key;
+    else if (len >= AKEYLEN)
+    {
+	tofree = akey = vim_strnsave(key, len);
+	if (akey == NULL)
+	    return NULL;
+    }
+    else
+    {
+	/* Avoid a malloc/free by using buf[]. */
+	STRNCPY(buf, key, len);
+	buf[len] = NUL;
+	akey = buf;
+    }
+
+    hi = hash_find(&d->dv_hashtable, akey);
+    vim_free(tofree);
+    if (HASHITEM_EMPTY(hi))
+	return NULL;
+    return HI2DI(hi);
 }
 
 /*
@@ -5176,32 +5203,40 @@ dict2string(tv)
     int		first = TRUE;
     char_u	*tofree;
     char_u	numbuf[NUMBUFLEN];
-    dictitem	*item;
+    hashitem	*hi;
     char_u	*s;
-
-    if (tv->vval.v_dict == NULL)
+    dictvar	*d;
+    int		todo;
+
+    if ((d = tv->vval.v_dict) == NULL)
 	return NULL;
     ga_init2(&ga, (int)sizeof(char), 80);
     ga_append(&ga, '{');
 
-    for (item = tv->vval.v_dict->dv_first; item != NULL; item = item->di_next)
-    {
-	if (first)
-	    first = FALSE;
-	else
-	    ga_concat(&ga, (char_u *)", ");
-
-	tofree = string_quote(item->di_key, FALSE);
-	if (tofree != NULL)
-	{
-	    ga_concat(&ga, tofree);
+    todo = d->dv_hashtable.ht_used;
+    for (hi = d->dv_hashtable.ht_array; todo > 0; ++hi)
+    {
+	if (!HASHITEM_EMPTY(hi))
+	{
+	    --todo;
+
+	    if (first)
+		first = FALSE;
+	    else
+		ga_concat(&ga, (char_u *)", ");
+
+	    tofree = string_quote(hi->hi_key, FALSE);
+	    if (tofree != NULL)
+	    {
+		ga_concat(&ga, tofree);
+		vim_free(tofree);
+	    }
+	    ga_concat(&ga, (char_u *)": ");
+	    s = tv2string(&HI2DI(hi)->di_tv, &tofree, numbuf);
+	    if (s != NULL)
+		ga_concat(&ga, s);
 	    vim_free(tofree);
 	}
-	ga_concat(&ga, (char_u *)": ");
-	s = tv2string(&item->di_tv, &tofree, numbuf);
-	if (s != NULL)
-	    ga_concat(&ga, s);
-	vim_free(tofree);
     }
 
     ga_append(&ga, '}');
@@ -5220,10 +5255,12 @@ get_dict_tv(arg, rettv, evaluate)
     int		evaluate;
 {
     dictvar	*d = NULL;
+    typeval	tvkey;
     typeval	tv;
     char_u	*key;
     dictitem	*item;
     char_u	*start = skipwhite(*arg + 1);
+    char_u	buf[NUMBUFLEN];
 
     /*
      * First check if it's not a curly-braces thing: {expr}.
@@ -5246,54 +5283,51 @@ get_dict_tv(arg, rettv, evaluate)
 	if (d == NULL)
 	    return FAIL;
     }
+    tvkey.v_type = VAR_UNKNOWN;
+    tv.v_type = VAR_UNKNOWN;
 
     *arg = skipwhite(*arg + 1);
     while (**arg != '}' && **arg != NUL)
     {
-	if (eval1(arg, &tv, evaluate) == FAIL)	/* recursive! */
+	if (eval1(arg, &tvkey, evaluate) == FAIL)	/* recursive! */
 	    goto failret;
 	if (**arg != ':')
 	{
 	    EMSG2(_("E720: Missing colon in Dictionary: %s"), *arg);
-	    clear_tv(&tv);
+	    clear_tv(&tvkey);
 	    goto failret;
 	}
-	key = get_tv_string(&tv);
+	key = get_tv_string_buf(&tvkey, buf);
 	if (*key == NUL)
 	{
 	    EMSG(_(e_emptykey));
-	    clear_tv(&tv);
+	    clear_tv(&tvkey);
 	    goto failret;
 	}
-	key = vim_strsave(key);
-	clear_tv(&tv);
-	if (key == NULL)
-	    goto failret;
 
 	*arg = skipwhite(*arg + 1);
 	if (eval1(arg, &tv, evaluate) == FAIL)	/* recursive! */
 	{
-	    vim_free(key);
+	    clear_tv(&tvkey);
 	    goto failret;
 	}
 	if (evaluate)
 	{
-	    item = dict_find(d, key, -1, NULL);
+	    item = dict_find(d, key, -1);
 	    if (item != NULL)
 	    {
 		EMSG(_("E721: Duplicate key in Dictionary"));
-		vim_free(key);
+		clear_tv(&tvkey);
 		clear_tv(&tv);
 		goto failret;
 	    }
-	    item = dictitem_alloc();
-	    if (item == NULL)
-		vim_free(key);
-	    else
-	    {
-		item->di_key = key;
+	    item = dictitem_alloc(key);
+	    clear_tv(&tvkey);
+	    if (item != NULL)
+	    {
 		item->di_tv = tv;
-		dict_add(d, item);
+		if (dict_add(d, item) == FAIL)
+		    dictitem_free(item);
 	    }
 	}
 
@@ -5994,7 +6028,8 @@ call_func(name, len, rettv, argcount, ar
 		    call_user_func(fp, argcount, argvars, rettv,
 					       firstline, lastline,
 				     (fp->flags & FC_DICT) ? selfdict : NULL);
-		    if (--fp->calls <= 0 && isdigit(*fp->name))
+		    if (--fp->calls <= 0 && isdigit(*fp->name)
+							 && fp->refcount <= 0)
 			/* Function was unreferenced while being used, free it
 			 * now. */
 			func_free(fp);
@@ -6744,11 +6779,12 @@ f_count(argvars, rettv)
     }
     else if (argvars[0].v_type == VAR_DICT)
     {
-	if (argvars[0].vval.v_dict != NULL)
-	{
-	    dictitem	*di;
-
-	    di = argvars[0].vval.v_dict->dv_first;
+	int	    todo;
+	dictvar	    *d;
+	hashitem    *hi;
+
+	if ((d = argvars[0].vval.v_dict) != NULL)
+	{
 	    if (argvars[2].v_type != VAR_UNKNOWN)
 	    {
 		ic = get_tv_number(&argvars[2]);
@@ -6756,9 +6792,16 @@ f_count(argvars, rettv)
 		    EMSG(_(e_invarg));
 	    }
 
-	    for ( ; di != NULL; di = di->di_next)
-		if (tv_equal(&di->di_tv, &argvars[1], ic))
-		    ++n;
+	    todo = d->dv_hashtable.ht_used;
+	    for (hi = d->dv_hashtable.ht_array; todo > 0; ++hi)
+	    {
+		if (!HASHITEM_EMPTY(hi))
+		{
+		    --todo;
+		    if (tv_equal(&HI2DI(hi)->di_tv, &argvars[1], ic))
+			++n;
+		}
+	    }
 	}
     }
     else
@@ -6972,7 +7015,7 @@ f_empty(argvars, rettv)
 	    break;
 	case VAR_DICT:
 	    n = argvars[0].vval.v_dict == NULL
-				  || argvars[0].vval.v_dict->dv_first == NULL;
+			|| argvars[0].vval.v_dict->dv_hashtable.ht_used == 0;
 	    break;
 	default:
 	    EMSG2(_(e_intern2), "f_empty()");
@@ -7197,9 +7240,11 @@ f_extend(argvars, rettv)
     else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type == VAR_DICT)
     {
 	dictvar		*d1, *d2;
-	dictitem	*d1i, *d2i;
+	dictitem	*di1;
 	char_u		*action;
 	int		i;
+	hashitem	*hi2;
+	int		todo;
 
 	d1 = argvars[0].vval.v_dict;
 	d2 = argvars[1].vval.v_dict;
@@ -7225,24 +7270,29 @@ f_extend(argvars, rettv)
 
 	    /* Go over all entries in the second dict and add them to the
 	     * first dict. */
-	    for (d2i = d2->dv_first; d2i != NULL; d2i = d2i->di_next)
-	    {
-		d1i = dict_find(d1, d2i->di_key, -1, NULL);
-		if (d1i == NULL)
-		{
-		    d1i = dictitem_copy(d2i);
-		    if (d1i != NULL)
-			dict_add(d1, d1i);
-		}
-		else if (*action == 'e')
-		{
-		    EMSG2(_("Key already exists: %s"), d2i->di_key);
-		    break;
-		}
-		else if (*action == 'f')
-		{
-		    clear_tv(&d1i->di_tv);
-		    copy_tv(&d2i->di_tv, &d1i->di_tv);
+	    todo = d2->dv_hashtable.ht_used;
+	    for (hi2 = d2->dv_hashtable.ht_array; todo > 0; ++hi2)
+	    {
+		if (!HASHITEM_EMPTY(hi2))
+		{
+		    --todo;
+		    di1 = dict_find(d1, hi2->hi_key, -1);
+		    if (di1 == NULL)
+		    {
+			di1 = dictitem_copy(HI2DI(hi2));
+			if (di1 != NULL && dict_add(d1, di1) == FAIL)
+			    dictitem_free(di1);
+		    }
+		    else if (*action == 'e')
+		    {
+			EMSG2(_("E737: Key already exists: %s"), hi2->hi_key);
+			break;
+		    }
+		    else if (*action == 'f')
+		    {
+			clear_tv(&di1->di_tv);
+			copy_tv(&HI2DI(hi2)->di_tv, &di1->di_tv);
+		    }
 		}
 	    }
 
@@ -7378,11 +7428,13 @@ filter_map(argvars, rettv, map)
     char_u	*expr;
     listitem	*li, *nli;
     listvar	*l = NULL;
-    dictitem	*di, **pdi;
+    dictitem	*di;
+    hashitem	*hi;
     dictvar	*d = NULL;
     typeval	save_val;
     typeval	save_key;
     int		rem;
+    int		todo;
 
     rettv->vval.v_number = 0;
     if (argvars[0].v_type == VAR_LIST)
@@ -7408,21 +7460,23 @@ filter_map(argvars, rettv, map)
     {
 	save_key = vimvars[VV_KEY].tv;
 	vimvars[VV_KEY].tv.v_type = VAR_STRING;
-	pdi = &d->dv_first;
-	for (di = d->dv_first; di != NULL; di = *pdi)
-	{
-	    vimvars[VV_KEY].tv.vval.v_string = vim_strsave(di->di_key);
-	    if (filter_map_one(&di->di_tv, expr, map, &rem) == FAIL)
-		break;
-	    if (!map && rem)
-	    {
-		*pdi = di->di_next;
-		dictitem_free(di);
-	    }
-	    else
-		pdi = &di->di_next;
-	    clear_tv(&vimvars[VV_KEY].tv);
-	}
+
+	todo = d->dv_hashtable.ht_used;
+	for (hi = d->dv_hashtable.ht_array; todo > 0; ++hi)
+	{
+	    if (!HASHITEM_EMPTY(hi))
+	    {
+		--todo;
+		di = HI2DI(hi);
+		vimvars[VV_KEY].tv.vval.v_string = vim_strsave(di->di_key);
+		if (filter_map_one(&di->di_tv, expr, map, &rem) == FAIL)
+		    break;
+		if (!map && rem)
+		    dictitem_remove(d, di);
+		clear_tv(&vimvars[VV_KEY].tv);
+	    }
+	}
+
 	clear_tv(&vimvars[VV_KEY].tv);
 	vimvars[VV_KEY].tv = save_key;
     }
@@ -7784,7 +7838,7 @@ f_get(argvars, rettv)
     {
 	if ((d = argvars[0].vval.v_dict) != NULL)
 	{
-	    di = dict_find(d, get_tv_string(&argvars[1]), -1, NULL);
+	    di = dict_find(d, get_tv_string(&argvars[1]), -1);
 	    if (di != NULL)
 		tv = &di->di_tv;
 	}
@@ -8917,7 +8971,7 @@ f_has_key(argvars, rettv)
 	return;
 
     rettv->vval.v_number = dict_find(argvars[0].vval.v_dict,
-				get_tv_string(&argvars[1]), -1, NULL) != NULL;
+				      get_tv_string(&argvars[1]), -1) != NULL;
 }
 
 /*
@@ -9412,8 +9466,11 @@ dict_list(argvars, rettv, what)
     listvar	*l;
     listvar	*l2;
     dictitem	*di;
+    hashitem	*hi;
     listitem	*li;
     listitem	*li2;
+    dictvar	*d;
+    int		todo;
 
     rettv->vval.v_number = 0;
     if (argvars[0].v_type != VAR_DICT)
@@ -9421,7 +9478,7 @@ dict_list(argvars, rettv, what)
 	EMSG(_(e_dictreq));
 	return;
     }
-    if (argvars[0].vval.v_dict == NULL)
+    if ((d = argvars[0].vval.v_dict) == NULL)
 	return;
 
     l = list_alloc();
@@ -9431,46 +9488,53 @@ dict_list(argvars, rettv, what)
     rettv->vval.v_list = l;
     ++l->lv_refcount;
 
-    for (di = argvars[0].vval.v_dict->dv_first; di != NULL; di = di->di_next)
-    {
-	li = listitem_alloc();
-	if (li == NULL)
-	    break;
-	list_append(l, li);
-
-	if (what == 0)
-	{
-	    /* keys() */
-	    li->li_tv.v_type = VAR_STRING;
-	    li->li_tv.vval.v_string = vim_strsave(di->di_key);
-	}
-	else if (what == 1)
-	{
-	    /* values() */
-	    copy_tv(&di->di_tv, &li->li_tv);
-	}
-	else
-	{
-	    /* items() */
-	    l2 = list_alloc();
-	    li->li_tv.v_type = VAR_LIST;
-	    li->li_tv.vval.v_list = l2;
-	    if (l2 == NULL)
+    todo = d->dv_hashtable.ht_used;
+    for (hi = d->dv_hashtable.ht_array; todo > 0; ++hi)
+    {
+	if (!HASHITEM_EMPTY(hi))
+	{
+	    --todo;
+	    di = HI2DI(hi);
+
+	    li = listitem_alloc();
+	    if (li == NULL)
 		break;
-	    ++l2->lv_refcount;
-
-	    li2 = listitem_alloc();
-	    if (li2 == NULL)
-		break;
-	    list_append(l2, li2);
-	    li2->li_tv.v_type = VAR_STRING;
-	    li2->li_tv.vval.v_string = vim_strsave(di->di_key);
-
-	    li2 = listitem_alloc();
-	    if (li2 == NULL)
-		break;
-	    list_append(l2, li2);
-	    copy_tv(&di->di_tv, &li2->li_tv);
+	    list_append(l, li);
+
+	    if (what == 0)
+	    {
+		/* keys() */
+		li->li_tv.v_type = VAR_STRING;
+		li->li_tv.vval.v_string = vim_strsave(di->di_key);
+	    }
+	    else if (what == 1)
+	    {
+		/* values() */
+		copy_tv(&di->di_tv, &li->li_tv);
+	    }
+	    else
+	    {
+		/* items() */
+		l2 = list_alloc();
+		li->li_tv.v_type = VAR_LIST;
+		li->li_tv.vval.v_list = l2;
+		if (l2 == NULL)
+		    break;
+		++l2->lv_refcount;
+
+		li2 = listitem_alloc();
+		if (li2 == NULL)
+		    break;
+		list_append(l2, li2);
+		li2->li_tv.v_type = VAR_STRING;
+		li2->li_tv.vval.v_string = vim_strsave(di->di_key);
+
+		li2 = listitem_alloc();
+		if (li2 == NULL)
+		    break;
+		list_append(l2, li2);
+		copy_tv(&di->di_tv, &li2->li_tv);
+	    }
 	}
     }
 }
@@ -10023,22 +10087,26 @@ max_min(argvars, rettv, domax)
     else if (argvars[0].v_type == VAR_DICT)
     {
 	dictvar		*d;
-	dictitem	*di;
+	int		first = TRUE;
+	hashitem	*hi;
+	int		todo;
 
 	d = argvars[0].vval.v_dict;
 	if (d != NULL)
 	{
-	    di = d->dv_first;
-	    if (di != NULL)
-	    {
-		n = get_tv_number(&di->di_tv);
-		while (1)
-		{
-		    di = di->di_next;
-		    if (di == NULL)
-			break;
-		    i = get_tv_number(&di->di_tv);
-		    if (domax ? i > n : i < n)
+	    todo = d->dv_hashtable.ht_used;
+	    for (hi = d->dv_hashtable.ht_array; todo > 0; ++hi)
+	    {
+		if (!HASHITEM_EMPTY(hi))
+		{
+		    --todo;
+		    i = get_tv_number(&HI2DI(hi)->di_tv);
+		    if (first)
+		    {
+			n = i;
+			first = FALSE;
+		    }
+		    else if (domax ? i > n : i < n)
 			n = i;
 		}
 	    }
@@ -10472,7 +10540,7 @@ f_remove(argvars, rettv)
     long	end;
     char_u	*key;
     dictvar	*d;
-    dictitem	*di, **pdi;
+    dictitem	*di;
 
     rettv->vval.v_number = 0;
     if (argvars[0].v_type == VAR_DICT)
@@ -10482,18 +10550,15 @@ f_remove(argvars, rettv)
 	else if ((d = argvars[0].vval.v_dict) != NULL)
 	{
 	    key = get_tv_string(&argvars[1]);
-	    pdi = &d->dv_first;
-	    for (di = d->dv_first; di != NULL; pdi = &di->di_next, di = *pdi)
-		if (STRCMP(di->di_key, key) == 0)
-		{
-		    *pdi = di->di_next;
-		    *rettv = di->di_tv;
-		    init_tv(&di->di_tv);
-		    dictitem_free(di);
-		    break;
-		}
+	    di = dict_find(d, key, -1);
 	    if (di == NULL)
 		EMSG2(_(e_dictkey), key);
+	    else
+	    {
+		*rettv = di->di_tv;
+		init_tv(&di->di_tv);
+		dictitem_remove(d, di);
+	    }
 	}
     }
     else if (argvars[0].v_type != VAR_LIST)
@@ -13134,9 +13199,11 @@ clear_tv(varp)
 		break;
 	    case VAR_LIST:
 		list_unref(varp->vval.v_list);
+		varp->vval.v_list = NULL;
 		break;
 	    case VAR_DICT:
 		dict_unref(varp->vval.v_dict);
+		varp->vval.v_dict = NULL;
 		break;
 	    case VAR_NUMBER:
 		varp->vval.v_number = 0;
@@ -14003,6 +14070,8 @@ ex_function(eap)
 	 */
 	if (!aborting())
 	{
+	    if (!eap->skip && fudi.fd_newkey != NULL)
+		EMSG2(_(e_dictkey), fudi.fd_newkey);
 	    vim_free(fudi.fd_newkey);
 	    return;
 	}
@@ -14348,15 +14417,17 @@ ex_function(eap)
 	    if (fudi.fd_di == NULL)
 	    {
 		/* add new dict entry */
-		fudi.fd_di = dictitem_alloc();
+		fudi.fd_di = dictitem_alloc(fudi.fd_newkey);
 		if (fudi.fd_di == NULL)
 		{
 		    vim_free(fp);
 		    goto erret;
 		}
-		fudi.fd_di->di_key = fudi.fd_newkey;
-		fudi.fd_newkey = NULL;
-		dict_add(fudi.fd_dict, fudi.fd_di);
+		if (dict_add(fudi.fd_dict, fudi.fd_di) == FAIL)
+		{
+		    vim_free(fudi.fd_di);
+		    goto erret;
+		}
 	    }
 	    else
 		/* overwrite existing dict entry */
@@ -14454,7 +14525,6 @@ trans_function_name(pp, skip, flags, fdp
 	    fdp->fd_newkey = lv.ll_newkey;
 	    lv.ll_newkey = NULL;
 	    fdp->fd_di = lv.ll_di;
-	    fdp->fd_pdi = lv.ll_pdi;
 	}
 	if (lv.ll_tv->v_type == VAR_FUNC && lv.ll_tv->vval.v_string != NULL)
 	{
@@ -14463,8 +14533,8 @@ trans_function_name(pp, skip, flags, fdp
 	}
 	else
 	{
-	    if (!skip && !(flags & TFN_QUIET)
-				       && (fdp == NULL || lv.ll_dict == NULL))
+	    if (!skip && !(flags & TFN_QUIET) && (fdp == NULL
+			     || lv.ll_dict == NULL || fdp->fd_newkey == NULL))
 		EMSG(_(e_funcref));
 	    else
 		*pp = end;
@@ -14743,8 +14813,7 @@ ex_delfunction(eap)
 	{
 	    /* Delete the dict item that refers to the function, it will
 	     * invoke func_unref() and possibly delete the function. */
-	    *fudi.fd_pdi = fudi.fd_di->di_next;
-	    dictitem_free(fudi.fd_di);
+	    dictitem_remove(fudi.fd_dict, fudi.fd_di);
 	}
 	else
 	    func_free(fp);
--- a/src/testdir/Make_vms.mms
+++ b/src/testdir/Make_vms.mms
@@ -4,7 +4,7 @@
 # Authors:	Zoltan Arpadffy, <arpadffy@polarhome.com>
 #		Sandor Kopanyi,  <sandor.kopanyi@mailbox.hu>
 #
-# Last change:  2004 Dec 24
+# Last change:  2005 Jan 19
 #
 # This has been tested on VMS 6.2 to 7.2 on DEC Alpha and VAX.
 # Edit the lines in the Configuration section below to select.
@@ -57,7 +57,7 @@ SCRIPT = test1.out  test2.out  test3.out
 	 test33.out test34.out test35.out test36.out test37.out \
 	 test38.out test39.out test40.out test41.out test42.out \
 	 test43.out test44.out test45.out test46.out \
-	 test48.out test51.out test53.out test54.out
+	 test48.out test51.out test53.out test54.out test55.out
 
 .IFDEF WANT_GUI
 SCRIPT_GUI = test16.out