This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Re: filename problems
[perl5.git] / hv.c
diff --git a/hv.c b/hv.c
index 44d37e3..d553920 100644 (file)
--- a/hv.c
+++ b/hv.c
@@ -1,6 +1,6 @@
 /*    hv.c
  *
- *    Copyright (c) 1991-2000, 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.
@@ -42,9 +42,14 @@ 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++;
@@ -69,25 +74,32 @@ 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
 Perl_unshare_hek(pTHX_ HEK *hek)
 {
-    unsharepvn(HEK_KEY(hek),HEK_LEN(hek),HEK_HASH(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)
+Perl_he_dup(pTHX_ HE *e, bool shared, clone_params* param)
 {
     HE *ret;
 
@@ -102,14 +114,14 @@ Perl_he_dup(pTHX_ HE *e, bool shared)
     ret = new_HE();
     ptr_table_store(PL_ptr_table, e, ret);
 
-    HeNEXT(ret) = he_dup(HeNEXT(e),shared);
+    HeNEXT(ret) = he_dup(HeNEXT(e),shared, param);
     if (HeKLEN(e) == HEf_SVKEY)
-       HeKEY_sv(ret) = SvREFCNT_inc(sv_dup(HeKEY_sv(e)));
+       HeKEY_sv(ret) = SvREFCNT_inc(sv_dup(HeKEY_sv(e), param));
     else if (shared)
-       HeKEY_hek(ret) = share_hek(HeKEY(e), HeKLEN(e), HeHASH(e));
+       HeKEY_hek(ret) = share_hek(HeKEY(e), HeKLEN_UTF8(e), HeHASH(e));
     else
-       HeKEY_hek(ret) = save_hek(HeKEY(e), HeKLEN(e), HeHASH(e));
-    HeVAL(ret) = SvREFCNT_inc(sv_dup(HeVAL(e)));
+       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 */
@@ -123,7 +135,7 @@ Perl_he_dup(pTHX_ HE *e, bool shared)
 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*>. 
+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.
@@ -132,26 +144,32 @@ information on how to use this function on tied hashes.
 */
 
 SV**
-Perl_hv_fetch(pTHX_ HV *hv, const 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])) {
@@ -165,46 +183,72 @@ Perl_hv_fetch(pTHX_ HV *hv, const 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,
-                PERL_HV_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)) {
+    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;
 }
 
@@ -219,7 +263,7 @@ 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. 
+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.
@@ -235,13 +279,14 @@ Perl_hv_fetch_ent(pTHX_ HV *hv, SV *keysv, I32 lval, register U32 hash)
     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);
