This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
unpack("Z*Z*", pack("Z*Z*", ..)) bug, patch and test from
[perl5.git] / hv.c
diff --git a/hv.c b/hv.c
index 61e406e..d553920 100644 (file)
--- a/hv.c
+++ b/hv.c
@@ -1,6 +1,6 @@
 /*    hv.c
  *
- *    Copyright (c) 1991-1997, Larry Wall
+ *    Copyright (c) 1991-2001, Larry Wall
  *
  *    You may distribute under the terms of either the GNU General Public
  *    License or the Artistic License, as specified in the README file.
  */
 
 #include "EXTERN.h"
+#define PERL_IN_HV_C
 #include "perl.h"
 
-static void hv_magic_check _((HV *hv, bool *needs_copy, bool *needs_store));
-#ifndef PERL_OBJECT
-static void hsplit _((HV *hv));
-static void hfreeentries _((HV *hv));
-static HE* more_he _((void));
-#endif
-
-#if defined(STRANGE_MALLOC) || defined(MYMALLOC)
-#  define ARRAY_ALLOC_BYTES(size) ( (size)*sizeof(HE*) )
-#else
-#  define MALLOC_OVERHEAD 16
-#  define ARRAY_ALLOC_BYTES(size) ( (size)*sizeof(HE*)*2 - MALLOC_OVERHEAD )
-#endif
-
 STATIC HE*
-new_he(void)
+S_new_he(pTHX)
 {
     HE* he;
-    if (PL_he_root) {
-        he = PL_he_root;
-        PL_he_root = HeNEXT(he);
-        return he;
-    }
-    return more_he();
+    LOCK_SV_MUTEX;
+    if (!PL_he_root)
+        more_he();
+    he = PL_he_root;
+    PL_he_root = HeNEXT(he);
+    UNLOCK_SV_MUTEX;
+    return he;
 }
 
 STATIC void
-del_he(HE *p)
+S_del_he(pTHX_ HE *p)
 {
+    LOCK_SV_MUTEX;
     HeNEXT(p) = (HE*)PL_he_root;
     PL_he_root = p;
+    UNLOCK_SV_MUTEX;
 }
 
-STATIC HE*
-more_he(void)
+STATIC void
+S_more_he(pTHX)
 {
     register HE* he;
     register HE* heend;
-    New(54, PL_he_root, 1008/sizeof(HE), HE);
-    he = PL_he_root;
+    XPV *ptr;
+    New(54, ptr, 1008/sizeof(XPV), XPV);
+    ptr->xpv_pv = (char*)PL_he_arenaroot;
+    PL_he_arenaroot = ptr;
+
+    he = (HE*)ptr;
     heend = &he[1008 / sizeof(HE) - 1];
+    PL_he_root = ++he;
     while (he < heend) {
         HeNEXT(he) = (HE*)(he + 1);
         he++;
     }
     HeNEXT(he) = 0;
-    return new_he();
 }
 
+#ifdef PURIFY
+
+#define new_HE() (HE*)safemalloc(sizeof(HE))
+#define del_HE(p) safefree((char*)p)
+
+#else
+
+#define new_HE() new_he()
+#define del_HE(p) del_he(p)
+
+#endif
+
 STATIC HEK *
-save_hek(char *str, I32 len, U32 hash)
+S_save_hek(pTHX_ const char *str, I32 len, U32 hash)
 {
     char *k;
     register HEK *hek;
-    
+    bool is_utf8 = FALSE;
+
+    if (len < 0) {
+      len = -len;
+      is_utf8 = TRUE;
+    }
+
     New(54, k, HEK_BASESIZE + len + 1, char);
     hek = (HEK*)k;
     Copy(str, HEK_KEY(hek), len, char);
-    *(HEK_KEY(hek) + len) = '\0';
     HEK_LEN(hek) = len;
     HEK_HASH(hek) = hash;
+    HEK_UTF8(hek) = (char)is_utf8;
     return hek;
 }
 
 void
-unshare_hek(HEK *hek)
+Perl_unshare_hek(pTHX_ HEK *hek)
+{
+    unsharepvn(HEK_KEY(hek),HEK_UTF8(hek)?-HEK_LEN(hek):HEK_LEN(hek),
+               HEK_HASH(hek));
+}
+
+#if defined(USE_ITHREADS)
+HE *
+Perl_he_dup(pTHX_ HE *e, bool shared, clone_params* param)
 {
-    unsharepvn(HEK_KEY(hek),HEK_LEN(hek),HEK_HASH(hek));
+    HE *ret;
+
+    if (!e)
+       return Nullhe;
+    /* look for it in the table first */
+    ret = (HE*)ptr_table_fetch(PL_ptr_table, e);
+    if (ret)
+       return ret;
+
+    /* create anew and remember what it is */
+    ret = new_HE();
+    ptr_table_store(PL_ptr_table, e, ret);
+
+    HeNEXT(ret) = he_dup(HeNEXT(e),shared, param);
+    if (HeKLEN(e) == HEf_SVKEY)
+       HeKEY_sv(ret) = SvREFCNT_inc(sv_dup(HeKEY_sv(e), param));
+    else if (shared)
+       HeKEY_hek(ret) = share_hek(HeKEY(e), HeKLEN_UTF8(e), HeHASH(e));
+    else
+       HeKEY_hek(ret) = save_hek(HeKEY(e), HeKLEN_UTF8(e), HeHASH(e));
+    HeVAL(ret) = SvREFCNT_inc(sv_dup(HeVAL(e), param));
+    return ret;
 }
+#endif /* USE_ITHREADS */
 
 /* (klen == HEf_SVKEY) is special for MAGICAL hv entries, meaning key slot
  * contains an SV* */
 
+/*
+=for apidoc hv_fetch
+
+Returns the SV which corresponds to the specified key in the hash.  The
+C<klen> is the length of the key.  If C<lval> is set then the fetch will be
+part of a store.  Check that the return value is non-null before
+dereferencing it to a C<SV*>.
+
+See L<perlguts/"Understanding the Magic of Tied Hashes and Arrays"> for more
+information on how to use this function on tied hashes.
+
+=cut
+*/
+
 SV**