@@ -255,7 +300,7 @@ Perl_hv_fetch_ent(pTHX_ 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)
@@ -272,35 +317,45 @@ Perl_hv_fetch_ent(pTHX_ 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,
-                PERL_HV_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)) {
+    if (SvRMAGICAL((SV*)hv) && mg_find((SV*)hv, PERL_MAGIC_env)) {
        unsigned long len;
        char *env = PerlEnv_ENVgetenv_len(key,&len);
        if (env) {
@@ -310,6 +365,8 @@ Perl_hv_fetch_ent(pTHX_ HV *hv, SV *keysv, I32 lval, register U32 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);
@@ -327,8 +384,8 @@ S_hv_magic_check(pTHX_ 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;
            }
        }
@@ -346,7 +403,7 @@ 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.  
+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.
@@ -355,16 +412,23 @@ information on how to use this function on tied hashes.
 */
 
 SV**
-Perl_hv_store(pTHX_ HV *hv, const 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;
@@ -372,24 +436,33 @@ Perl_hv_store(pTHX_ HV *hv, const char *key, U32 klen, SV *val, register U32 has
        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(newSVpvn(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,
-            PERL_HV_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;
 
@@ -398,26 +471,32 @@ Perl_hv_store(pTHX_ HV *hv, const char *key, U32 klen, SV *val, register U32 has
            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();
     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);
     }
 
@@ -432,10 +511,10 @@ 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
+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. 
+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.
@@ -452,13 +531,14 @@ Perl_hv_store_ent(pTHX_ 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);
@@ -469,10 +549,10 @@ Perl_hv_store_ent(pTHX_ 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(newSVpvn(key,klen));
                (void)strupr(SvPVX(keysv));
@@ -482,15 +562,21 @@ Perl_hv_store_ent(pTHX_ 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,
-            PERL_HV_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;
 
@@ -499,26 +585,32 @@ Perl_hv_store_ent(pTHX_ 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();
     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);
     }
 
@@ -529,7 +621,7 @@ Perl_hv_store_ent(pTHX_ HV *hv, SV *keysv, SV *val, register U32 hash)
 =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. 
+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.
 
@@ -537,7 +629,7 @@ will be returned.
 */
 
 SV *
-Perl_hv_delete(pTHX_ HV *hv, const 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;
@@ -546,9 +638,15 @@ Perl_hv_delete(pTHX_ HV *hv, const 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;
@@ -558,14 +656,15 @@ Perl_hv_delete(pTHX_ HV *hv, const 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')) {
+           else if (mg_find((SV*)hv, PERL_MAGIC_env)) {
                sv = sv_2mortal(newSVpvn(key,klen));
                key = strupr(SvPVX(sv));
            }
@@ -573,11 +672,19 @@ Perl_hv_delete(pTHX_ HV *hv, const char *key, U32 klen, I32 flags)
         }
     }
     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;
@@ -586,24 +693,30 @@ Perl_hv_delete(pTHX_ HV *hv, const 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_2mortal(HeVAL(entry));
            HeVAL(entry) = &PL_sv_undef;
        }
-       if (entry == xhv->xhv_eiter)
+       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;
 }
 
@@ -628,7 +741,9 @@ Perl_hv_delete_ent(pTHX_ 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)) {
@@ -640,31 +755,37 @@ Perl_hv_delete_ent(pTHX_ 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(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;
@@ -673,24 +794,30 @@ Perl_hv_delete_ent(pTHX_ 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_2mortal(HeVAL(entry));
            HeVAL(entry) = &PL_sv_undef;
        }
-       if (entry == xhv->xhv_eiter)
+       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;
 }
 
@@ -704,26 +831,32 @@ C<klen> is the length of the key.
 */
 
 bool
-Perl_hv_exists(pTHX_ HV *hv, const 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')) {
+       else if (mg_find((SV*)hv, PERL_MAGIC_env)) {
            sv = sv_2mortal(newSVpvn(key,klen));
            key = strupr(SvPVX(sv));
        }
@@ -732,28 +865,40 @@ Perl_hv_exists(pTHX_ HV *hv, const char *key, U32 klen)
 
     xhv = (XPVHV*)SvANY(hv);
 #ifndef DYNAMIC_ENV_FETCH
-    if (!xhv->xhv_array)
-       return 0; 
+    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) entry = Null(HE*);
+    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 (HvNAME(hv) && strEQ(HvNAME(hv), ENV_HV_NAME)) {
+    if (SvRMAGICAL((SV*)hv) && mg_find((SV*)hv, PERL_MAGIC_env)) {
        unsigned long len;
        char *env = PerlEnv_ENVgetenv_len(key,&len);
        if (env) {
@@ -764,6 +909,8 @@ Perl_hv_exists(pTHX_ HV *hv, const char *key, U32 klen)
        }
     }
 #endif
+    if (key != keysave)
+       Safefree(key);
     return FALSE;
 }
 
@@ -786,55 +933,65 @@ Perl_hv_exists_ent(pTHX_ HV *hv, SV *keysv, U32 hash)
     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(newSVpvn(key,klen));
            (void)strupr(SvPVX(keysv));
-           hash = 0; 
+           hash = 0;
        }
 #endif
     }
 
     xhv = (XPVHV*)SvANY(hv);
 #ifndef DYNAMIC_ENV_FETCH
-    if (!xhv->xhv_array)
-       return 0; 
+    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) entry = Null(HE*);
+    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 (HvNAME(hv) && strEQ(HvNAME(hv), ENV_HV_NAME)) {
+    if (SvRMAGICAL((SV*)hv) && mg_find((SV*)hv, PERL_MAGIC_env)) {
        unsigned long len;
        char *env = PerlEnv_ENVgetenv_len(key,&len);
        if (env) {
@@ -845,6 +1002,8 @@ Perl_hv_exists_ent(pTHX_ HV *hv, SV *keysv, U32 hash)
        }
     }
 #endif
+    if (key != keysave)
+       Safefree(key);
     return FALSE;
 }
 
@@ -852,10 +1011,10 @@ STATIC void
 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;
@@ -869,24 +1028,24 @@ S_hsplit(pTHX_ HV *hv)
       return;
     }
 #else
-#define MALLOC_OVERHEAD 16
     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, PERL_HV_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++) {
@@ -898,7 +1057,7 @@ S_hsplit(pTHX_ HV *hv)
                *oentry = HeNEXT(entry);
                HeNEXT(entry) = *bep;
                if (!*bep)
-                   xhv->xhv_fill++;
+                   xhv->xhv_fill++; /* HvFILL(hv)++ */
                *bep = entry;
                continue;
            }
@@ -906,7 +1065,7 @@ S_hsplit(pTHX_ HV *hv)
                oentry = &HeNEXT(entry);
        }
        if (!*aep)                              /* everything moved */
-           xhv->xhv_fill--;
+           xhv->xhv_fill--; /* HvFILL(hv)-- */
     }
 }
 
@@ -914,7 +1073,7 @@ void
 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;
@@ -934,7 +1093,7 @@ Perl_hv_ksplit(pTHX_ 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)
@@ -949,12 +1108,13 @@ Perl_hv_ksplit(pTHX_ HV *hv, IV newmax)
          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, PERL_HV_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*/
@@ -962,9 +1122,9 @@ Perl_hv_ksplit(pTHX_ HV *hv, IV newmax)
     else {
        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;
@@ -976,7 +1136,7 @@ Perl_hv_ksplit(pTHX_ 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;
            }
@@ -984,7 +1144,7 @@ Perl_hv_ksplit(pTHX_ HV *hv, IV newmax)
                oentry = &HeNEXT(entry);
        }
        if (!*aep)                              /* everything moved */