-hv_fetch(HV *hv, char *key, U32 klen, I32 lval)
+Perl_hv_fetch(pTHX_ HV *hv, const char *key, I32 klen, I32 lval)
 {
     register XPVHV* xhv;
     register U32 hash;
     register HE *entry;
     SV *sv;
+    bool is_utf8 = FALSE;
+    const char *keysave = key;
 
     if (!hv)
        return 0;
 
+    if (klen < 0) {
+      klen = -klen;
+      is_utf8 = TRUE;
+    }
+
     if (SvRMAGICAL(hv)) {
-       if (mg_find((SV*)hv,'P')) {
-           dTHR;
+       if (mg_find((SV*)hv, PERL_MAGIC_tied) || SvGMAGICAL((SV*)hv)) {
            sv = sv_newmortal();
            mg_copy((SV*)hv, sv, key, klen);
            PL_hv_fetch_sv = sv;
            return &PL_hv_fetch_sv;
        }
 #ifdef ENV_IS_CASELESS
-       else if (mg_find((SV*)hv,'E')) {
+       else if (mg_find((SV*)hv, PERL_MAGIC_env)) {
            U32 i;
            for (i = 0; i < klen; ++i)
                if (isLOWER(key[i])) {
-                   char *nkey = strupr(SvPVX(sv_2mortal(newSVpv(key,klen))));
+                   char *nkey = strupr(SvPVX(sv_2mortal(newSVpvn(key,klen))));
                    SV **ret = hv_fetch(hv, nkey, klen, 0);
                    if (!ret && lval)
                        ret = hv_store(hv, key, klen, NEWSV(61,0), 0);
@@ -121,65 +183,110 @@ hv_fetch(HV *hv, char *key, U32 klen, I32 lval)
 #endif
     }
 
+    /* We use xhv->xhv_foo fields directly instead of HvFOO(hv) to
+       avoid unnecessary pointer dereferencing. */
     xhv = (XPVHV*)SvANY(hv);
-    if (!xhv->xhv_array) {
-       if (lval 
+    if (!xhv->xhv_array /* !HvARRAY(hv) */) {
+       if (lval
 #ifdef DYNAMIC_ENV_FETCH  /* if it's an %ENV lookup, we may get it on the fly */
-                || (HvNAME(hv) && strEQ(HvNAME(hv),ENV_HV_NAME))
+                || (SvRMAGICAL((SV*)hv) && mg_find((SV*)hv, PERL_MAGIC_env))
 #endif
                                                                  )
-           Newz(503,xhv->xhv_array, ARRAY_ALLOC_BYTES(xhv->xhv_max + 1), char);
+           Newz(503, xhv->xhv_array /* HvARRAY(hv) */,
+                PERL_HV_ARRAY_ALLOC_BYTES(xhv->xhv_max+1 /* HvMAX(hv)+1 */),
+                char);
        else
            return 0;
     }
 
+    if (is_utf8) {
+       STRLEN tmplen = klen;
+       /* Just casting the &klen to (STRLEN) won't work well
+        * if STRLEN and I32 are of different widths. --jhi */
+       key = (char*)bytes_from_utf8((U8*)key, &tmplen, &is_utf8);
+       klen = tmplen;
+    }
+
     PERL_HASH(hash, key, klen);
 
+    /* entry = (HvARRAY(hv))[hash & (I32) HvMAX(hv)]; */
     entry = ((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max];
     for (; entry; entry = HeNEXT(entry)) {
        if (HeHASH(entry) != hash)              /* strings can't be equal */
            continue;
        if (HeKLEN(entry) != klen)
            continue;
-       if (memNE(HeKEY(entry),key,klen))       /* is this it? */
+       if (HeKEY(entry) != key && memNE(HeKEY(entry),key,klen))        /* is this it? */
            continue;
+       if (HeKUTF8(entry) != (char)is_utf8)
+           continue;
+       if (key != keysave)
+           Safefree(key);
        return &HeVAL(entry);
     }
 #ifdef DYNAMIC_ENV_FETCH  /* %ENV lookup?  If so, try to fetch the value now */
-    if (HvNAME(hv) && strEQ(HvNAME(hv),ENV_HV_NAME)) {
-      char *gotenv;
-
-      if ((gotenv = PerlEnv_getenv(key)) != Nullch) {
-        sv = newSVpv(gotenv,strlen(gotenv));
-        SvTAINTED_on(sv);
-        return hv_store(hv,key,klen,sv,hash);
-      }
+    if (SvRMAGICAL((SV*)hv) && mg_find((SV*)hv, PERL_MAGIC_env)) {
+       unsigned long len;
+       char *env = PerlEnv_ENVgetenv_len(key,&len);
+       if (env) {
+           sv = newSVpvn(env,len);
+           SvTAINTED_on(sv);
+           if (key != keysave)
+               Safefree(key);
+           return hv_store(hv,key,klen,sv,hash);
+       }
     }
 #endif
     if (lval) {                /* gonna assign to this, so it better be there */
        sv = NEWSV(61,0);
-       return hv_store(hv,key,klen,sv,hash);
+       if (key != keysave) { /* must be is_utf8 == 0 */
+           SV **ret = hv_store(hv,key,klen,sv,hash);
+           Safefree(key);
+           return ret;
+       }
+       else
+           return hv_store(hv,key,is_utf8?-klen:klen,sv,hash);
     }
+    if (key != keysave)
+       Safefree(key);
     return 0;
 }
 
 /* returns a HE * structure with the all fields set */
 /* note that hent_val will be a mortal sv for MAGICAL hashes */
+/*
+=for apidoc hv_fetch_ent
+
+Returns the hash entry which corresponds to the specified key in the hash.
+C<hash> must be a valid precomputed hash number for the given C<key>, or 0
+if you want the function to compute it.  IF C<lval> is set then the fetch
+will be part of a store.  Make sure the return value is non-null before
+accessing it.  The return value when C<tb> is a tied hash is a pointer to a
+static location, so be sure to make a copy of the structure if you need to
+store it somewhere.
+
+See L<perlguts/"Understanding the Magic of Tied Hashes and Arrays"> for more
+information on how to use this function on tied hashes.
+
+=cut
+*/
+
 HE *
-hv_fetch_ent(HV *hv, SV *keysv, I32 lval, register U32 hash)
+Perl_hv_fetch_ent(pTHX_ HV *hv, SV *keysv, I32 lval, register U32 hash)
 {
     register XPVHV* xhv;
     register char *key;
     STRLEN klen;
     register HE *entry;
     SV *sv;
+    bool is_utf8;
+    char *keysave;
 
     if (!hv)
        return 0;
 
     if (SvRMAGICAL(hv)) {
-       if (mg_find((SV*)hv,'P')) {
-           dTHR;
+       if (mg_find((SV*)hv, PERL_MAGIC_tied) || SvGMAGICAL((SV*)hv)) {
            sv = sv_newmortal();
            keysv = sv_2mortal(newSVsv(keysv));
            mg_copy((SV*)hv, sv, (char*)keysv, HEf_SVKEY);
@@ -193,12 +300,12 @@ hv_fetch_ent(HV *hv, SV *keysv, I32 lval, register U32 hash)
            return &PL_hv_fetch_ent_mh;
        }
 #ifdef ENV_IS_CASELESS
-       else if (mg_find((SV*)hv,'E')) {
+       else if (mg_find((SV*)hv, PERL_MAGIC_env)) {
            U32 i;
            key = SvPV(keysv, klen);
            for (i = 0; i < klen; ++i)
                if (isLOWER(key[i])) {
-                   SV *nkeysv = sv_2mortal(newSVpv(key,klen));
+                   SV *nkeysv = sv_2mortal(newSVpvn(key,klen));
                    (void)strupr(SvPVX(nkeysv));
                    entry = hv_fetch_ent(hv, nkeysv, 0, 0);
                    if (!entry && lval)
@@ -210,43 +317,56 @@ hv_fetch_ent(HV *hv, SV *keysv, I32 lval, register U32 hash)
     }
 
     xhv = (XPVHV*)SvANY(hv);
-    if (!xhv->xhv_array) {
-       if (lval 
+    if (!xhv->xhv_array /* !HvARRAY(hv) */) {
+       if (lval
 #ifdef DYNAMIC_ENV_FETCH  /* if it's an %ENV lookup, we may get it on the fly */
-                || (HvNAME(hv) && strEQ(HvNAME(hv),ENV_HV_NAME))
+                || (SvRMAGICAL((SV*)hv) && mg_find((SV*)hv, PERL_MAGIC_env))
 #endif
                                                                  )
-           Newz(503,xhv->xhv_array, ARRAY_ALLOC_BYTES(xhv->xhv_max + 1), char);
+           Newz(503, xhv->xhv_array /* HvARRAY(hv) */,
+                PERL_HV_ARRAY_ALLOC_BYTES(xhv->xhv_max+1 /* HvMAX(hv)+1 */),
+                char);
        else
            return 0;
     }
 
-    key = SvPV(keysv, klen);
-    
+    keysave = key = SvPV(keysv, klen);
+    is_utf8 = (SvUTF8(keysv)!=0);
+
+    if (is_utf8)
+       key = (char*)bytes_from_utf8((U8*)key, &klen, &is_utf8);
+
     if (!hash)
        PERL_HASH(hash, key, klen);
 
+    /* entry = (HvARRAY(hv))[hash & (I32) HvMAX(hv)]; */
     entry = ((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max];
     for (; entry; entry = HeNEXT(entry)) {
        if (HeHASH(entry) != hash)              /* strings can't be equal */
            continue;
        if (HeKLEN(entry) != klen)
            continue;
-       if (memNE(HeKEY(entry),key,klen))       /* is this it? */
+       if (HeKEY(entry) != key && memNE(HeKEY(entry),key,klen))        /* is this it? */
+           continue;
+       if (HeKUTF8(entry) != (char)is_utf8)
            continue;
+       if (key != keysave)
+           Safefree(key);
        return entry;
     }
 #ifdef DYNAMIC_ENV_FETCH  /* %ENV lookup?  If so, try to fetch the value now */
-    if (HvNAME(hv) && strEQ(HvNAME(hv),ENV_HV_NAME)) {
-      char *gotenv;
-
-      if ((gotenv = PerlEnv_getenv(key)) != Nullch) {
-        sv = newSVpv(gotenv,strlen(gotenv));
-        SvTAINTED_on(sv);
-        return hv_store_ent(hv,keysv,sv,hash);
-      }
+    if (SvRMAGICAL((SV*)hv) && mg_find((SV*)hv, PERL_MAGIC_env)) {
+       unsigned long len;
+       char *env = PerlEnv_ENVgetenv_len(key,&len);
+       if (env) {
+           sv = newSVpvn(env,len);
+           SvTAINTED_on(sv);
+           return hv_store_ent(hv,keysv,sv,hash);
+       }
     }
 #endif
+    if (key != keysave)
+       Safefree(key);
     if (lval) {                /* gonna assign to this, so it better be there */
        sv = NEWSV(61,0);
        return hv_store_ent(hv,keysv,sv,hash);
@@ -254,8 +374,8 @@ hv_fetch_ent(HV *hv, SV *keysv, I32 lval, register U32 hash)
     return 0;
 }
 
-static void
-hv_magic_check (HV *hv, bool *needs_copy, bool *needs_store)
+STATIC void
+S_hv_magic_check(pTHX_ HV *hv, bool *needs_copy, bool *needs_store)
 {
     MAGIC *mg = SvMAGIC(hv);
     *needs_copy = FALSE;
@@ -264,8 +384,8 @@ hv_magic_check (HV *hv, bool *needs_copy, bool *needs_store)
        if (isUPPER(mg->mg_type)) {
            *needs_copy = TRUE;
            switch (mg->mg_type) {
-           case 'P':
-           case 'S':
+           case PERL_MAGIC_tied:
+           case PERL_MAGIC_sig:
                *needs_store = FALSE;
            }
        }
@@ -273,17 +393,42 @@ hv_magic_check (HV *hv, bool *needs_copy, bool *needs_store)
     }
 }
 
+/*
+=for apidoc hv_store
+
+Stores an SV in a hash.  The hash key is specified as C<key> and C<klen> is
+the length of the key.  The C<hash> parameter is the precomputed hash
+value; if it is zero then Perl will compute it.  The return value will be
+NULL if the operation failed or if the value did not need to be actually
+stored within the hash (as in the case of tied hashes).  Otherwise it can
+be dereferenced to get the original C<SV*>.  Note that the caller is
+responsible for suitably incrementing the reference count of C<val> before
+the call, and decrementing it if the function returned NULL.
+
+See L<perlguts/"Understanding the Magic of Tied Hashes and Arrays"> for more
+information on how to use this function on tied hashes.
+
+=cut
+*/
+
 SV**
-hv_store(HV *hv, char *key, U32 klen, SV *val, register U32 hash)
+Perl_hv_store(pTHX_ HV *hv, const char *key, I32 klen, SV *val, register U32 hash)
 {
     register XPVHV* xhv;
     register I32 i;
     register HE *entry;
     register HE **oentry;
+    bool is_utf8 = FALSE;
+    const char *keysave = key;
 
     if (!hv)
        return 0;
 
+    if (klen < 0) {
+      klen = -klen;
+      is_utf8 = TRUE;
+    }
+
     xhv = (XPVHV*)SvANY(hv);
     if (SvMAGICAL(hv)) {
        bool needs_copy;
@@ -291,23 +436,33 @@ hv_store(HV *hv, char *key, U32 klen, SV *val, register U32 hash)
        hv_magic_check (hv, &needs_copy, &needs_store);
        if (needs_copy) {
            mg_copy((SV*)hv, val, key, klen);
-           if (!xhv->xhv_array && !needs_store)
+           if (!xhv->xhv_array /* !HvARRAY */ && !needs_store)
                return 0;
 #ifdef ENV_IS_CASELESS
-           else if (mg_find((SV*)hv,'E')) {
-               SV *sv = sv_2mortal(newSVpv(key,klen));
-               key = strupr(SvPVX(sv));
+           else if (mg_find((SV*)hv, PERL_MAGIC_env)) {
+                key = savepvn(key,klen);
+               key = (const char*)strupr((char*)key);
                hash = 0;
            }
 #endif
        }
     }
+    if (is_utf8) {
+       STRLEN tmplen = klen;
+       /* See the note in hv_fetch(). --jhi */
+       key = (char*)bytes_from_utf8((U8*)key, &tmplen, &is_utf8);
+       klen = tmplen;
+    }
+
     if (!hash)
        PERL_HASH(hash, key, klen);
 
-    if (!xhv->xhv_array)
-       Newz(505, xhv->xhv_array, ARRAY_ALLOC_BYTES(xhv->xhv_max + 1), char);
+    if (!xhv->xhv_array /* !HvARRAY(hv) */)
+       Newz(505, xhv->xhv_array /* HvARRAY(hv) */,
+            PERL_HV_ARRAY_ALLOC_BYTES(xhv->xhv_max+1 /* HvMAX(hv)+1 */),
+            char);
 
+    /* oentry = &(HvARRAY(hv))[hash & (I32) HvMAX(hv)]; */
     oentry = &((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max];
     i = 1;
 
@@ -316,34 +471,59 @@ hv_store(HV *hv, char *key, U32 klen, SV *val, register U32 hash)
            continue;
        if (HeKLEN(entry) != klen)
            continue;
-       if (memNE(HeKEY(entry),key,klen))       /* is this it? */
+       if (HeKEY(entry) != key && memNE(HeKEY(entry),key,klen))        /* is this it? */
+           continue;
+       if (HeKUTF8(entry) != (char)is_utf8)
            continue;
        SvREFCNT_dec(HeVAL(entry));
        HeVAL(entry) = val;
+       if (key != keysave)
+           Safefree(key);
        return &HeVAL(entry);
     }
 
-    entry = new_he();
+    entry = new_HE();
     if (HvSHAREKEYS(hv))
-       HeKEY_hek(entry) = share_hek(key, klen, hash);
+       HeKEY_hek(entry) = share_hek(key, is_utf8?-klen:klen, hash);
     else                                       /* gotta do the real thing */
-       HeKEY_hek(entry) = save_hek(key, klen, hash);
+       HeKEY_hek(entry) = save_hek(key, is_utf8?-klen:klen, hash);
+    if (key != keysave)
+       Safefree(key);
     HeVAL(entry) = val;
     HeNEXT(entry) = *oentry;
     *oentry = entry;
 
-    xhv->xhv_keys++;
+    xhv->xhv_keys++; /* HvKEYS(hv)++ */
     if (i) {                           /* initial entry? */
-       ++xhv->xhv_fill;
-       if (xhv->xhv_keys > xhv->xhv_max)
+       xhv->xhv_fill++; /* HvFILL(hv)++ */
+       if (xhv->xhv_keys > xhv->xhv_max /* HvKEYS(hv) > HvMAX(hv) */)
            hsplit(hv);
     }
 
     return &HeVAL(entry);
 }
 
+/*
+=for apidoc hv_store_ent
+
+Stores C<val> in a hash.  The hash key is specified as C<key>.  The C<hash>
+parameter is the precomputed hash value; if it is zero then Perl will
+compute it.  The return value is the new hash entry so created.  It will be
+NULL if the operation failed or if the value did not need to be actually
+stored within the hash (as in the case of tied hashes).  Otherwise the
+contents of the return value can be accessed using the C<He?> macros
+described here.  Note that the caller is responsible for suitably
+incrementing the reference count of C<val> before the call, and
+decrementing it if the function returned NULL.
+
+See L<perlguts/"Understanding the Magic of Tied Hashes and Arrays"> for more
+information on how to use this function on tied hashes.
+
+=cut
+*/
+
 HE *
-hv_store_ent(HV *hv, SV *keysv, SV *val, register U32 hash)
+Perl_hv_store_ent(pTHX_ HV *hv, SV *keysv, SV *val, register U32 hash)
 {
     register XPVHV* xhv;
     register char *key;
@@ -351,13 +531,14 @@ hv_store_ent(HV *hv, SV *keysv, SV *val, register U32 hash)
     register I32 i;
     register HE *entry;
     register HE **oentry;
+    bool is_utf8;
+    char *keysave;
 
     if (!hv)
        return 0;
 
     xhv = (XPVHV*)SvANY(hv);
     if (SvMAGICAL(hv)) {
-       dTHR;
        bool needs_copy;
        bool needs_store;
        hv_magic_check (hv, &needs_copy, &needs_store);
@@ -368,12 +549,12 @@ hv_store_ent(HV *hv, SV *keysv, SV *val, register U32 hash)
            keysv = sv_2mortal(newSVsv(keysv));
            mg_copy((SV*)hv, val, (char*)keysv, HEf_SVKEY);
            TAINT_IF(save_taint);
-           if (!xhv->xhv_array && !needs_store)
+           if (!xhv->xhv_array /* !HvARRAY(hv) */ && !needs_store)
                return Nullhe;
 #ifdef ENV_IS_CASELESS
-           else if (mg_find((SV*)hv,'E')) {
+           else if (mg_find((SV*)hv, PERL_MAGIC_env)) {
                key = SvPV(keysv, klen);
-               keysv = sv_2mortal(newSVpv(key,klen));
+               keysv = sv_2mortal(newSVpvn(key,klen));
                (void)strupr(SvPVX(keysv));
                hash = 0;
            }
@@ -381,14 +562,21 @@ hv_store_ent(HV *hv, SV *keysv, SV *val, register U32 hash)
        }
     }
 
-    key = SvPV(keysv, klen);
+    keysave = key = SvPV(keysv, klen);
+    is_utf8 = (SvUTF8(keysv) != 0);
+
+    if (is_utf8)
+       key = (char*)bytes_from_utf8((U8*)key, &klen, &is_utf8);
 
     if (!hash)
        PERL_HASH(hash, key, klen);
 
-    if (!xhv->xhv_array)
-       Newz(505, xhv->xhv_array, ARRAY_ALLOC_BYTES(xhv->xhv_max + 1), char);
+    if (!xhv->xhv_array /* !HvARRAY(hv) */)
+       Newz(505, xhv->xhv_array /* HvARRAY(hv) */,
+            PERL_HV_ARRAY_ALLOC_BYTES(xhv->xhv_max+1 /* HvMAX(hv)+1 */),
+            char);
 
+    /* oentry = &(HvARRAY(hv))[hash & (I32) HvMAX(hv)]; */
     oentry = &((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max];
     i = 1;
 
@@ -397,34 +585,51 @@ hv_store_ent(HV *hv, SV *keysv, SV *val, register U32 hash)
            continue;
        if (HeKLEN(entry) != klen)
            continue;
-       if (memNE(HeKEY(entry),key,klen))       /* is this it? */
+       if (HeKEY(entry) != key && memNE(HeKEY(entry),key,klen))        /* is this it? */
+           continue;
+       if (HeKUTF8(entry) != (char)is_utf8)
            continue;
        SvREFCNT_dec(HeVAL(entry));
        HeVAL(entry) = val;
+       if (key != keysave)
+           Safefree(key);
        return entry;
     }
 
-    entry = new_he();
+    entry = new_HE();
     if (HvSHAREKEYS(hv))
-       HeKEY_hek(entry) = share_hek(key, klen, hash);
+       HeKEY_hek(entry) = share_hek(key, is_utf8?-(I32)klen:klen, hash);
     else                                       /* gotta do the real thing */
-       HeKEY_hek(entry) = save_hek(key, klen, hash);
+       HeKEY_hek(entry) = save_hek(key, is_utf8?-(I32)klen:klen, hash);
+    if (key != keysave)
+       Safefree(key);
     HeVAL(entry) = val;
     HeNEXT(entry) = *oentry;
     *oentry = entry;
 
-    xhv->xhv_keys++;
+    xhv->xhv_keys++; /* HvKEYS(hv)++ */
     if (i) {                           /* initial entry? */
-       ++xhv->xhv_fill;
-       if (xhv->xhv_keys > xhv->xhv_max)
+       xhv->xhv_fill++; /* HvFILL(hv)++ */
+       if (xhv->xhv_keys > xhv->xhv_max /* HvKEYS(hv) > HvMAX(hv) */)
            hsplit(hv);
     }
 
     return entry;
 }
 
+/*
+=for apidoc hv_delete
+
+Deletes a key/value pair in the hash.  The value SV is removed from the
+hash and returned to the caller.  The C<klen> is the length of the key.
+The C<flags> value will normally be zero; if set to G_DISCARD then NULL
+will be returned.
+
+=cut
+*/
+
 SV *
-hv_delete(HV *hv, char *key, U32 klen, I32 flags)
+Perl_hv_delete(pTHX_ HV *hv, const char *key, I32 klen, I32 flags)
 {
     register XPVHV* xhv;
     register I32 i;
@@ -433,9 +638,15 @@ hv_delete(HV *hv, char *key, U32 klen, I32 flags)
     register HE **oentry;
     SV **svp;
     SV *sv;
+    bool is_utf8 = FALSE;
+    const char *keysave = key;
 
     if (!hv)
        return Nullsv;
+    if (klen < 0) {
+      klen = -klen;
+      is_utf8 = TRUE;
+    }
     if (SvRMAGICAL(hv)) {
        bool needs_copy;
        bool needs_store;
@@ -445,26 +656,35 @@ hv_delete(HV *hv, char *key, U32 klen, I32 flags)
            sv = *svp;
            mg_clear(sv);
            if (!needs_store) {
-               if (mg_find(sv, 'p')) {
-                   sv_unmagic(sv, 'p');        /* No longer an element */
+               if (mg_find(sv, PERL_MAGIC_tiedelem)) {
+                   /* No longer an element */
+                   sv_unmagic(sv, PERL_MAGIC_tiedelem);
                    return sv;
                }
                return Nullsv;          /* element cannot be deleted */
            }
 #ifdef ENV_IS_CASELESS
-           else if (mg_find((SV*)hv,'E')) {
-               sv = sv_2mortal(newSVpv(key,klen));
+           else if (mg_find((SV*)hv, PERL_MAGIC_env)) {
+               sv = sv_2mortal(newSVpvn(key,klen));
                key = strupr(SvPVX(sv));
            }
 #endif
         }
     }
     xhv = (XPVHV*)SvANY(hv);
-    if (!xhv->xhv_array)
+    if (!xhv->xhv_array /* !HvARRAY(hv) */)
        return Nullsv;
 
+    if (is_utf8) {
+       STRLEN tmplen = klen;
+       /* See the note in hv_fetch(). --jhi */
+       key = (char*)bytes_from_utf8((U8*)key, &tmplen, &is_utf8);
+       klen = tmplen;
+    }
+
     PERL_HASH(hash, key, klen);
 
+    /* oentry = &(HvARRAY(hv))[hash & (I32) HvMAX(hv)]; */
     oentry = &((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max];
     entry = *oentry;
     i = 1;
@@ -473,27 +693,46 @@ hv_delete(HV *hv, char *key, U32 klen, I32 flags)
            continue;
        if (HeKLEN(entry) != klen)
            continue;
-       if (memNE(HeKEY(entry),key,klen))       /* is this it? */
+       if (HeKEY(entry) != key && memNE(HeKEY(entry),key,klen))        /* is this it? */
            continue;
+       if (HeKUTF8(entry) != (char)is_utf8)
+           continue;
+       if (key != keysave)
+           Safefree(key);
        *oentry = HeNEXT(entry);
        if (i && !*oentry)
-           xhv->xhv_fill--;
+           xhv->xhv_fill--; /* HvFILL(hv)-- */
        if (flags & G_DISCARD)
            sv = Nullsv;
-       else
-           sv = sv_mortalcopy(HeVAL(entry));
-       if (entry == xhv->xhv_eiter)
+       else {
+           sv = sv_2mortal(HeVAL(entry));
+           HeVAL(entry) = &PL_sv_undef;
+       }
+       if (entry == xhv->xhv_eiter /* HvEITER(hv) */)
            HvLAZYDEL_on(hv);
        else
            hv_free_ent(hv, entry);
-       --xhv->xhv_keys;
+       xhv->xhv_keys--; /* HvKEYS(hv)-- */
        return sv;
     }
+    if (key != keysave)
+       Safefree(key);
     return Nullsv;
 }
 
+/*
+=for apidoc hv_delete_ent
+
+Deletes a key/value pair in the hash.  The value SV is removed from the
+hash and returned to the caller.  The C<flags> value will normally be zero;
+if set to G_DISCARD then NULL will be returned.  C<hash> can be a valid
+precomputed hash value, or 0 to ask for it to be computed.
+
+=cut
+*/
+
 SV *
-hv_delete_ent(HV *hv, SV *keysv, I32 flags, U32 hash)
+Perl_hv_delete_ent(pTHX_ HV *hv, SV *keysv, I32 flags, U32 hash)
 {
     register XPVHV* xhv;
     register I32 i;
@@ -502,7 +741,9 @@ hv_delete_ent(HV *hv, SV *keysv, I32 flags, U32 hash)
     register HE *entry;
     register HE **oentry;
     SV *sv;
-    
+    bool is_utf8;
+    char *keysave;
+
     if (!hv)
        return Nullsv;
     if (SvRMAGICAL(hv)) {
@@ -514,31 +755,37 @@ hv_delete_ent(HV *hv, SV *keysv, I32 flags, U32 hash)
            sv = HeVAL(entry);
            mg_clear(sv);
            if (!needs_store) {
-               if (mg_find(sv, 'p')) {
-                   sv_unmagic(sv, 'p');        /* No longer an element */
+               if (mg_find(sv, PERL_MAGIC_tiedelem)) {
+                   /* No longer an element */
+                   sv_unmagic(sv, PERL_MAGIC_tiedelem);
                    return sv;
                }               
                return Nullsv;          /* element cannot be deleted */
            }
 #ifdef ENV_IS_CASELESS
-           else if (mg_find((SV*)hv,'E')) {
+           else if (mg_find((SV*)hv, PERL_MAGIC_env)) {
                key = SvPV(keysv, klen);
-               keysv = sv_2mortal(newSVpv(key,klen));
+               keysv = sv_2mortal(newSVpvn(key,klen));
                (void)strupr(SvPVX(keysv));
-               hash = 0; 
+               hash = 0;
            }
 #endif
        }
     }
     xhv = (XPVHV*)SvANY(hv);
-    if (!xhv->xhv_array)
+    if (!xhv->xhv_array /* !HvARRAY(hv) */)
        return Nullsv;
 
-    key = SvPV(keysv, klen);
-    
+    keysave = key = SvPV(keysv, klen);
+    is_utf8 = (SvUTF8(keysv) != 0);
+
+    if (is_utf8)
+       key = (char*)bytes_from_utf8((U8*)key, &klen, &is_utf8);
+
     if (!hash)
        PERL_HASH(hash, key, klen);
 
+    /* oentry = &(HvARRAY(hv))[hash & (I32) HvMAX(hv)]; */
     oentry = &((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max];
     entry = *oentry;
     i = 1;
@@ -547,132 +794,227 @@ hv_delete_ent(HV *hv, SV *keysv, I32 flags, U32 hash)
            continue;
        if (HeKLEN(entry) != klen)
            continue;
-       if (memNE(HeKEY(entry),key,klen))       /* is this it? */
+       if (HeKEY(entry) != key && memNE(HeKEY(entry),key,klen))        /* is this it? */
            continue;
+       if (HeKUTF8(entry) != (char)is_utf8)
+           continue;
+       if (key != keysave)
+           Safefree(key);
        *oentry = HeNEXT(entry);
        if (i && !*oentry)
-           xhv->xhv_fill--;
+           xhv->xhv_fill--; /* HvFILL(hv)-- */
        if (flags & G_DISCARD)
            sv = Nullsv;
-       else
-           sv = sv_mortalcopy(HeVAL(entry));
-       if (entry == xhv->xhv_eiter)
+       else {
+           sv = sv_2mortal(HeVAL(entry));
+           HeVAL(entry) = &PL_sv_undef;
+       }
+       if (entry == xhv->xhv_eiter /* HvEITER(hv) */)
            HvLAZYDEL_on(hv);
        else
            hv_free_ent(hv, entry);
-       --xhv->xhv_keys;
+       xhv->xhv_keys--; /* HvKEYS(hv)-- */
        return sv;
     }
+    if (key != keysave)
+       Safefree(key);
     return Nullsv;
 }
 
+/*
+=for apidoc hv_exists
+
+Returns a boolean indicating whether the specified hash key exists.  The
+C<klen> is the length of the key.
+
+=cut
+*/
+
 bool
-hv_exists(HV *hv, char *key, U32 klen)
+Perl_hv_exists(pTHX_ HV *hv, const char *key, I32 klen)
 {
     register XPVHV* xhv;
     register U32 hash;
     register HE *entry;
     SV *sv;
+    bool is_utf8 = FALSE;
+    const char *keysave = key;
 
     if (!hv)
        return 0;
 
+    if (klen < 0) {
+      klen = -klen;
+      is_utf8 = TRUE;
+    }
+
     if (SvRMAGICAL(hv)) {
-       if (mg_find((SV*)hv,'P')) {
-           dTHR;
+       if (mg_find((SV*)hv, PERL_MAGIC_tied) || SvGMAGICAL((SV*)hv)) {
            sv = sv_newmortal();
-           mg_copy((SV*)hv, sv, key, klen); 
-           magic_existspack(sv, mg_find(sv, 'p'));
+           mg_copy((SV*)hv, sv, key, klen);
+           magic_existspack(sv, mg_find(sv, PERL_MAGIC_tiedelem));
            return SvTRUE(sv);
        }
 #ifdef ENV_IS_CASELESS
-       else if (mg_find((SV*)hv,'E')) {
-           sv = sv_2mortal(newSVpv(key,klen));
+       else if (mg_find((SV*)hv, PERL_MAGIC_env)) {
+           sv = sv_2mortal(newSVpvn(key,klen));
            key = strupr(SvPVX(sv));
        }
 #endif
     }
 
     xhv = (XPVHV*)SvANY(hv);
-    if (!xhv->xhv_array)
-       return 0; 
+#ifndef DYNAMIC_ENV_FETCH
+    if (!xhv->xhv_array /* !HvARRAY(hv) */)
+       return 0;
+#endif
+
+    if (is_utf8) {
+       STRLEN tmplen = klen;
+       /* See the note in hv_fetch(). --jhi */
+       key = (char*)bytes_from_utf8((U8*)key, &tmplen, &is_utf8);
+       klen = tmplen;
+    }
 
     PERL_HASH(hash, key, klen);
 
+#ifdef DYNAMIC_ENV_FETCH
+    if (!xhv->xhv_array /* !HvARRAY(hv) */) entry = Null(HE*);
+    else
+#endif
+    /* entry = (HvARRAY(hv))[hash & (I32) HvMAX(hv)]; */
     entry = ((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max];
     for (; entry; entry = HeNEXT(entry)) {
        if (HeHASH(entry) != hash)              /* strings can't be equal */
            continue;
        if (HeKLEN(entry) != klen)
            continue;
-       if (memNE(HeKEY(entry),key,klen))       /* is this it? */
+       if (HeKEY(entry) != key && memNE(HeKEY(entry),key,klen))        /* is this it? */
+           continue;
+       if (HeKUTF8(entry) != (char)is_utf8)
            continue;
+       if (key != keysave)
+           Safefree(key);
        return TRUE;
     }
+#ifdef DYNAMIC_ENV_FETCH  /* is it out there? */
+    if (SvRMAGICAL((SV*)hv) && mg_find((SV*)hv, PERL_MAGIC_env)) {
+       unsigned long len;
+       char *env = PerlEnv_ENVgetenv_len(key,&len);
+       if (env) {
+           sv = newSVpvn(env,len);
+           SvTAINTED_on(sv);
+           (void)hv_store(hv,key,klen,sv,hash);
+           return TRUE;
+       }
+    }
+#endif
+    if (key != keysave)
+       Safefree(key);
     return FALSE;
 }
 
 
+/*
+=for apidoc hv_exists_ent
+
+Returns a boolean indicating whether the specified hash key exists. C<hash>
+can be a valid precomputed hash value, or 0 to ask for it to be
+computed.
+
+=cut
+*/
+
 bool
-hv_exists_ent(HV *hv, SV *keysv, U32 hash)
+Perl_hv_exists_ent(pTHX_ HV *hv, SV *keysv, U32 hash)
 {
     register XPVHV* xhv;
     register char *key;
     STRLEN klen;
     register HE *entry;
     SV *sv;
+    bool is_utf8;
+    char *keysave;
 
     if (!hv)
        return 0;
 
     if (SvRMAGICAL(hv)) {
-       if (mg_find((SV*)hv,'P')) {
-           dTHR;               /* just for SvTRUE */
+       if (mg_find((SV*)hv, PERL_MAGIC_tied) || SvGMAGICAL((SV*)hv)) {
+           SV* svret = sv_newmortal();
            sv = sv_newmortal();
            keysv = sv_2mortal(newSVsv(keysv));
-           mg_copy((SV*)hv, sv, (char*)keysv, HEf_SVKEY); 
-           magic_existspack(sv, mg_find(sv, 'p'));
-           return SvTRUE(sv);
+           mg_copy((SV*)hv, sv, (char*)keysv, HEf_SVKEY);
+           magic_existspack(svret, mg_find(sv, PERL_MAGIC_tiedelem));
+           return SvTRUE(svret);
        }
 #ifdef ENV_IS_CASELESS
-       else if (mg_find((SV*)hv,'E')) {
+       else if (mg_find((SV*)hv, PERL_MAGIC_env)) {
            key = SvPV(keysv, klen);
-           keysv = sv_2mortal(newSVpv(key,klen));
+           keysv = sv_2mortal(newSVpvn(key,klen));
            (void)strupr(SvPVX(keysv));
-           hash = 0; 
+           hash = 0;
        }
 #endif
     }
 
     xhv = (XPVHV*)SvANY(hv);
-    if (!xhv->xhv_array)
-       return 0; 
+#ifndef DYNAMIC_ENV_FETCH
+    if (!xhv->xhv_array /* !HvARRAY(hv) */)
+       return 0;
+#endif
 
-    key = SvPV(keysv, klen);
+    keysave = key = SvPV(keysv, klen);
+    is_utf8 = (SvUTF8(keysv) != 0);
+    if (is_utf8)
+       key = (char*)bytes_from_utf8((U8*)key, &klen, &is_utf8);
     if (!hash)
        PERL_HASH(hash, key, klen);
 
+#ifdef DYNAMIC_ENV_FETCH
+    if (!xhv->xhv_array /* !HvARRAY(hv) */) entry = Null(HE*);
+    else
+#endif
+    /* entry = (HvARRAY(hv))[hash & (I32) HvMAX(hv)]; */
     entry = ((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max];
     for (; entry; entry = HeNEXT(entry)) {
        if (HeHASH(entry) != hash)              /* strings can't be equal */
            continue;
        if (HeKLEN(entry) != klen)
            continue;
-       if (memNE(HeKEY(entry),key,klen))       /* is this it? */
+       if (HeKEY(entry) != key && memNE(HeKEY(entry),key,klen))        /* is this it? */
+           continue;
+       if (HeKUTF8(entry) != (char)is_utf8)
            continue;
+       if (key != keysave)
+           Safefree(key);
        return TRUE;
     }
+#ifdef DYNAMIC_ENV_FETCH  /* is it out there? */
+    if (SvRMAGICAL((SV*)hv) && mg_find((SV*)hv, PERL_MAGIC_env)) {
+       unsigned long len;
+       char *env = PerlEnv_ENVgetenv_len(key,&len);
+       if (env) {
+           sv = newSVpvn(env,len);
+           SvTAINTED_on(sv);
+           (void)hv_store_ent(hv,keysv,sv,hash);
+           return TRUE;
+       }
+    }
+#endif
+    if (key != keysave)
+       Safefree(key);
     return FALSE;
 }
 
 STATIC void
-hsplit(HV *hv)
+S_hsplit(pTHX_ HV *hv)
 {
     register XPVHV* xhv = (XPVHV*)SvANY(hv);
-    I32 oldsize = (I32) xhv->xhv_max + 1; /* sic(k) */
+    I32 oldsize = (I32) xhv->xhv_max+1; /* HvMAX(hv)+1 (sick) */
     register I32 newsize = oldsize * 2;
     register I32 i;
-    register char *a = xhv->xhv_array;
+    register char *a = xhv->xhv_array; /* HvARRAY(hv) */
     register HE **aep;
     register HE **bep;
     register HE *entry;
@@ -680,30 +1022,30 @@ hsplit(HV *hv)
 
     PL_nomemok = TRUE;
 #if defined(STRANGE_MALLOC) || defined(MYMALLOC)
-    Renew(a, ARRAY_ALLOC_BYTES(newsize), char);
+    Renew(a, PERL_HV_ARRAY_ALLOC_BYTES(newsize), char);
     if (!a) {
       PL_nomemok = FALSE;
       return;
     }
 #else
-#define MALLOC_OVERHEAD 16
-    New(2, a, ARRAY_ALLOC_BYTES(newsize), char);
+    New(2, a, PERL_HV_ARRAY_ALLOC_BYTES(newsize), char);
     if (!a) {
       PL_nomemok = FALSE;
       return;
     }
-    Copy(xhv->xhv_array, a, oldsize * sizeof(HE*), char);
+    Copy(xhv->xhv_array /* HvARRAY(hv) */, a, oldsize * sizeof(HE*), char);
     if (oldsize >= 64) {
-       offer_nice_chunk(xhv->xhv_array, ARRAY_ALLOC_BYTES(oldsize));
+       offer_nice_chunk(xhv->xhv_array /* HvARRAY(hv) */,
+                       PERL_HV_ARRAY_ALLOC_BYTES(oldsize));
     }
     else
-       Safefree(xhv->xhv_array);
+       Safefree(xhv->xhv_array /* HvARRAY(hv) */);
 #endif
 
     PL_nomemok = FALSE;
     Zero(&a[oldsize * sizeof(HE*)], (newsize-oldsize) * sizeof(HE*), char);    /* zero 2nd half*/
-    xhv->xhv_max = --newsize;
-    xhv->xhv_array = a;
+    xhv->xhv_max = --newsize;  /* HvMAX(hv) = --newsize */
+    xhv->xhv_array = a;                /* HvARRAY(hv) = a */
     aep = (HE**)a;
 
     for (i=0; i<oldsize; i++,aep++) {
@@ -715,7 +1057,7 @@ hsplit(HV *hv)
                *oentry = HeNEXT(entry);
                HeNEXT(entry) = *bep;
                if (!*bep)
-                   xhv->xhv_fill++;
+                   xhv->xhv_fill++; /* HvFILL(hv)++ */
                *bep = entry;
                continue;
            }
@@ -723,15 +1065,15 @@ hsplit(HV *hv)
                oentry = &HeNEXT(entry);
        }
        if (!*aep)                              /* everything moved */
-           xhv->xhv_fill--;
+           xhv->xhv_fill--; /* HvFILL(hv)-- */
     }
 }
 
 void
-hv_ksplit(HV *hv, IV newmax)
+Perl_hv_ksplit(pTHX_ HV *hv, IV newmax)
 {
     register XPVHV* xhv = (XPVHV*)SvANY(hv);
-    I32 oldsize = (I32) xhv->xhv_max + 1; /* sic(k) */
+    I32 oldsize = (I32) xhv->xhv_max+1; /* HvMAX(hv)+1 (sick) */
     register I32 newsize;
     register I32 i;
     register I32 j;
@@ -751,37 +1093,38 @@ hv_ksplit(HV *hv, IV newmax)
     if (newsize < newmax)
        return;                                 /* overflow detection */
 
-    a = xhv->xhv_array;
+    a = xhv->xhv_array; /* HvARRAY(hv) */
     if (a) {
        PL_nomemok = TRUE;
 #if defined(STRANGE_MALLOC) || defined(MYMALLOC)
-       Renew(a, ARRAY_ALLOC_BYTES(newsize), char);
+       Renew(a, PERL_HV_ARRAY_ALLOC_BYTES(newsize), char);
         if (!a) {
          PL_nomemok = FALSE;
          return;
        }
 #else
-       New(2, a, ARRAY_ALLOC_BYTES(newsize), char);
+       New(2, a, PERL_HV_ARRAY_ALLOC_BYTES(newsize), char);
         if (!a) {
          PL_nomemok = FALSE;
          return;
        }
-       Copy(xhv->xhv_array, a, oldsize * sizeof(HE*), char);
+       Copy(xhv->xhv_array /* HvARRAY(hv) */, a, oldsize * sizeof(HE*), char);
        if (oldsize >= 64) {
-           offer_nice_chunk(xhv->xhv_array, ARRAY_ALLOC_BYTES(oldsize));
+           offer_nice_chunk(xhv->xhv_array /* HvARRAY(hv) */,
+                           PERL_HV_ARRAY_ALLOC_BYTES(oldsize));
        }
        else
-           Safefree(xhv->xhv_array);
+           Safefree(xhv->xhv_array /* HvARRAY(hv) */);
 #endif
        PL_nomemok = FALSE;
        Zero(&a[oldsize * sizeof(HE*)], (newsize-oldsize) * sizeof(HE*), char); /* zero 2nd half*/
     }
     else {
-       Newz(0, a, ARRAY_ALLOC_BYTES(newsize), char);
+       Newz(0, a, PERL_HV_ARRAY_ALLOC_BYTES(newsize), char);
     }
-    xhv->xhv_max = --newsize;
-    xhv->xhv_array = a;
-    if (!xhv->xhv_fill)                                /* skip rest if no entries */
+    xhv->xhv_max = --newsize;  /* HvMAX(hv) = --newsize */
+    xhv->xhv_array = a;        /* HvARRAY(hv) = a */
+    if (!xhv->xhv_fill /* !HvFILL(hv) */)      /* skip rest if no entries */
        return;
 
     aep = (HE**)a;
@@ -793,7 +1136,7 @@ hv_ksplit(HV *hv, IV newmax)
                j -= i;
                *oentry = HeNEXT(entry);
                if (!(HeNEXT(entry) = aep[j]))
-                   xhv->xhv_fill++;
+                   xhv->xhv_fill++; /* HvFILL(hv)++ */
                aep[j] = entry;
                continue;
            }
@@ -801,12 +1144,20 @@ hv_ksplit(HV *hv, IV newmax)
                oentry = &HeNEXT(entry);
        }
        if (!*aep)                              /* everything moved */
-           xhv->xhv_fill--;
+           xhv->xhv_fill--; /* HvFILL(hv)-- */
     }
 }
 
+/*
+=for apidoc newHV
+
+Creates a new HV.  The reference count is set to 1.
+
+=cut
+*/
+
 HV *
-newHV(void)
+Perl_newHV(pTHX)
 {
     register HV *hv;
     register XPVHV* xhv;
@@ -816,57 +1167,92 @@ newHV(void)
     xhv = (XPVHV*)SvANY(hv);
     SvPOK_off(hv);
     SvNOK_off(hv);
-#ifndef NODEFAULT_SHAREKEYS    
+#ifndef NODEFAULT_SHAREKEYS
     HvSHAREKEYS_on(hv);         /* key-sharing on by default */
-#endif    
-    xhv->xhv_max = 7;          /* start with 8 buckets */
-    xhv->xhv_fill = 0;
-    xhv->xhv_pmroot = 0;
+#endif
+    xhv->xhv_max    = 7;       /* HvMAX(hv) = 7 (start with 8 buckets) */
+    xhv->xhv_fill   = 0;       /* HvFILL(hv) = 0 */
+    xhv->xhv_pmroot = 0;       /* HvPMROOT(hv) = 0 */
     (void)hv_iterinit(hv);     /* so each() will start off right */
     return hv;
 }
 
 HV *
-newHVhv(HV *ohv)
+Perl_newHVhv(pTHX_ HV *ohv)
 {
-    register HV *hv;
-    register XPVHV* xhv;
-    STRLEN hv_max = ohv ? HvMAX(ohv) : 0;
-    STRLEN hv_fill = ohv ? HvFILL(ohv) : 0;
-
-    hv = newHV();
-    while (hv_max && hv_max + 1 >= hv_fill * 2)
-       hv_max = hv_max / 2;    /* Is always 2^n-1 */
-    ((XPVHV*)SvANY(hv))->xhv_max = hv_max;
-    if (!hv_fill)
+    HV *hv = newHV();
+    STRLEN hv_max, hv_fill;
+
+    if (!ohv || (hv_fill = HvFILL(ohv)) == 0)
        return hv;
+    hv_max = HvMAX(ohv);
+
+    if (!SvMAGICAL((SV *)ohv)) {
+       /* It's an ordinary hash, so copy it fast. AMS 20010804 */
+       int i, shared = !!HvSHAREKEYS(ohv);
+       HE **ents, **oents = (HE **)HvARRAY(ohv);
+       char *a;
+       New(0, a, PERL_HV_ARRAY_ALLOC_BYTES(hv_max+1), char);
+       ents = (HE**)a;
+
+       /* In each bucket... */
+       for (i = 0; i <= hv_max; i++) {
+           HE *prev = NULL, *ent = NULL, *oent = oents[i];
+
+           if (!oent) {
+               ents[i] = NULL;
+               continue;
+           }
 
-#if 0
-    if (!SvRMAGICAL(ohv) || !mg_find((SV*)ohv,'P')) {
-       /* Quick way ???*/
-    } 
-    else 
-#endif
-    {
+           /* Copy the linked list of entries. */
+           for (oent = oents[i]; oent; oent = HeNEXT(oent)) {
+               U32 hash   = HeHASH(oent);
+               char *key  = HeKEY(oent);
+               STRLEN len = HeKLEN_UTF8(oent);
+
+               ent = new_HE();
+               HeVAL(ent)     = newSVsv(HeVAL(oent));
+               HeKEY_hek(ent) = shared ? share_hek(key, len, hash)
+                                       :  save_hek(key, len, hash);
+               if (prev)
+                   HeNEXT(prev) = ent;
+               else
+                   ents[i] = ent;
+               prev = ent;
+               HeNEXT(ent) = NULL;
+           }
+       }
+
+       HvMAX(hv)   = hv_max;
+       HvFILL(hv)  = hv_fill;
+       HvKEYS(hv)  = HvKEYS(ohv);
+       HvARRAY(hv) = ents;
+    }
+    else {
+       /* Iterate over ohv, copying keys and values one at a time. */
        HE *entry;
-       I32 hv_riter = HvRITER(ohv);    /* current root of iterator */
-       HE *hv_eiter = HvEITER(ohv);    /* current entry of iterator */
-       
-       /* Slow way */
-       hv_iterinit(hv);
-       while (entry = hv_iternext(ohv)) {
-           hv_store(hv, HeKEY(entry), HeKLEN(entry), 
-                    SvREFCNT_inc(HeVAL(entry)), HeHASH(entry));
+       I32 riter = HvRITER(ohv);
+       HE *eiter = HvEITER(ohv);
+
+       /* Can we use fewer buckets? (hv_max is always 2^n-1) */
+       while (hv_max && hv_max + 1 >= hv_fill * 2)
+           hv_max = hv_max / 2;
+       HvMAX(hv) = hv_max;
+
+       hv_iterinit(ohv);
+       while ((entry = hv_iternext(ohv))) {
+           hv_store(hv, HeKEY(entry), HeKLEN_UTF8(entry),
+                    newSVsv(HeVAL(entry)), HeHASH(entry));
        }
-       HvRITER(ohv) = hv_riter;
-       HvEITER(ohv) = hv_eiter;
+       HvRITER(ohv) = riter;
+       HvEITER(ohv) = eiter;
     }
-    
+
     return hv;
 }
 
 void
-hv_free_ent(HV *hv, register HE *entry)
+Perl_hv_free_ent(pTHX_ HV *hv, register HE *entry)
 {
     SV *val;
 
@@ -884,11 +1270,11 @@ hv_free_ent(HV *hv, register HE *entry)
        unshare_hek(HeKEY_hek(entry));
     else
        Safefree(HeKEY_hek(entry));
-    del_he(entry);
+    del_HE(entry);
 }
 
 void
-hv_delayfree_ent(HV *hv, register HE *entry)
+Perl_hv_delayfree_ent(pTHX_ HV *hv, register HE *entry)
 {
     if (!entry)
        return;
@@ -903,28 +1289,37 @@ hv_delayfree_ent(HV *hv, register HE *entry)
        unshare_hek(HeKEY_hek(entry));
     else
        Safefree(HeKEY_hek(entry));
-    del_he(entry);
+    del_HE(entry);
 }
 
+/*
+=for apidoc hv_clear
+
+Clears a hash, making it empty.
+
+=cut
+*/
+
 void
-hv_clear(HV *hv)
+Perl_hv_clear(pTHX_ HV *hv)
 {
     register XPVHV* xhv;
     if (!hv)
        return;
     xhv = (XPVHV*)SvANY(hv);
     hfreeentries(hv);
-    xhv->xhv_fill = 0;
-    xhv->xhv_keys = 0;
-    if (xhv->xhv_array)
-       (void)memzero(xhv->xhv_array, (xhv->xhv_max + 1) * sizeof(HE*));
+    xhv->xhv_fill = 0; /* HvFILL(hv) = 0 */
+    xhv->xhv_keys = 0; /* HvKEYS(hv) = 0 */
+    if (xhv->xhv_array /* HvARRAY(hv) */)
+       (void)memzero(xhv->xhv_array /* HvARRAY(hv) */,
+                     (xhv->xhv_max+1 /* HvMAX(hv)+1 */) * sizeof(HE*));
 
     if (SvRMAGICAL(hv))
-       mg_clear((SV*)hv); 
+       mg_clear((SV*)hv);
 }
 
 STATIC void
-hfreeentries(HV *hv)
+S_hfreeentries(pTHX_ HV *hv)
 {
     register HE **array;
     register HE *entry;
@@ -951,58 +1346,85 @@ hfreeentries(HV *hv)
            if (++riter > max)
                break;
            entry = array[riter];
-       } 
+       }
     }
     (void)hv_iterinit(hv);
 }
 
+/*
+=for apidoc hv_undef
+
+Undefines the hash.
+
+=cut
+*/
+
 void
-hv_undef(HV *hv)
+Perl_hv_undef(pTHX_ HV *hv)
 {
     register XPVHV* xhv;
     if (!hv)
        return;
     xhv = (XPVHV*)SvANY(hv);
     hfreeentries(hv);
-    Safefree(xhv->xhv_array);
+    Safefree(xhv->xhv_array /* HvARRAY(hv) */);
     if (HvNAME(hv)) {
        Safefree(HvNAME(hv));
        HvNAME(hv) = 0;
     }
-    xhv->xhv_array = 0;
-    xhv->xhv_max = 7;          /* it's a normal hash */
-    xhv->xhv_fill = 0;
-    xhv->xhv_keys = 0;
+    xhv->xhv_max   = 7;        /* HvMAX(hv) = 7 (it's a normal hash) */
+    xhv->xhv_array = 0;        /* HvARRAY(hv) = 0 */
+    xhv->xhv_fill  = 0;        /* HvFILL(hv) = 0 */
+    xhv->xhv_keys  = 0;        /* HvKEYS(hv) = 0 */
 
     if (SvRMAGICAL(hv))
-       mg_clear((SV*)hv); 
+       mg_clear((SV*)hv);
 }
 
+/*
+=for apidoc hv_iterinit
+
+Prepares a starting point to traverse a hash table.  Returns the number of
+keys in the hash (i.e. the same as C<HvKEYS(tb)>).  The return value is
+currently only meaningful for hashes without tie magic.
+
+NOTE: Before version 5.004_65, C<hv_iterinit> used to return the number of
+hash buckets that happen to be in use.  If you still need that esoteric
+value, you can get it through the macro C<HvFILL(tb)>.
+
+=cut
+*/
+
 I32
-hv_iterinit(HV *hv)
+Perl_hv_iterinit(pTHX_ HV *hv)
 {
     register XPVHV* xhv;
     HE *entry;
 
     if (!hv)
-       croak("Bad hash");
+       Perl_croak(aTHX_ "Bad hash");
     xhv = (XPVHV*)SvANY(hv);
-    entry = xhv->xhv_eiter;
-#ifdef DYNAMIC_ENV_FETCH  /* set up %ENV for iteration */
-    if (HvNAME(hv) && strEQ(HvNAME(hv), ENV_HV_NAME))
-       prime_env_iter();
-#endif
+    entry = xhv->xhv_eiter; /* HvEITER(hv) */
     if (entry && HvLAZYDEL(hv)) {      /* was deleted earlier? */
        HvLAZYDEL_off(hv);
        hv_free_ent(hv, entry);
     }
-    xhv->xhv_riter = -1;
-    xhv->xhv_eiter = Null(HE*);
-    return xhv->xhv_keys;      /* used to be xhv->xhv_fill before 5.004_65 */
+    xhv->xhv_riter = -1;       /* HvRITER(hv) = -1 */
+    xhv->xhv_eiter = Null(HE*); /* HvEITER(hv) = Null(HE*) */
+    /* used to be xhv->xhv_fill before 5.004_65 */
+    return xhv->xhv_keys; /* HvKEYS(hv) */
 }
 
+/*
+=for apidoc hv_iternext
+
+Returns entries from a hash iterator.  See C<hv_iterinit>.
+
+=cut
+*/
+
 HE *
-hv_iternext(HV *hv)
+Perl_hv_iternext(pTHX_ HV *hv)
 {
     register XPVHV* xhv;
     register HE *entry;
@@ -1010,11 +1432,11 @@ hv_iternext(HV *hv)
     MAGIC* mg;
 
     if (!hv)
-       croak("Bad hash");
+       Perl_croak(aTHX_ "Bad hash");
     xhv = (XPVHV*)SvANY(hv);
-    oldentry = entry = xhv->xhv_eiter;
+    oldentry = entry = xhv->xhv_eiter; /* HvEITER(hv) */
 
-    if (SvRMAGICAL(hv) && (mg = mg_find((SV*)hv,'P'))) {
+    if ((mg = SvTIED_mg((SV*)hv, PERL_MAGIC_tied))) {
        SV *key = sv_newmortal();
        if (entry) {
            sv_setsv(key, HeSVKEY_force(entry));
@@ -1024,7 +1446,8 @@ hv_iternext(HV *hv)
            char *k;
            HEK *hek;
 
-           xhv->xhv_eiter = entry = new_he();  /* one HE per MAGICAL hash */
+           /* one HE per MAGICAL hash */
+           xhv->xhv_eiter = entry = new_HE(); /* HvEITER(hv) = new_HE() */
            Zero(entry, 1, HE);
            Newz(54, k, HEK_BASESIZE + sizeof(SV*), char);
            hek = (HEK*)k;
@@ -1040,21 +1463,28 @@ hv_iternext(HV *hv)
        if (HeVAL(entry))
            SvREFCNT_dec(HeVAL(entry));
        Safefree(HeKEY_hek(entry));
-       del_he(entry);
-       xhv->xhv_eiter = Null(HE*);
+       del_HE(entry);
+       xhv->xhv_eiter = Null(HE*); /* HvEITER(hv) = Null(HE*) */
        return Null(HE*);
     }
+#ifdef DYNAMIC_ENV_FETCH  /* set up %ENV for iteration */
+    if (!entry && SvRMAGICAL((SV*)hv) && mg_find((SV*)hv, PERL_MAGIC_env))
+       prime_env_iter();
+#endif
 
-    if (!xhv->xhv_array)
-       Newz(506,xhv->xhv_array, ARRAY_ALLOC_BYTES(xhv->xhv_max + 1), char);
+    if (!xhv->xhv_array /* !HvARRAY(hv) */)
+       Newz(506, xhv->xhv_array /* HvARRAY(hv) */,
+            PERL_HV_ARRAY_ALLOC_BYTES(xhv->xhv_max+1 /* HvMAX(hv)+1 */),
+            char);
     if (entry)
        entry = HeNEXT(entry);
     while (!entry) {
-       ++xhv->xhv_riter;
-       if (xhv->xhv_riter > xhv->xhv_max) {
-           xhv->xhv_riter = -1;
+       xhv->xhv_riter++; /* HvRITER(hv)++ */
+       if (xhv->xhv_riter > xhv->xhv_max /* HvRITER(hv) > HvMAX(hv) */) {
+           xhv->xhv_riter = -1; /* HvRITER(hv) = -1 */
            break;
        }
+       /* entry = (HvARRAY(hv))[HvRITER(hv)]; */
        entry = ((HE**)xhv->xhv_array)[xhv->xhv_riter];
     }
 
@@ -1063,12 +1493,21 @@ hv_iternext(HV *hv)
        hv_free_ent(hv, oldentry);
     }
 
-    xhv->xhv_eiter = entry;
+    xhv->xhv_eiter = entry; /* HvEITER(hv) = entry */
     return entry;
 }
 
+/*
+=for apidoc hv_iterkey
+
+Returns the key from the current position of the hash iterator.  See
+C<hv_iterinit>.
+
+=cut
+*/
+
 char *
-hv_iterkey(register HE *entry, I32 *retlen)
+Perl_hv_iterkey(pTHX_ register HE *entry, I32 *retlen)
 {
     if (HeKLEN(entry) == HEf_SVKEY) {
        STRLEN len;
@@ -1083,21 +1522,40 @@ hv_iterkey(register HE *entry, I32 *retlen)
 }
 
 /* unlike hv_iterval(), this always returns a mortal copy of the key */
+/*
+=for apidoc hv_iterkeysv
+
+Returns the key as an C<SV*> from the current position of the hash
+iterator.  The return value will always be a mortal copy of the key.  Also
+see C<hv_iterinit>.
+
+=cut
+*/
+
 SV *
-hv_iterkeysv(register HE *entry)
+Perl_hv_iterkeysv(pTHX_ register HE *entry)
 {
     if (HeKLEN(entry) == HEf_SVKEY)
        return sv_mortalcopy(HeKEY_sv(entry));
     else
-       return sv_2mortal(newSVpv((HeKLEN(entry) ? HeKEY(entry) : ""),
-                                 HeKLEN(entry)));
+       return sv_2mortal(newSVpvn_share((HeKLEN(entry) ? HeKEY(entry) : ""),
+                                        HeKLEN_UTF8(entry), HeHASH(entry)));
 }
 
+/*
+=for apidoc hv_iterval
+
+Returns the value from the current position of the hash iterator.  See
+C<hv_iterkey>.
+
+=cut
+*/
+
 SV *
-hv_iterval(HV *hv, register HE *entry)
+Perl_hv_iterval(pTHX_ HV *hv, register HE *entry)
 {
     if (SvRMAGICAL(hv)) {
-       if (mg_find((SV*)hv,'P')) {
+       if (mg_find((SV*)hv, PERL_MAGIC_tied)) {
            SV* sv = sv_newmortal();
            if (HeKLEN(entry) == HEf_SVKEY)
                mg_copy((SV*)hv, sv, (char*)HeKEY_sv(entry), HEf_SVKEY);
@@ -1108,8 +1566,17 @@ hv_iterval(HV *hv, register HE *entry)
     return HeVAL(entry);
 }
 
+/*
+=for apidoc hv_iternextsv
+
+Performs an C<hv_iternext>, C<hv_iterkey>, and C<hv_iterval> in one
+operation.
+
+=cut
+*/
+
 SV *
-hv_iternextsv(HV *hv, char **key, I32 *retlen)
+Perl_hv_iternextsv(pTHX_ HV *hv, char **key, I32 *retlen)
 {
     HE *he;
     if ( (he = hv_iternext(hv)) == NULL)
@@ -1118,14 +1585,22 @@ hv_iternextsv(HV *hv, char **key, I32 *retlen)
     return hv_iterval(hv, he);
 }
 
+/*
+=for apidoc hv_magic
+
+Adds magic to a hash.  See C<sv_magic>.
+
+=cut
+*/
+
 void
-hv_magic(HV *hv, GV *gv, int how)
+Perl_hv_magic(pTHX_ HV *hv, GV *gv, int how)
 {
     sv_magic((SV*)hv, (SV*)gv, how, Nullch, 0);
 }
 
 char*  
-sharepvn(char *sv, I32 len, U32 hash)
+Perl_sharepvn(pTHX_ const char *sv, I32 len, U32 hash)
 {
     return HEK_KEY(share_hek(sv, len, hash));
 }
@@ -1134,43 +1609,59 @@ sharepvn(char *sv, I32 len, U32 hash)
  * len and hash must both be valid for str.
  */
 void
-unsharepvn(char *str, I32 len, U32 hash)
+Perl_unsharepvn(pTHX_ const char *str, I32 len, U32 hash)
 {
     register XPVHV* xhv;
     register HE *entry;
     register HE **oentry;
     register I32 i = 1;
     I32 found = 0;
-    
+    bool is_utf8 = FALSE;
+    const char *save = str;
+
+    if (len < 0) {
+      STRLEN tmplen = -len;
+      is_utf8 = TRUE;
+      /* See the note in hv_fetch(). --jhi */
+      str = (char*)bytes_from_utf8((U8*)str, &tmplen, &is_utf8);
+      len = tmplen;
+    }
+
     /* what follows is the moral equivalent of:
-    if ((Svp = hv_fetch(strtab, tmpsv, FALSE, hash))) {
+    if ((Svp = hv_fetch(PL_strtab, tmpsv, FALSE, hash))) {
        if (--*Svp == Nullsv)
-           hv_delete(strtab, str, len, G_DISCARD, hash);
+           hv_delete(PL_strtab, str, len, G_DISCARD, hash);
     } */
     xhv = (XPVHV*)SvANY(PL_strtab);
     /* assert(xhv_array != 0) */
+    LOCK_STRTAB_MUTEX;
+    /* oentry = &(HvARRAY(hv))[hash & (I32) HvMAX(hv)]; */
     oentry = &((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max];
     for (entry = *oentry; entry; i=0, oentry = &HeNEXT(entry), entry = *oentry) {
        if (HeHASH(entry) != hash)              /* strings can't be equal */
            continue;
        if (HeKLEN(entry) != len)
            continue;
-       if (memNE(HeKEY(entry),str,len))        /* is this it? */
+       if (HeKEY(entry) != str && memNE(HeKEY(entry),str,len)) /* is this it? */
+           continue;
+       if (HeKUTF8(entry) != (char)is_utf8)
            continue;
        found = 1;
        if (--HeVAL(entry) == Nullsv) {
            *oentry = HeNEXT(entry);
            if (i && !*oentry)
-               xhv->xhv_fill--;
+               xhv->xhv_fill--; /* HvFILL(hv)-- */
            Safefree(HeKEY_hek(entry));
-           del_he(entry);
-           --xhv->xhv_keys;
+           del_HE(entry);
+           xhv->xhv_keys--; /* HvKEYS(hv)-- */
        }
        break;
     }
-    
-    if (!found)
-       warn("Attempt to free non-existent shared string");    
+    UNLOCK_STRTAB_MUTEX;
+    if (str != save)
+       Safefree(str);
+    if (!found && ckWARN_d(WARN_INTERNAL))
+       Perl_warner(aTHX_ WARN_INTERNAL, "Attempt to free non-existent shared string '%s'",str);
 }
 
 /* get a (constant) string ptr from the global string table
@@ -1178,49 +1669,63 @@ unsharepvn(char *str, I32 len, U32 hash)
  * len and hash must both be valid for str.
  */
 HEK *
-share_hek(char *str, I32 len, register U32 hash)
+Perl_share_hek(pTHX_ const char *str, I32 len, register U32 hash)
 {
     register XPVHV* xhv;
     register HE *entry;
     register HE **oentry;
     register I32 i = 1;
     I32 found = 0;
+    bool is_utf8 = FALSE;
+    const char *save = str;
+
+    if (len < 0) {
+      STRLEN tmplen = -len;
+      is_utf8 = TRUE;
+      /* See the note in hv_fetch(). --jhi */
+      str = (char*)bytes_from_utf8((U8*)str, &tmplen, &is_utf8);
+      len = tmplen;
+    }
 
     /* what follows is the moral equivalent of:
-       
-    if (!(Svp = hv_fetch(strtab, str, len, FALSE)))
-       hv_store(strtab, str, len, Nullsv, hash);
+
+    if (!(Svp = hv_fetch(PL_strtab, str, len, FALSE)))
+       hv_store(PL_strtab, str, len, Nullsv, hash);
     */
     xhv = (XPVHV*)SvANY(PL_strtab);
     /* assert(xhv_array != 0) */
+    LOCK_STRTAB_MUTEX;
+    /* oentry = &(HvARRAY(hv))[hash & (I32) HvMAX(hv)]; */
     oentry = &((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max];
     for (entry = *oentry; entry; i=0, entry = HeNEXT(entry)) {
        if (HeHASH(entry) != hash)              /* strings can't be equal */
            continue;
        if (HeKLEN(entry) != len)
            continue;
-       if (memNE(HeKEY(entry),str,len))        /* is this it? */
+       if (HeKEY(entry) != str && memNE(HeKEY(entry),str,len)) /* is this it? */
+           continue;
+       if (HeKUTF8(entry) != (char)is_utf8)
            continue;
        found = 1;
        break;
     }
     if (!found) {
-       entry = new_he();
-       HeKEY_hek(entry) = save_hek(str, len, hash);
+       entry = new_HE();
+       HeKEY_hek(entry) = save_hek(str, is_utf8?-len:len, hash);
        HeVAL(entry) = Nullsv;
        HeNEXT(entry) = *oentry;
        *oentry = entry;
-       xhv->xhv_keys++;
+       xhv->xhv_keys++; /* HvKEYS(hv)++ */
        if (i) {                                /* initial entry? */
-           ++xhv->xhv_fill;
-           if (xhv->xhv_keys > xhv->xhv_max)
+           xhv->xhv_fill++; /* HvFILL(hv)++ */
+           if (xhv->xhv_keys > xhv->xhv_max /* HvKEYS(hv) > HvMAX(hv) */)
                hsplit(PL_strtab);
        }
     }
 
     ++HeVAL(entry);                            /* use value slot as REFCNT */
+    UNLOCK_STRTAB_MUTEX;
+    if (str != save)
+       Safefree(str);
     return HeKEY_hek(entry);
 }
-
-
-