-           xhv->xhv_fill--;
+           xhv->xhv_fill--; /* HvFILL(hv)-- */
     }
 }
 
@@ -1007,12 +1167,12 @@ Perl_newHV(pTHX)
     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;
 }
@@ -1020,38 +1180,74 @@ Perl_newHV(pTHX)
 HV *
 Perl_newHVhv(pTHX_ HV *ohv)
 {
-    register HV *hv;
-    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 */
-    HvMAX(hv) = 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 (! SvTIED_mg((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 */
+       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(entry), 
-                    SvREFCNT_inc(HeVAL(entry)), HeHASH(entry));
+           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;
 }
 
@@ -1112,13 +1308,14 @@ Perl_hv_clear(pTHX_ HV *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
@@ -1149,7 +1346,7 @@ S_hfreeentries(pTHX_ HV *hv)
            if (++riter > max)
                break;
            entry = array[riter];
-       } 
+       }
     }
     (void)hv_iterinit(hv);
 }
@@ -1170,18 +1367,18 @@ Perl_hv_undef(pTHX_ HV *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);
 }
 
 /*
@@ -1189,7 +1386,7 @@ Perl_hv_undef(pTHX_ HV *hv)
 
 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. 
+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
@@ -1207,14 +1404,15 @@ Perl_hv_iterinit(pTHX_ HV *hv)
     if (!hv)
        Perl_croak(aTHX_ "Bad hash");
     xhv = (XPVHV*)SvANY(hv);
-    entry = xhv->xhv_eiter;
+    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) */
 }
 
 /*
@@ -1236,9 +1434,9 @@ Perl_hv_iternext(pTHX_ HV *hv)
     if (!hv)
        Perl_croak(aTHX_ "Bad hash");
     xhv = (XPVHV*)SvANY(hv);
-    oldentry = entry = xhv->xhv_eiter;
+    oldentry = entry = xhv->xhv_eiter; /* HvEITER(hv) */
 
-    if ((mg = SvTIED_mg((SV*)hv, 'P'))) {
+    if ((mg = SvTIED_mg((SV*)hv, PERL_MAGIC_tied))) {
        SV *key = sv_newmortal();
        if (entry) {
            sv_setsv(key, HeSVKEY_force(entry));
@@ -1248,7 +1446,8 @@ Perl_hv_iternext(pTHX_ 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;
@@ -1265,25 +1464,27 @@ Perl_hv_iternext(pTHX_ HV *hv)
            SvREFCNT_dec(HeVAL(entry));
        Safefree(HeKEY_hek(entry));
        del_HE(entry);
-       xhv->xhv_eiter = Null(HE*);
+       xhv->xhv_eiter = Null(HE*); /* HvEITER(hv) = Null(HE*) */
        return Null(HE*);
     }
 #ifdef DYNAMIC_ENV_FETCH  /* set up %ENV for iteration */
-    if (!entry && HvNAME(hv) && strEQ(HvNAME(hv), ENV_HV_NAME))
+    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,
-            PERL_HV_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];
     }
 
@@ -1292,7 +1493,7 @@ Perl_hv_iternext(pTHX_ HV *hv)
        hv_free_ent(hv, oldentry);
     }
 
-    xhv->xhv_eiter = entry;
+    xhv->xhv_eiter = entry; /* HvEITER(hv) = entry */
     return entry;
 }
 
@@ -1337,8 +1538,8 @@ Perl_hv_iterkeysv(pTHX_ register HE *entry)
     if (HeKLEN(entry) == HEf_SVKEY)
        return sv_mortalcopy(HeKEY_sv(entry));
     else
-       return sv_2mortal(newSVpvn((HeKLEN(entry) ? HeKEY(entry) : ""),
-                                 HeKLEN(entry)));
+       return sv_2mortal(newSVpvn_share((HeKLEN(entry) ? HeKEY(entry) : ""),
+                                        HeKLEN_UTF8(entry), HeHASH(entry)));
 }
 
 /*
@@ -1354,7 +1555,7 @@ SV *
 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);
@@ -1415,7 +1616,17 @@ Perl_unsharepvn(pTHX_ const char *str, I32 len, U32 hash)
     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(PL_strtab, tmpsv, FALSE, hash))) {
        if (--*Svp == Nullsv)
@@ -1424,32 +1635,33 @@ Perl_unsharepvn(pTHX_ const char *str, I32 len, U32 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;
+           xhv->xhv_keys--; /* HvKEYS(hv)-- */
        }
        break;
     }
     UNLOCK_STRTAB_MUTEX;
-    
-    {
-        dTHR;
-        if (!found && ckWARN_d(WARN_INTERNAL))
-           Perl_warner(aTHX_ WARN_INTERNAL, "Attempt to free non-existent shared string");    
-    }
+    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
@@ -1464,44 +1676,56 @@ Perl_share_hek(pTHX_ const char *str, I32 len, register U32 hash)
     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(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);
+       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);
 }
-
-
-