This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Fix RT26188, speed up keys() on empty hash
[perl5.git] / hv.c
diff --git a/hv.c b/hv.c
index 118439a..5f233d6 100644 (file)
--- a/hv.c
+++ b/hv.c
@@ -1,7 +1,7 @@
 /*    hv.c
  *
- *    Copyright (C) 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
- *    2000, 2001, 2002, 2003, 2004, 2005, 2006, by Larry Wall and others
+ *    Copyright (C) 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
+ *    2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 by Larry Wall and others
  *
  *    You may distribute under the terms of either the GNU General Public
  *    License or the Artistic License, as specified in the README file.
@@ -9,7 +9,11 @@
  */
 
 /*
- * "I sit beside the fire and think of all that I have seen."  --Bilbo
+ *      I sit beside the fire and think
+ *          of all that I have seen.
+ *                         --Bilbo
+ *
+ *     [p.278 of _The Lord of the Rings_, II/iii: "The Ring Goes South"]
  */
 
 /* 
@@ -40,12 +44,12 @@ STATIC void
 S_more_he(pTHX)
 {
     dVAR;
-    HE* he;
-    HE* heend;
-
-    he = (HE*) Perl_get_arena(aTHX_ PERL_ARENA_SIZE);
+    /* We could generate this at compile time via (another) auxiliary C
+       program?  */
+    const size_t arena_size = Perl_malloc_good_size(PERL_ARENA_SIZE);
+    HE* he = (HE*) Perl_get_arena(aTHX_ arena_size, HE_SVSLOT);
+    HE * const heend = &he[arena_size / sizeof(HE) - 1];
 
-    heend = &he[PERL_ARENA_SIZE / sizeof(HE) - 1];
     PL_body_roots[HE_SVSLOT] = he;
     while (he < heend) {
        HeNEXT(he) = (HE*)(he + 1);
@@ -68,22 +72,19 @@ S_new_he(pTHX)
     HE* he;
     void ** const root = &PL_body_roots[HE_SVSLOT];
 
-    LOCK_SV_MUTEX;
     if (!*root)
        S_more_he(aTHX);
-    he = *root;
+    he = (HE*) *root;
+    assert(he);
     *root = HeNEXT(he);
-    UNLOCK_SV_MUTEX;
     return he;
 }
 
 #define new_HE() new_he()
 #define del_HE(p) \
     STMT_START { \
-       LOCK_SV_MUTEX; \
        HeNEXT(p) = (HE*)(PL_body_roots[HE_SVSLOT]);    \
        PL_body_roots[HE_SVSLOT] = p; \
-       UNLOCK_SV_MUTEX; \
     } STMT_END
 
 
@@ -97,13 +98,15 @@ S_save_hek_flags(const char *str, I32 len, U32 hash, int flags)
     char *k;
     register HEK *hek;
 
+    PERL_ARGS_ASSERT_SAVE_HEK_FLAGS;
+
     Newx(k, HEK_BASESIZE + len + 2, 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_FLAGS(hek) = (unsigned char)flags_masked;
+    HEK_FLAGS(hek) = (unsigned char)flags_masked | HVhek_UNSHARED;
 
     if (flags & HVhek_FREEKEY)
        Safefree(str);
@@ -131,10 +134,15 @@ Perl_free_tied_hv_pool(pTHX)
 HEK *
 Perl_hek_dup(pTHX_ HEK *source, CLONE_PARAMS* param)
 {
-    HEK *shared = (HEK*)ptr_table_fetch(PL_ptr_table, source);
+    HEK *shared;
 
+    PERL_ARGS_ASSERT_HEK_DUP;
     PERL_UNUSED_ARG(param);
 
+    if (!source)
+       return NULL;
+
+    shared = (HEK*)ptr_table_fetch(PL_ptr_table, source);
     if (shared) {
        /* We already shared this hash key.  */
        (void)share_hek_hek(shared);
@@ -153,6 +161,8 @@ Perl_he_dup(pTHX_ const HE *e, bool shared, CLONE_PARAMS* param)
 {
     HE *ret;
 
+    PERL_ARGS_ASSERT_HE_DUP;
+
     if (!e)
        return NULL;
     /* look for it in the table first */
@@ -167,7 +177,7 @@ Perl_he_dup(pTHX_ const HE *e, bool shared, CLONE_PARAMS* param)
     HeNEXT(ret) = he_dup(HeNEXT(e),shared, param);
     if (HeKLEN(e) == HEf_SVKEY) {
        char *k;
-       Newx(k, HEK_BASESIZE + sizeof(SV*), char);
+       Newx(k, HEK_BASESIZE + sizeof(const SV *), char);
        HeKEY_hek(ret) = (HEK*)k;
        HeKEY_sv(ret) = SvREFCNT_inc(sv_dup(HeKEY_sv(e), param));
     }
@@ -202,6 +212,9 @@ S_hv_notallowed(pTHX_ int flags, const char *key, I32 klen,
                const char *msg)
 {
     SV * const sv = sv_newmortal();
+
+    PERL_ARGS_ASSERT_HV_NOTALLOWED;
+
     if (!(flags & HVhek_FREEKEY)) {
        sv_setpvn(sv, key, klen);
     }
@@ -213,17 +226,12 @@ S_hv_notallowed(pTHX_ int flags, const char *key, I32 klen,
     if (flags & HVhek_UTF8) {
        SvUTF8_on(sv);
     }
-    Perl_croak(aTHX_ msg, sv);
+    Perl_croak(aTHX_ msg, SVfARG(sv));
 }
 
 /* (klen == HEf_SVKEY) is special for MAGICAL hv entries, meaning key slot
  * contains an SV* */
 
-#define HV_FETCH_ISSTORE   0x01
-#define HV_FETCH_ISEXISTS  0x02
-#define HV_FETCH_LVALUE    0x04
-#define HV_FETCH_JUST_SV   0x08
-
 /*
 =for apidoc hv_store
 
@@ -247,39 +255,6 @@ hv_store_ent.
 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**
-Perl_hv_store(pTHX_ HV *hv, const char *key, I32 klen_i32, SV *val, U32 hash)
-{
-    HE *hek;
-    STRLEN klen;
-    int flags;
-
-    if (klen_i32 < 0) {
-       klen = -klen_i32;
-       flags = HVhek_UTF8;
-    } else {
-       klen = klen_i32;
-       flags = 0;
-    }
-    hek = hv_fetch_common (hv, NULL, key, klen, flags,
-                          (HV_FETCH_ISSTORE|HV_FETCH_JUST_SV), val, hash);
-    return hek ? &HeVAL(hek) : NULL;
-}
-
-/* XXX This looks like an ideal candidate to inline */
-SV**
-Perl_hv_store_flags(pTHX_ HV *hv, const char *key, I32 klen, SV *val,
-                 register U32 hash, int flags)
-{
-    HE * const hek = hv_fetch_common (hv, NULL, key, klen, flags,
-                              (HV_FETCH_ISSTORE|HV_FETCH_JUST_SV), val, hash);
-    return hek ? &HeVAL(hek) : NULL;
-}
-
-/*
 =for apidoc hv_store_ent
 
 Stores C<val> in a hash.  The hash key is specified as C<key>.  The C<hash>
@@ -305,43 +280,11 @@ hv_store in preference to hv_store_ent.
 See L<perlguts/"Understanding the Magic of Tied Hashes and Arrays"> for more
 information on how to use this function on tied hashes.
 
-=cut
-*/
-
-/* XXX This looks like an ideal candidate to inline */
-HE *
-Perl_hv_store_ent(pTHX_ HV *hv, SV *keysv, SV *val, U32 hash)
-{
-  return hv_fetch_common(hv, keysv, NULL, 0, 0, HV_FETCH_ISSTORE, val, hash);
-}
-
-/*
 =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
-Perl_hv_exists(pTHX_ HV *hv, const char *key, I32 klen_i32)
-{
-    STRLEN klen;
-    int flags;
-
-    if (klen_i32 < 0) {
-       klen = -klen_i32;
-       flags = HVhek_UTF8;
-    } else {
-       klen = klen_i32;
-       flags = 0;
-    }
-    return hv_fetch_common(hv, NULL, key, klen, flags, HV_FETCH_ISEXISTS, 0, 0)
-       ? TRUE : FALSE;
-}
-
-/*
 =for apidoc hv_fetch
 
 Returns the SV which corresponds to the specified key in the hash.  The
@@ -352,30 +295,6 @@ dereferencing it to an 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**
-Perl_hv_fetch(pTHX_ HV *hv, const char *key, I32 klen_i32, I32 lval)
-{
-    HE *hek;
-    STRLEN klen;
-    int flags;
-
-    if (klen_i32 < 0) {
-       klen = -klen_i32;
-       flags = HVhek_UTF8;
-    } else {
-       klen = klen_i32;
-       flags = 0;
-    }
-    hek = hv_fetch_common (hv, NULL, key, klen, flags,
-                          lval ? (HV_FETCH_JUST_SV | HV_FETCH_LVALUE) : HV_FETCH_JUST_SV,
-                          NULL, 0);
-    return hek ? &HeVAL(hek) : NULL;
-}
-
-/*
 =for apidoc hv_exists_ent
 
 Returns a boolean indicating whether the specified hash key exists. C<hash>
@@ -385,14 +304,6 @@ computed.
 =cut
 */
 
-/* XXX This looks like an ideal candidate to inline */
-bool
-Perl_hv_exists_ent(pTHX_ HV *hv, SV *keysv, U32 hash)
-{
-    return hv_fetch_common(hv, keysv, NULL, 0, 0, HV_FETCH_ISEXISTS, 0, hash)
-       ? TRUE : FALSE;
-}
-
 /* returns an HE * structure with the all fields set */
 /* note that hent_val will be a mortal sv for MAGICAL hashes */
 /*
@@ -412,16 +323,29 @@ information on how to use this function on tied hashes.
 =cut
 */
 
-HE *
-Perl_hv_fetch_ent(pTHX_ HV *hv, SV *keysv, I32 lval, register U32 hash)
+/* Common code for hv_delete()/hv_exists()/hv_fetch()/hv_store()  */
+void *
+Perl_hv_common_key_len(pTHX_ HV *hv, const char *key, I32 klen_i32,
+                      const int action, SV *val, const U32 hash)
 {
-    return hv_fetch_common(hv, keysv, NULL, 0, 0, 
-                          (lval ? HV_FETCH_LVALUE : 0), NULL, hash);
+    STRLEN klen;
+    int flags;
+
+    PERL_ARGS_ASSERT_HV_COMMON_KEY_LEN;
+
+    if (klen_i32 < 0) {
+       klen = -klen_i32;
+       flags = HVhek_UTF8;
+    } else {
+       klen = klen_i32;
+       flags = 0;
+    }
+    return hv_common(hv, NULL, key, klen, flags, action, val, hash);
 }
 
-STATIC HE *
-S_hv_fetch_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
-                 int flags, int action, SV *val, register U32 hash)
+void *
+Perl_hv_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
+              int flags, int action, SV *val, register U32 hash)
 {
     dVAR;
     XPVHV* xhv;
@@ -430,38 +354,75 @@ S_hv_fetch_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
     SV *sv;
     bool is_utf8;
     int masked_flags;
+    const int return_svp = action & HV_FETCH_JUST_SV;
 
     if (!hv)
        return NULL;
+    if (SvTYPE(hv) == SVTYPEMASK)
+       return NULL;
+
+    assert(SvTYPE(hv) == SVt_PVHV);
+
+    if (SvSMAGICAL(hv) && SvGMAGICAL(hv) && !(action & HV_DISABLE_UVAR_XKEY)) {
+       MAGIC* mg;
+       if ((mg = mg_find((const SV *)hv, PERL_MAGIC_uvar))) {
+           struct ufuncs * const uf = (struct ufuncs *)mg->mg_ptr;
+           if (uf->uf_set == NULL) {
+               SV* obj = mg->mg_obj;
 
+               if (!keysv) {
+                   keysv = newSVpvn_flags(key, klen, SVs_TEMP |
+                                          ((flags & HVhek_UTF8)
+                                           ? SVf_UTF8 : 0));
+               }
+               
+               mg->mg_obj = keysv;         /* pass key */
+               uf->uf_index = action;      /* pass action */
+               magic_getuvar(MUTABLE_SV(hv), mg);
+               keysv = mg->mg_obj;         /* may have changed */
+               mg->mg_obj = obj;
+
+               /* If the key may have changed, then we need to invalidate
+                  any passed-in computed hash value.  */
+               hash = 0;
+           }
+       }
+    }
     if (keysv) {
        if (flags & HVhek_FREEKEY)
            Safefree(key);
        key = SvPV_const(keysv, klen);
-       flags = 0;
        is_utf8 = (SvUTF8(keysv) != 0);
+       if (SvIsCOW_shared_hash(keysv)) {
+           flags = HVhek_KEYCANONICAL | (is_utf8 ? HVhek_UTF8 : 0);
+       } else {
+           flags = 0;
+       }
     } else {
        is_utf8 = ((flags & HVhek_UTF8) ? TRUE : FALSE);
     }
 
+    if (action & HV_DELETE) {
+       return (void *) hv_delete_common(hv, keysv, key, klen,
+                                        flags | (is_utf8 ? HVhek_UTF8 : 0),
+                                        action, hash);
+    }
+
     xhv = (XPVHV*)SvANY(hv);
     if (SvMAGICAL(hv)) {
        if (SvRMAGICAL(hv) && !(action & (HV_FETCH_ISSTORE|HV_FETCH_ISEXISTS))) {
-           if (mg_find((SV*)hv, PERL_MAGIC_tied) || SvGMAGICAL((SV*)hv)) {
-               sv = sv_newmortal();
-
-               /* XXX should be able to skimp on the HE/HEK here when
+           if (mg_find((const SV *)hv, PERL_MAGIC_tied)
+               || SvGMAGICAL((const SV *)hv))
+           {
+               /* FIXME should be able to skimp on the HE/HEK here when
                   HV_FETCH_JUST_SV is true.  */
-
                if (!keysv) {
-                   keysv = newSVpvn(key, klen);
-                   if (is_utf8) {
-                       SvUTF8_on(keysv);
-                   }
-               } else {
+                   keysv = newSVpvn_utf8(key, klen, is_utf8);
+               } else {
                    keysv = newSVsv(keysv);
                }
-               mg_copy((SV*)hv, sv, (char *)keysv, HEf_SVKEY);
+                sv = sv_newmortal();
+                mg_copy(MUTABLE_SV(hv), sv, (char *)keysv, HEf_SVKEY);
 
                /* grab a fake HE/HEK pair from the pool or make a new one */
                entry = PL_hv_fetch_ent_mh;
@@ -470,7 +431,7 @@ S_hv_fetch_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
                else {
                    char *k;
                    entry = new_HE();
-                   Newx(k, HEK_BASESIZE + sizeof(SV*), char);
+                   Newx(k, HEK_BASESIZE + sizeof(const SV *), char);
                    HeKEY_hek(entry) = (HEK*)k;
                }
                HeNEXT(entry) = NULL;
@@ -479,16 +440,19 @@ S_hv_fetch_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
                sv_upgrade(sv, SVt_PVLV);
                LvTYPE(sv) = 'T';
                 /* so we can free entry when freeing sv */
-               LvTARG(sv) = (SV*)entry;
+               LvTARG(sv) = MUTABLE_SV(entry);
 
                /* XXX remove at some point? */
                if (flags & HVhek_FREEKEY)
                    Safefree(key);
 
-               return entry;
+               if (return_svp) {
+                   return entry ? (void *) &HeVAL(entry) : NULL;
+               }
+               return (void *) entry;
            }
 #ifdef ENV_IS_CASELESS
-           else if (mg_find((SV*)hv, PERL_MAGIC_env)) {
+           else if (mg_find((const SV *)hv, PERL_MAGIC_env)) {
                U32 i;
                for (i = 0; i < klen; ++i)
                    if (isLOWER(key[i])) {
@@ -497,29 +461,34 @@ S_hv_fetch_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
                        const char * const nkey = strupr(savepvn(key,klen));
                        /* Note that this fetch is for nkey (the uppercased
                           key) whereas the store is for key (the original)  */
-                       entry = hv_fetch_common(hv, NULL, nkey, klen,
-                                               HVhek_FREEKEY, /* free nkey */
-                                               0 /* non-LVAL fetch */,
-                                               NULL /* no value */,
-                                               0 /* compute hash */);
-                       if (!entry && (action & HV_FETCH_LVALUE)) {
+                       void *result = hv_common(hv, NULL, nkey, klen,
+                                                HVhek_FREEKEY, /* free nkey */
+                                                0 /* non-LVAL fetch */
+                                                | HV_DISABLE_UVAR_XKEY
+                                                | return_svp,
+                                                NULL /* no value */,
+                                                0 /* compute hash */);
+                       if (!result && (action & HV_FETCH_LVALUE)) {
                            /* This call will free key if necessary.
                               Do it this way to encourage compiler to tail
                               call optimise.  */
-                           entry = hv_fetch_common(hv, keysv, key, klen,
-                                                   flags, HV_FETCH_ISSTORE,
-                                                   newSV(0), hash);
+                           result = hv_common(hv, keysv, key, klen, flags,
+                                              HV_FETCH_ISSTORE
+                                              | HV_DISABLE_UVAR_XKEY
+                                              | return_svp,
+                                              newSV(0), hash);
                        } else {
                            if (flags & HVhek_FREEKEY)
                                Safefree(key);
                        }
-                       return entry;
+                       return result;
                    }
            }
 #endif
        } /* ISFETCH */
        else if (SvRMAGICAL(hv) && (action & HV_FETCH_ISEXISTS)) {
-           if (mg_find((SV*)hv, PERL_MAGIC_tied) || SvGMAGICAL((SV*)hv)) {
+           if (mg_find((const SV *)hv, PERL_MAGIC_tied)
+               || SvGMAGICAL((const SV *)hv)) {
                /* I don't understand why hv_exists_ent has svret and sv,
                   whereas hv_exists only had one.  */
                SV * const svret = sv_newmortal();
@@ -527,14 +496,13 @@ S_hv_fetch_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
 
                if (keysv || is_utf8) {
                    if (!keysv) {
-                       keysv = newSVpvn(key, klen);
-                       SvUTF8_on(keysv);
+                       keysv = newSVpvn_utf8(key, klen, TRUE);
                    } else {
                        keysv = newSVsv(keysv);
                    }
-                   mg_copy((SV*)hv, sv, (char *)sv_2mortal(keysv), HEf_SVKEY);
+                   mg_copy(MUTABLE_SV(hv), sv, (char *)sv_2mortal(keysv), HEf_SVKEY);
                } else {
-                   mg_copy((SV*)hv, sv, key, klen);
+                   mg_copy(MUTABLE_SV(hv), sv, key, klen);
                }
                if (flags & HVhek_FREEKEY)
                    Safefree(key);
@@ -542,10 +510,10 @@ S_hv_fetch_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
                /* This cast somewhat evil, but I'm merely using NULL/
                   not NULL to return the boolean exists.
                   And I know hv is not NULL.  */
-               return SvTRUE(svret) ? (HE *)hv : NULL;
+               return SvTRUE(svret) ? (void *)hv : NULL;
                }
 #ifdef ENV_IS_CASELESS
-           else if (mg_find((SV*)hv, PERL_MAGIC_env)) {
+           else if (mg_find((const SV *)hv, PERL_MAGIC_env)) {
                /* XXX This code isn't UTF8 clean.  */
                char * const keysave = (char * const)key;
                /* Will need to free this, so set FREEKEY flag.  */
@@ -570,15 +538,14 @@ S_hv_fetch_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
                const bool save_taint = PL_tainted;
                if (keysv || is_utf8) {
                    if (!keysv) {
-                       keysv = newSVpvn(key, klen);
-                       SvUTF8_on(keysv);
+                       keysv = newSVpvn_utf8(key, klen, TRUE);
                    }
                    if (PL_tainting)
                        PL_tainted = SvTAINTED(keysv);
                    keysv = sv_2mortal(newSVsv(keysv));
-                   mg_copy((SV*)hv, val, (char*)keysv, HEf_SVKEY);
+                   mg_copy(MUTABLE_SV(hv), val, (char*)keysv, HEf_SVKEY);
                } else {
-                   mg_copy((SV*)hv, val, key, klen);
+                   mg_copy(MUTABLE_SV(hv), val, key, klen);
                }
 
                TAINT_IF(save_taint);
@@ -588,7 +555,7 @@ S_hv_fetch_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
                    return NULL;
                }
 #ifdef ENV_IS_CASELESS
-               else if (mg_find((SV*)hv, PERL_MAGIC_env)) {
+               else if (mg_find((const SV *)hv, PERL_MAGIC_env)) {
                    /* XXX This code isn't UTF8 clean.  */
                    const char *keysave = key;
                    /* Will need to free this, so set FREEKEY flag.  */
@@ -611,7 +578,8 @@ S_hv_fetch_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
     if (!HvARRAY(hv)) {
        if ((action & (HV_FETCH_LVALUE | HV_FETCH_ISSTORE))
 #ifdef DYNAMIC_ENV_FETCH  /* if it's an %ENV lookup, we may get it on the fly */
-                || (SvRMAGICAL((SV*)hv) && mg_find((SV*)hv, PERL_MAGIC_env))
+                || (SvRMAGICAL((const SV *)hv)
+                    && mg_find((const SV *)hv, PERL_MAGIC_env))
 #endif
                                                                  ) {
            char *array;
@@ -631,11 +599,11 @@ S_hv_fetch_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
             if (flags & HVhek_FREEKEY)
                 Safefree(key);
 
-           return 0;
+           return NULL;
        }
     }
 
-    if (is_utf8) {
+    if (is_utf8 & !(flags & HVhek_KEYCANONICAL)) {
        char * const keysave = (char *)key;
        key = (char*)bytes_from_utf8((U8*)key, &klen, &is_utf8);
         if (is_utf8)
@@ -646,6 +614,11 @@ S_hv_fetch_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
            if (flags & HVhek_FREEKEY)
                Safefree(keysave);
             flags |= HVhek_WASUTF8 | HVhek_FREEKEY;
+           /* If the caller calculated a hash, it was on the sequence of
+              octets that are the UTF-8 form. We've now changed the sequence
+              of octets stored to that of the equivalent byte representation,
+              so the hash we need is different.  */
+           hash = 0;
        }
     }
 
@@ -747,18 +720,23 @@ S_hv_fetch_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
        }
        if (flags & HVhek_FREEKEY)
            Safefree(key);
+       if (return_svp) {
+           return entry ? (void *) &HeVAL(entry) : NULL;
+       }
        return entry;
     }
 #ifdef DYNAMIC_ENV_FETCH  /* %ENV lookup?  If so, try to fetch the value now */
     if (!(action & HV_FETCH_ISSTORE) 
-       && SvRMAGICAL((SV*)hv) && mg_find((SV*)hv, PERL_MAGIC_env)) {
+       && SvRMAGICAL((const SV *)hv)
+       && mg_find((const SV *)hv, PERL_MAGIC_env)) {
        unsigned long len;
        const char * const env = PerlEnv_ENVgetenv_len(key,&len);
        if (env) {
            sv = newSVpvn(env,len);
            SvTAINTED_on(sv);
-           return hv_fetch_common(hv,keysv,key,klen,flags,HV_FETCH_ISSTORE,sv,
-                                  hash);
+           return hv_common(hv, keysv, key, klen, flags,
+                            HV_FETCH_ISSTORE|HV_DISABLE_UVAR_XKEY|return_svp,
+                            sv, hash);
        }
     }
 #endif
@@ -772,7 +750,7 @@ S_hv_fetch_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
        /* Not doing some form of store, so return failure.  */
        if (flags & HVhek_FREEKEY)
            Safefree(key);
-       return 0;
+       return NULL;
     }
     if (action & HV_FETCH_LVALUE) {
        val = newSV(0);
@@ -781,8 +759,15 @@ S_hv_fetch_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
               which in turn might do some tied magic. So we need to make that
               magic check happen.  */
            /* gonna assign to this, so it better be there */
-           return hv_fetch_common(hv, keysv, key, klen, flags,
-                                  HV_FETCH_ISSTORE, val, hash);
+           /* If a fetch-as-store fails on the fetch, then the action is to
+              recurse once into "hv_store". If we didn't do this, then that
+              recursive call would call the key conversion routine again.
+              However, as we replace the original key with the converted
+              key, this would result in a double conversion, which would show
+              up as a bug if the conversion routine is not idempotent.  */
+           return hv_common(hv, keysv, key, klen, flags,
+                            HV_FETCH_ISSTORE|HV_DISABLE_UVAR_XKEY|return_svp,
+                            val, hash);
            /* XXX Surely that could leak if the fetch-was-store fails?
               Just like the hv_fetch.  */
        }
@@ -853,13 +838,19 @@ S_hv_fetch_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
        }
     }
 
-    return entry;
+    if (return_svp) {
+       return entry ? (void *) &HeVAL(entry) : NULL;
+    }
+    return (void *) entry;
 }
 
 STATIC void
 S_hv_magic_check(HV *hv, bool *needs_copy, bool *needs_store)
 {
     const MAGIC *mg = SvMAGIC(hv);
+
+    PERL_ARGS_ASSERT_HV_MAGIC_CHECK;
+
     *needs_copy = FALSE;
     *needs_store = TRUE;
     while (mg) {
@@ -887,14 +878,16 @@ Perl_hv_scalar(pTHX_ HV *hv)
 {
     SV *sv;
 
+    PERL_ARGS_ASSERT_HV_SCALAR;
+
     if (SvRMAGICAL(hv)) {
-       MAGIC * const mg = mg_find((SV*)hv, PERL_MAGIC_tied);
+       MAGIC * const mg = mg_find((const SV *)hv, PERL_MAGIC_tied);
        if (mg)
            return magic_scalarpack(hv, mg);
     }
 
     sv = sv_newmortal();
-    if (HvFILL((HV*)hv)) 
+    if (HvFILL((const HV *)hv)) 
         Perl_sv_setpvf(aTHX_ sv, "%ld/%ld",
                 (long)HvFILL(hv), (long)HvMAX(hv) + 1);
     else
@@ -911,26 +904,6 @@ 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 *
-Perl_hv_delete(pTHX_ HV *hv, const char *key, I32 klen_i32, I32 flags)
-{
-    STRLEN klen;
-    int k_flags;
-
-    if (klen_i32 < 0) {
-       klen = -klen_i32;
-       k_flags = HVhek_UTF8;
-    } else {
-       klen = klen_i32;
-       k_flags = 0;
-    }
-    return hv_delete_common(hv, NULL, key, klen, k_flags, flags, 0);
-}
-
-/*
 =for apidoc hv_delete_ent
 
 Deletes a key/value pair in the hash.  The value SV is removed from the
@@ -941,13 +914,6 @@ precomputed hash value, or 0 to ask for it to be computed.
 =cut
 */
 
-/* XXX This looks like an ideal candidate to inline */
-SV *
-Perl_hv_delete_ent(pTHX_ HV *hv, SV *keysv, I32 flags, U32 hash)
-{
-    return hv_delete_common(hv, keysv, NULL, 0, 0, flags, hash);
-}
-
 STATIC SV *
 S_hv_delete_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
                   int k_flags, I32 d_flags, U32 hash)
@@ -957,22 +923,9 @@ S_hv_delete_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
     register HE *entry;
     register HE **oentry;
     HE *const *first_entry;
-    bool is_utf8;
+    bool is_utf8 = (k_flags & HVhek_UTF8) ? TRUE : FALSE;
     int masked_flags;
 
-    if (!hv)
-       return NULL;
-
-    if (keysv) {
-       if (k_flags & HVhek_FREEKEY)
-           Safefree(key);
-       key = SvPV_const(keysv, klen);
-       k_flags = 0;
-       is_utf8 = (SvUTF8(keysv) != 0);
-    } else {
-       is_utf8 = ((k_flags & HVhek_UTF8) ? TRUE : FALSE);
-    }
-
     if (SvRMAGICAL(hv)) {
        bool needs_copy;
        bool needs_store;
@@ -980,9 +933,10 @@ S_hv_delete_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
 
        if (needs_copy) {
            SV *sv;
-           entry = hv_fetch_common(hv, keysv, key, klen,
-                                   k_flags & ~HVhek_FREEKEY, HV_FETCH_LVALUE,
-                                   NULL, hash);
+           entry = (HE *) hv_common(hv, keysv, key, klen,
+                                    k_flags & ~HVhek_FREEKEY,
+                                    HV_FETCH_LVALUE|HV_DISABLE_UVAR_XKEY,
+                                    NULL, hash);
            sv = entry ? HeVAL(entry) : NULL;
            if (sv) {
                if (SvMAGICAL(sv)) {
@@ -997,9 +951,9 @@ S_hv_delete_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
                    return NULL;                /* element cannot be deleted */
                }
 #ifdef ENV_IS_CASELESS
-               else if (mg_find((SV*)hv, PERL_MAGIC_env)) {
+               else if (mg_find((const SV *)hv, PERL_MAGIC_env)) {
                    /* XXX This code isn't UTF8 clean.  */
-                   keysv = sv_2mortal(newSVpvn(key,klen));
+                   keysv = newSVpvn_flags(key, klen, SVs_TEMP);
                    if (k_flags & HVhek_FREEKEY) {
                        Safefree(key);
                    }
@@ -1032,7 +986,7 @@ S_hv_delete_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
            }
            k_flags |= HVhek_WASUTF8 | HVhek_FREEKEY;
        }
-        HvHASKFLAGS_on((SV*)hv);
+        HvHASKFLAGS_on(MUTABLE_SV(hv));
     }
 
     if (HvREHASH(hv)) {
@@ -1129,7 +1083,7 @@ STATIC void
 S_hsplit(pTHX_ HV *hv)
 {
     dVAR;
-    register XPVHV* xhv = (XPVHV*)SvANY(hv);
+    register XPVHV* const xhv = (XPVHV*)SvANY(hv);
     const I32 oldsize = (I32) xhv->xhv_max+1; /* HvMAX(hv)+1 (sick) */
     register I32 newsize = oldsize * 2;
     register I32 i;
@@ -1139,8 +1093,10 @@ S_hsplit(pTHX_ HV *hv)
     int longest_chain = 0;
     int was_shared;
 
+    PERL_ARGS_ASSERT_HSPLIT;
+
     /*PerlIO_printf(PerlIO_stderr(), "hsplit called for %p which had %d\n",
-      hv, (int) oldsize);*/
+      (void*)hv, (int) oldsize);*/
 
     if (HvPLACEHOLDERS_get(hv) && !SvREADONLY(hv)) {
       /* Can make this clear any placeholders first for non-restricted hashes,
@@ -1159,7 +1115,7 @@ S_hsplit(pTHX_ HV *hv)
       return;
     }
     if (SvOOK(hv)) {
-       Copy(&a[oldsize * sizeof(HE*)], &a[newsize * sizeof(HE*)], 1, struct xpvhv_aux);
+       Move(&a[oldsize * sizeof(HE*)], &a[newsize * sizeof(HE*)], 1, struct xpvhv_aux);
     }
 #else
     Newx(a, PERL_HV_ARRAY_ALLOC_BYTES(newsize)
@@ -1236,7 +1192,7 @@ S_hsplit(pTHX_ HV *hv)
     }
 
     /* Awooga. Awooga. Pathological data.  */
-    /*PerlIO_printf(PerlIO_stderr(), "%p %d of %d with %d/%d buckets\n", hv,
+    /*PerlIO_printf(PerlIO_stderr(), "%p %d of %d with %d/%d buckets\n", (void*)hv,
       longest_chain, HvTOTALKEYS(hv), HvFILL(hv),  1+HvMAX(hv));*/
 
     ++newsize;
@@ -1308,6 +1264,8 @@ Perl_hv_ksplit(pTHX_ HV *hv, IV newmax)
     register HE *entry;
     register HE **oentry;
 
+    PERL_ARGS_ASSERT_HV_KSPLIT;
+
     newsize = (I32) newmax;                    /* possible truncation here */
     if (newsize != newmax || newmax <= oldsize)
        return;
@@ -1385,33 +1343,6 @@ Perl_hv_ksplit(pTHX_ HV *hv, IV newmax)
     }
 }
 
-/*
-=for apidoc newHV
-
-Creates a new HV.  The reference count is set to 1.
-
-=cut
-*/
-
-HV *
-Perl_newHV(pTHX)
-{
-    register XPVHV* xhv;
-    HV * const hv = (HV*)newSV(0);
-
-    sv_upgrade((SV *)hv, SVt_PVHV);
-    xhv = (XPVHV*)SvANY(hv);
-    SvPOK_off(hv);
-    SvNOK_off(hv);
-#ifndef NODEFAULT_SHAREKEYS
-    HvSHAREKEYS_on(hv);         /* key-sharing on by default */
-#endif
-
-    xhv->xhv_max    = 7;       /* HvMAX(hv) = 7 (start with 8 buckets) */
-    xhv->xhv_fill   = 0;       /* HvFILL(hv) = 0 */
-    return hv;
-}
-
 HV *
 Perl_newHVhv(pTHX_ HV *ohv)
 {
@@ -1422,7 +1353,7 @@ Perl_newHVhv(pTHX_ HV *ohv)
        return hv;
     hv_max = HvMAX(ohv);
 
-    if (!SvMAGICAL((SV *)ohv)) {
+    if (!SvMAGICAL((const SV *)ohv)) {
        /* It's an ordinary hash, so copy it fast. AMS 20010804 */
        STRLEN i;
        const bool shared = !!HvSHAREKEYS(ohv);
@@ -1480,9 +1411,9 @@ Perl_newHVhv(pTHX_ HV *ohv)
 
        hv_iterinit(ohv);
        while ((entry = hv_iternext_flags(ohv, 0))) {
-           hv_store_flags(hv, HeKEY(entry), HeKLEN(entry),
-                           newSVsv(HeVAL(entry)), HeHASH(entry),
-                           HeKFLAGS(entry));
+           (void)hv_store_flags(hv, HeKEY(entry), HeKLEN(entry),
+                                newSVsv(HeVAL(entry)), HeHASH(entry),
+                                HeKFLAGS(entry));
        }
        HvRITER_set(ohv, riter);
        HvEITER_set(ohv, eiter);
@@ -1491,17 +1422,54 @@ Perl_newHVhv(pTHX_ HV *ohv)
     return hv;
 }
 
+/* A rather specialised version of newHVhv for copying %^H, ensuring all the
+   magic stays on it.  */
+HV *
+Perl_hv_copy_hints_hv(pTHX_ HV *const ohv)
+{
+    HV * const hv = newHV();
+    STRLEN hv_fill;
+
+    if (ohv && (hv_fill = HvFILL(ohv))) {
+       STRLEN hv_max = HvMAX(ohv);
+       HE *entry;
+       const I32 riter = HvRITER_get(ohv);
+       HE * const eiter = HvEITER_get(ohv);
+
+       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_flags(ohv, 0))) {
+           SV *const sv = newSVsv(HeVAL(entry));
+           SV *heksv = newSVhek(HeKEY_hek(entry));
+           sv_magic(sv, NULL, PERL_MAGIC_hintselem,
+                    (char *)heksv, HEf_SVKEY);
+           SvREFCNT_dec(heksv);
+           (void)hv_store_flags(hv, HeKEY(entry), HeKLEN(entry),
+                                sv, HeHASH(entry), HeKFLAGS(entry));
+       }
+       HvRITER_set(ohv, riter);
+       HvEITER_set(ohv, eiter);
+    }
+    hv_magic(hv, NULL, PERL_MAGIC_hints);
+    return hv;
+}
+
 void
 Perl_hv_free_ent(pTHX_ HV *hv, register HE *entry)
 {
     dVAR;
     SV *val;
 
+    PERL_ARGS_ASSERT_HV_FREE_ENT;
+
     if (!entry)
        return;
     val = HeVAL(entry);
-    if (val && isGV(val) && GvCVu(val) && HvNAME_get(hv))
-       PL_sub_generation++;    /* may be deletion of method from stash */
+    if (val && isGV(val) && isGV_with_GP(val) && GvCVu(val) && HvNAME_get(hv))
+        mro_method_changed_in(hv);     /* deletion of method from stash */
     SvREFCNT_dec(val);
     if (HeKLEN(entry) == HEf_SVKEY) {
        SvREFCNT_dec(HeKEY_sv(entry));
@@ -1518,6 +1486,9 @@ void
 Perl_hv_delayfree_ent(pTHX_ HV *hv, register HE *entry)
 {
     dVAR;
+
+    PERL_ARGS_ASSERT_HV_DELAYFREE_ENT;
+
     if (!entry)
        return;
     /* SvREFCNT_inc to counter the SvREFCNT_dec in hv_free_ent  */
@@ -1559,8 +1530,8 @@ Perl_hv_clear(pTHX_ HV *hv)
                    if (HeVAL(entry) && SvREADONLY(HeVAL(entry))) {
                        SV* const keysv = hv_iterkeysv(entry);
                        Perl_croak(aTHX_
-       "Attempt to delete readonly key '%"SVf"' from a restricted hash",
-                                  keysv);
+                                  "Attempt to delete readonly key '%"SVf"' from a restricted hash",
+                                  (void*)keysv);
                    }
                    SvREFCNT_dec(HeVAL(entry));
                    HeVAL(entry) = &PL_sv_placeholder;
@@ -1574,16 +1545,17 @@ Perl_hv_clear(pTHX_ HV *hv)
     hfreeentries(hv);
     HvPLACEHOLDERS_set(hv, 0);
     if (HvARRAY(hv))
-       (void)memzero(HvARRAY(hv),
-                     (xhv->xhv_max+1 /* HvMAX(hv)+1 */) * sizeof(HE*));
+       Zero(HvARRAY(hv), xhv->xhv_max+1 /* HvMAX(hv)+1 */, HE*);
 
     if (SvRMAGICAL(hv))
-       mg_clear((SV*)hv);
+       mg_clear(MUTABLE_SV(hv));
 
     HvHASKFLAGS_off(hv);
     HvREHASH_off(hv);
     reset:
     if (SvOOK(hv)) {
+        if(HvNAME_get(hv))
+            mro_isa_changed_in(hv);
        HvEITER_set(hv, NULL);
     }
 }
@@ -1608,6 +1580,8 @@ Perl_hv_clear_placeholders(pTHX_ HV *hv)
     dVAR;
     const U32 items = (U32)HvPLACEHOLDERS_get(hv);
 
+    PERL_ARGS_ASSERT_HV_CLEAR_PLACEHOLDERS;
+
     if (items)
        clear_placeholders(hv, items);
 }
@@ -1618,6 +1592,8 @@ S_clear_placeholders(pTHX_ HV *hv, U32 items)
     dVAR;
     I32 i;
 
+    PERL_ARGS_ASSERT_CLEAR_PLACEHOLDERS;
+
     if (items == 0)
        return;
 
@@ -1661,11 +1637,13 @@ STATIC void
 S_hfreeentries(pTHX_ HV *hv)
 {
     /* This is the array that we're going to restore  */
-    HE **orig_array;
+    HE **const orig_array = HvARRAY(hv);
     HEK *name;
     int attempts = 100;
 
-    if (!HvARRAY(hv))
+    PERL_ARGS_ASSERT_HFREEENTRIES;
+
+    if (!orig_array)
        return;
 
     if (SvOOK(hv)) {
@@ -1679,7 +1657,6 @@ S_hfreeentries(pTHX_ HV *hv)
        name = NULL;
     }
 
-    orig_array = HvARRAY(hv);
     /* orig_array remains unchanged throughout the loop. If after freeing all
        the entries it turns out that one of the little blighters has triggered
        an action that has caused HvARRAY to be re-allocated, then we set
@@ -1698,6 +1675,7 @@ S_hfreeentries(pTHX_ HV *hv)
 
        if (SvOOK(hv)) {
            HE *entry;
+            struct mro_meta *meta;
            struct xpvhv_aux *iter = HvAUX(hv);
            /* If there are weak references to this HV, we need to avoid
               freeing them up here.  In particular we need to keep the AV
@@ -1715,7 +1693,8 @@ S_hfreeentries(pTHX_ HV *hv)
                    SvREFCNT_dec(iter->xhv_backreferences);
 
                } else {
-                   sv_magic((SV*)hv, (SV*)iter->xhv_backreferences,
+                   sv_magic(MUTABLE_SV(hv),
+                            MUTABLE_SV(iter->xhv_backreferences),
                             PERL_MAGIC_backref, NULL, 0);
                }
                iter->xhv_backreferences = NULL;
@@ -1729,6 +1708,24 @@ S_hfreeentries(pTHX_ HV *hv)
            iter->xhv_riter = -1;       /* HvRITER(hv) = -1 */
            iter->xhv_eiter = NULL;     /* HvEITER(hv) = NULL */
 
+            if((meta = iter->xhv_mro_meta)) {
+               if (meta->mro_linear_all) {
+                   SvREFCNT_dec(MUTABLE_SV(meta->mro_linear_all));
+                   meta->mro_linear_all = NULL;
+                   /* This is just acting as a shortcut pointer.  */
+                   meta->mro_linear_current = NULL;
+               } else if (meta->mro_linear_current) {
+                   /* Only the current MRO is stored, so this owns the data.
+                    */
+                   SvREFCNT_dec(meta->mro_linear_current);
+                   meta->mro_linear_current = NULL;
+               }
+                if(meta->mro_nextmethod) SvREFCNT_dec(meta->mro_nextmethod);
+                SvREFCNT_dec(meta->isa);
+                Safefree(meta);
+                iter->xhv_mro_meta = NULL;
+            }
+
            /* There are now no allocated pointers in the aux structure.  */
 
            SvFLAGS(hv) &= ~SVf_OOK; /* Goodbye, aux structure.  */
@@ -1812,10 +1809,14 @@ Perl_hv_undef(pTHX_ HV *hv)
        return;
     DEBUG_A(Perl_hv_assert(aTHX_ hv));
     xhv = (XPVHV*)SvANY(hv);
+
+    if ((name = HvNAME_get(hv)) && !PL_dirty)
+        mro_isa_changed_in(hv);
+
     hfreeentries(hv);
-    if ((name = HvNAME_get(hv))) {
-        if(PL_stashcache)
-           hv_delete(PL_stashcache, name, HvNAMELEN_get(hv), G_DISCARD);
+    if (name) {
+        if (PL_stashcache)
+           (void)hv_delete(PL_stashcache, name, HvNAMELEN_get(hv), G_DISCARD);
        hv_name_set(hv, NULL, 0, 0);
     }
     SvFLAGS(hv) &= ~SVf_OOK;
@@ -1825,7 +1826,7 @@ Perl_hv_undef(pTHX_ HV *hv)
     HvPLACEHOLDERS_set(hv, 0);
 
     if (SvRMAGICAL(hv))
-       mg_clear((SV*)hv);
+       mg_clear(MUTABLE_SV(hv));
 }
 
 static struct xpvhv_aux*
@@ -1833,6 +1834,8 @@ S_hv_auxinit(HV *hv) {
     struct xpvhv_aux *iter;
     char *array;
 
+    PERL_ARGS_ASSERT_HV_AUXINIT;
+
     if (!HvARRAY(hv)) {
        Newxz(array, PERL_HV_ARRAY_ALLOC_BYTES(HvMAX(hv) + 1)
            + sizeof(struct xpvhv_aux), char);
@@ -1850,6 +1853,7 @@ S_hv_auxinit(HV *hv) {
     iter->xhv_eiter = NULL;    /* HvEITER(hv) = NULL */
     iter->xhv_name = 0;
     iter->xhv_backreferences = 0;
+    iter->xhv_mro_meta = NULL;
     return iter;
 }
 
@@ -1871,6 +1875,10 @@ value, you can get it through the macro C<HvFILL(tb)>.
 I32
 Perl_hv_iterinit(pTHX_ HV *hv)
 {
+    PERL_ARGS_ASSERT_HV_ITERINIT;
+
+    /* FIXME: Are we not NULL, or do we croak? Place bets now! */
+
     if (!hv)
        Perl_croak(aTHX_ "Bad hash");
 
@@ -1895,6 +1903,8 @@ I32 *
 Perl_hv_riter_p(pTHX_ HV *hv) {
     struct xpvhv_aux *iter;
 
+    PERL_ARGS_ASSERT_HV_RITER_P;
+
     if (!hv)
        Perl_croak(aTHX_ "Bad hash");
 
@@ -1906,6 +1916,8 @@ HE **
 Perl_hv_eiter_p(pTHX_ HV *hv) {
     struct xpvhv_aux *iter;
 
+    PERL_ARGS_ASSERT_HV_EITER_P;
+
     if (!hv)
        Perl_croak(aTHX_ "Bad hash");
 
@@ -1917,6 +1929,8 @@ void
 Perl_hv_riter_set(pTHX_ HV *hv, I32 riter) {
     struct xpvhv_aux *iter;
 
+    PERL_ARGS_ASSERT_HV_RITER_SET;
+
     if (!hv)
        Perl_croak(aTHX_ "Bad hash");
 
@@ -1935,6 +1949,8 @@ void
 Perl_hv_eiter_set(pTHX_ HV *hv, HE *eiter) {
     struct xpvhv_aux *iter;
 
+    PERL_ARGS_ASSERT_HV_EITER_SET;
+
     if (!hv)
        Perl_croak(aTHX_ "Bad hash");
 
@@ -1958,6 +1974,7 @@ Perl_hv_name_set(pTHX_ HV *hv, const char *name, U32 len, U32 flags)
     struct xpvhv_aux *iter;
     U32 hash;
 
+    PERL_ARGS_ASSERT_HV_NAME_SET;
     PERL_UNUSED_ARG(flags);
 
     if (len > I32_MAX)
@@ -1975,13 +1992,16 @@ Perl_hv_name_set(pTHX_ HV *hv, const char *name, U32 len, U32 flags)
        iter = hv_auxinit(hv);
     }
     PERL_HASH(hash, name, len);
-    iter->xhv_name = name ? share_hek(name, len, hash) : 0;
+    iter->xhv_name = name ? share_hek(name, len, hash) : NULL;
 }
 
 AV **
 Perl_hv_backreferences_p(pTHX_ HV *hv) {
     struct xpvhv_aux * const iter = SvOOK(hv) ? HvAUX(hv) : hv_auxinit(hv);
+
+    PERL_ARGS_ASSERT_HV_BACKREFERENCES_P;
     PERL_UNUSED_CONTEXT;
+
     return &(iter->xhv_backreferences);
 }
 
@@ -1989,6 +2009,8 @@ void
 Perl_hv_kill_backrefs(pTHX_ HV *hv) {
     AV *av;
 
+    PERL_ARGS_ASSERT_HV_KILL_BACKREFS;
+
     if (!SvOOK(hv))
        return;
 
@@ -1996,7 +2018,8 @@ Perl_hv_kill_backrefs(pTHX_ HV *hv) {
 
     if (av) {
        HvAUX(hv)->xhv_backreferences = 0;
-       Perl_sv_kill_backrefs(aTHX_ (SV*) hv, av);
+       Perl_sv_kill_backrefs(aTHX_ MUTABLE_SV(hv), av);
+       SvREFCNT_dec(av);
     }
 }
 
@@ -2039,8 +2062,11 @@ Perl_hv_iternext_flags(pTHX_ HV *hv, I32 flags)
     MAGIC* mg;
     struct xpvhv_aux *iter;
 
+    PERL_ARGS_ASSERT_HV_ITERNEXT_FLAGS;
+
     if (!hv)
        Perl_croak(aTHX_ "Bad hash");
+
     xhv = (XPVHV*)SvANY(hv);
 
     if (!SvOOK(hv)) {
@@ -2052,40 +2078,42 @@ Perl_hv_iternext_flags(pTHX_ HV *hv, I32 flags)
     iter = HvAUX(hv);
 
     oldentry = entry = iter->xhv_eiter; /* HvEITER(hv) */
-
-    if ((mg = SvTIED_mg((SV*)hv, PERL_MAGIC_tied))) {
-       SV * const key = sv_newmortal();
-       if (entry) {
-           sv_setsv(key, HeSVKEY_force(entry));
-           SvREFCNT_dec(HeSVKEY(entry));       /* get rid of previous key */
-       }
-       else {
-           char *k;
-           HEK *hek;
-
-           /* one HE per MAGICAL hash */
-           iter->xhv_eiter = entry = new_HE(); /* HvEITER(hv) = new_HE() */
-           Zero(entry, 1, HE);
-           Newxz(k, HEK_BASESIZE + sizeof(SV*), char);
-           hek = (HEK*)k;
-           HeKEY_hek(entry) = hek;
-           HeKLEN(entry) = HEf_SVKEY;
-       }
-       magic_nextpack((SV*) hv,mg,key);
-       if (SvOK(key)) {
-           /* force key to stay around until next time */
-           HeSVKEY_set(entry, SvREFCNT_inc_simple_NN(key));
-           return entry;               /* beware, hent_val is not set */
-       }
-       if (HeVAL(entry))
-           SvREFCNT_dec(HeVAL(entry));
-       Safefree(HeKEY_hek(entry));
-       del_HE(entry);
-       iter->xhv_eiter = NULL; /* HvEITER(hv) = NULL */
-       return NULL;
+    if (SvMAGICAL(hv) && SvRMAGICAL(hv)) {
+       if ( ( mg = mg_find((const SV *)hv, PERL_MAGIC_tied) ) ) {
+            SV * const key = sv_newmortal();
+            if (entry) {
+                sv_setsv(key, HeSVKEY_force(entry));
+                SvREFCNT_dec(HeSVKEY(entry));       /* get rid of previous key */
+            }
+            else {
+                char *k;
+                HEK *hek;
+
+                /* one HE per MAGICAL hash */
+                iter->xhv_eiter = entry = new_HE(); /* HvEITER(hv) = new_HE() */
+                Zero(entry, 1, HE);
+                Newxz(k, HEK_BASESIZE + sizeof(const SV *), char);
+                hek = (HEK*)k;
+                HeKEY_hek(entry) = hek;
+                HeKLEN(entry) = HEf_SVKEY;
+            }
+            magic_nextpack(MUTABLE_SV(hv),mg,key);
+            if (SvOK(key)) {
+                /* force key to stay around until next time */
+                HeSVKEY_set(entry, SvREFCNT_inc_simple_NN(key));
+                return entry;               /* beware, hent_val is not set */
+            }
+            if (HeVAL(entry))
+                SvREFCNT_dec(HeVAL(entry));
+            Safefree(HeKEY_hek(entry));
+            del_HE(entry);
+            iter->xhv_eiter = NULL; /* HvEITER(hv) = NULL */
+            return NULL;
+        }
     }
-#ifdef DYNAMIC_ENV_FETCH  /* set up %ENV for iteration */
-    if (!entry && SvRMAGICAL((SV*)hv) && mg_find((SV*)hv, PERL_MAGIC_env)) {
+#if defined(DYNAMIC_ENV_FETCH) && !defined(__riscos__)  /* set up %ENV for iteration */
+    if (!entry && SvRMAGICAL((const SV *)hv)
+       && mg_find((const SV *)hv, PERL_MAGIC_env)) {
        prime_env_iter();
 #ifdef VMS
        /* The prime_env_iter() on VMS just loaded up new hash values
@@ -2115,26 +2143,32 @@ Perl_hv_iternext_flags(pTHX_ HV *hv, I32 flags)
             }
        }
     }
-    while (!entry) {
-       /* OK. Come to the end of the current list.  Grab the next one.  */
 
-       iter->xhv_riter++; /* HvRITER(hv)++ */
-       if (iter->xhv_riter > (I32)xhv->xhv_max /* HvRITER(hv) > HvMAX(hv) */) {
-           /* There is no next one.  End of the hash.  */
-           iter->xhv_riter = -1; /* HvRITER(hv) = -1 */
-           break;
-       }
-       entry = (HvARRAY(hv))[iter->xhv_riter];
+    /* Quick bailout if the hash is empty anyway.
+       I don't know if placeholders are included in the KEYS count, so a defensive check
+    */
+    if (HvKEYS(hv) || (flags & HV_ITERNEXT_WANTPLACEHOLDERS)) {
+       while (!entry) {
+           /* OK. Come to the end of the current list.  Grab the next one.  */
+
+           iter->xhv_riter++; /* HvRITER(hv)++ */
+           if (iter->xhv_riter > (I32)xhv->xhv_max /* HvRITER(hv) > HvMAX(hv) */) {
+               /* There is no next one.  End of the hash.  */
+               iter->xhv_riter = -1; /* HvRITER(hv) = -1 */
+               break;
+           }
+           entry = (HvARRAY(hv))[iter->xhv_riter];
 
-        if (!(flags & HV_ITERNEXT_WANTPLACEHOLDERS)) {
-            /* If we have an entry, but it's a placeholder, don't count it.
-              Try the next.  */
-           while (entry && HeVAL(entry) == &PL_sv_placeholder)
-               entry = HeNEXT(entry);
+           if (!(flags & HV_ITERNEXT_WANTPLACEHOLDERS)) {
+               /* If we have an entry, but it's a placeholder, don't count it.
+                  Try the next.  */
+               while (entry && HeVAL(entry) == &PL_sv_placeholder)
+                   entry = HeNEXT(entry);
+           }
+           /* Will loop again if this linked list starts NULL
+              (for HV_ITERNEXT_WANTPLACEHOLDERS)
+              or if we run through it and find only placeholders.  */
        }
-       /* Will loop again if this linked list starts NULL
-          (for HV_ITERNEXT_WANTPLACEHOLDERS)
-          or if we run through it and find only placeholders.  */
     }
 
     if (oldentry && HvLAZYDEL(hv)) {           /* was deleted earlier? */
@@ -2143,7 +2177,7 @@ Perl_hv_iternext_flags(pTHX_ HV *hv, I32 flags)
     }
 
     /*if (HvREHASH(hv) && entry && !HeKREHASH(entry))
-      PerlIO_printf(PerlIO_stderr(), "Awooga %p %p\n", hv, entry);*/
+      PerlIO_printf(PerlIO_stderr(), "Awooga %p %p\n", (void*)hv, (void*)entry);*/
 
     iter->xhv_eiter = entry; /* HvEITER(hv) = entry */
     return entry;
@@ -2161,6 +2195,8 @@ C<hv_iterinit>.
 char *
 Perl_hv_iterkey(pTHX_ register HE *entry, I32 *retlen)
 {
+    PERL_ARGS_ASSERT_HV_ITERKEY;
+
     if (HeKLEN(entry) == HEf_SVKEY) {
        STRLEN len;
        char * const p = SvPV(HeKEY_sv(entry), len);
@@ -2187,6 +2223,8 @@ see C<hv_iterinit>.
 SV *
 Perl_hv_iterkeysv(pTHX_ register HE *entry)
 {
+    PERL_ARGS_ASSERT_HV_ITERKEYSV;
+
     return sv_2mortal(newSVhek(HeKEY_hek(entry)));
 }
 
@@ -2202,13 +2240,15 @@ C<hv_iterkey>.
 SV *
 Perl_hv_iterval(pTHX_ HV *hv, register HE *entry)
 {
+    PERL_ARGS_ASSERT_HV_ITERVAL;
+
     if (SvRMAGICAL(hv)) {
-       if (mg_find((SV*)hv, PERL_MAGIC_tied)) {
+       if (mg_find((const SV *)hv, PERL_MAGIC_tied)) {
            SV* const sv = sv_newmortal();
            if (HeKLEN(entry) == HEf_SVKEY)
-               mg_copy((SV*)hv, sv, (char*)HeKEY_sv(entry), HEf_SVKEY);
+               mg_copy(MUTABLE_SV(hv), sv, (char*)HeKEY_sv(entry), HEf_SVKEY);
            else
-               mg_copy((SV*)hv, sv, HeKEY(entry), HeKLEN(entry));
+               mg_copy(MUTABLE_SV(hv), sv, HeKEY(entry), HeKLEN(entry));
            return sv;
        }
     }
@@ -2229,6 +2269,8 @@ Perl_hv_iternextsv(pTHX_ HV *hv, char **key, I32 *retlen)
 {
     HE * const he = hv_iternext_flags(hv, 0);
 
+    PERL_ARGS_ASSERT_HV_ITERNEXTSV;
+
     if (!he)
        return NULL;
     *key = hv_iterkey(he, retlen);
@@ -2259,6 +2301,7 @@ Perl_unsharepvn(pTHX_ const char *str, I32 len, U32 hash)
 void
 Perl_unshare_hek(pTHX_ HEK *hek)
 {
+    assert(hek);
     unshare_hek_or_pvn(hek, NULL, 0, 0);
 }
 
@@ -2274,7 +2317,6 @@ S_unshare_hek_or_pvn(pTHX_ const HEK *hek, const char *str, I32 len, U32 hash)
     HE *entry;
     register HE **oentry;
     HE **first;
-    bool found = 0;
     bool is_utf8 = FALSE;
     int k_flags = 0;
     const char * const save = str;
@@ -2290,13 +2332,10 @@ S_unshare_hek_or_pvn(pTHX_ const HEK *hek, const char *str, I32 len, U32 hash)
           shared hek  */
        assert (he->shared_he_he.hent_hek == hek);
 
-       LOCK_STRTAB_MUTEX;
        if (he->shared_he_he.he_valu.hent_refcount - 1) {
            --he->shared_he_he.he_valu.hent_refcount;
-           UNLOCK_STRTAB_MUTEX;
            return;
        }
-       UNLOCK_STRTAB_MUTEX;
 
         hash = HEK_HASH(hek);
     } else if (len < 0) {
@@ -2318,15 +2357,12 @@ S_unshare_hek_or_pvn(pTHX_ const HEK *hek, const char *str, I32 len, U32 hash)
     } */
     xhv = (XPVHV*)SvANY(PL_strtab);
     /* assert(xhv_array != 0) */
-    LOCK_STRTAB_MUTEX;
     first = oentry = &(HvARRAY(PL_strtab))[hash & (I32) HvMAX(PL_strtab)];
     if (he) {
        const HE *const he_he = &(he->shared_he_he);
         for (entry = *oentry; entry; oentry = &HeNEXT(entry), entry = *oentry) {
-            if (entry != he_he)
-                continue;
-            found = 1;
-            break;
+            if (entry == he_he)
+                break;
         }
     } else {
         const int flags_masked = k_flags & HVhek_MASK;
@@ -2339,13 +2375,12 @@ S_unshare_hek_or_pvn(pTHX_ const HEK *hek, const char *str, I32 len, U32 hash)
                 continue;
             if (HeKFLAGS(entry) != flags_masked)
                 continue;
-            found = 1;
             break;
         }
     }
 
-    if (found) {
-        if (--he->shared_he_he.he_valu.hent_refcount == 0) {
+    if (entry) {
+        if (--entry->he_valu.hent_refcount == 0) {
             *oentry = HeNEXT(entry);
             if (!*first) {
                /* There are now no entries in our slot.  */
@@ -2356,8 +2391,7 @@ S_unshare_hek_or_pvn(pTHX_ const HEK *hek, const char *str, I32 len, U32 hash)
         }
     }
 
-    UNLOCK_STRTAB_MUTEX;
-    if (!found && ckWARN_d(WARN_INTERNAL))
+    if (!entry && ckWARN_d(WARN_INTERNAL))
        Perl_warner(aTHX_ packWARN(WARN_INTERNAL),
                     "Attempt to free non-existent shared string '%s'%s"
                     pTHX__FORMAT,
@@ -2378,6 +2412,8 @@ Perl_share_hek(pTHX_ const char *str, I32 len, register U32 hash)
     int flags = 0;
     const char * const save = str;
 
+    PERL_ARGS_ASSERT_SHARE_HEK;
+
     if (len < 0) {
       STRLEN tmplen = -len;
       is_utf8 = TRUE;
@@ -2405,6 +2441,9 @@ S_share_hek_flags(pTHX_ const char *str, I32 len, register U32 hash, int flags)
     register HE *entry;
     const int flags_masked = flags & HVhek_MASK;
     const U32 hindex = hash & (I32) HvMAX(PL_strtab);
+    register XPVHV * const xhv = (XPVHV*)SvANY(PL_strtab);
+
+    PERL_ARGS_ASSERT_SHARE_HEK_FLAGS;
 
     /* what follows is the moral equivalent of:
 
@@ -2414,9 +2453,8 @@ S_share_hek_flags(pTHX_ const char *str, I32 len, register U32 hash, int flags)
        Can't rehash the shared string table, so not sure if it's worth
        counting the number of entries in the linked list
     */
-    register XPVHV * const xhv = (XPVHV*)SvANY(PL_strtab);
+
     /* assert(xhv_array != 0) */
-    LOCK_STRTAB_MUTEX;
     entry = (HvARRAY(PL_strtab))[hindex];
     for (;entry; entry = HeNEXT(entry)) {
        if (HeHASH(entry) != hash)              /* strings can't be equal */
@@ -2474,7 +2512,6 @@ S_share_hek_flags(pTHX_ const char *str, I32 len, register U32 hash, int flags)
     }
 
     ++entry->he_valu.hent_refcount;
-    UNLOCK_STRTAB_MUTEX;
 
     if (flags & HVhek_FREEKEY)
        Safefree(str);
@@ -2486,10 +2523,12 @@ I32 *
 Perl_hv_placeholders_p(pTHX_ HV *hv)
 {
     dVAR;
-    MAGIC *mg = mg_find((SV*)hv, PERL_MAGIC_rhash);
+    MAGIC *mg = mg_find((const SV *)hv, PERL_MAGIC_rhash);
+
+    PERL_ARGS_ASSERT_HV_PLACEHOLDERS_P;
 
     if (!mg) {
-       mg = sv_magicext((SV*)hv, 0, PERL_MAGIC_rhash, 0, 0, 0);
+       mg = sv_magicext(MUTABLE_SV(hv), 0, PERL_MAGIC_rhash, 0, 0, 0);
 
        if (!mg) {
            Perl_die(aTHX_ "panic: hv_placeholders_p");
@@ -2500,10 +2539,12 @@ Perl_hv_placeholders_p(pTHX_ HV *hv)
 
 
 I32
-Perl_hv_placeholders_get(pTHX_ HV *hv)
+Perl_hv_placeholders_get(pTHX_ const HV *hv)
 {
     dVAR;
-    MAGIC * const mg = mg_find((SV*)hv, PERL_MAGIC_rhash);
+    MAGIC * const mg = mg_find((const SV *)hv, PERL_MAGIC_rhash);
+
+    PERL_ARGS_ASSERT_HV_PLACEHOLDERS_GET;
 
     return mg ? mg->mg_len : 0;
 }
@@ -2512,21 +2553,65 @@ void
 Perl_hv_placeholders_set(pTHX_ HV *hv, I32 ph)
 {
     dVAR;
-    MAGIC * const mg = mg_find((SV*)hv, PERL_MAGIC_rhash);
+    MAGIC * const mg = mg_find((const SV *)hv, PERL_MAGIC_rhash);
+
+    PERL_ARGS_ASSERT_HV_PLACEHOLDERS_SET;
 
     if (mg) {
        mg->mg_len = ph;
     } else if (ph) {
-       if (!sv_magicext((SV*)hv, 0, PERL_MAGIC_rhash, 0, 0, ph))
+       if (!sv_magicext(MUTABLE_SV(hv), 0, PERL_MAGIC_rhash, 0, 0, ph))
            Perl_die(aTHX_ "panic: hv_placeholders_set");
     }
     /* else we don't need to add magic to record 0 placeholders.  */
 }
 
+STATIC SV *
+S_refcounted_he_value(pTHX_ const struct refcounted_he *he)
+{
+    dVAR;
+    SV *value;
+
+    PERL_ARGS_ASSERT_REFCOUNTED_HE_VALUE;
+
+    switch(he->refcounted_he_data[0] & HVrhek_typemask) {
+    case HVrhek_undef:
+       value = newSV(0);
+       break;
+    case HVrhek_delete:
+       value = &PL_sv_placeholder;
+       break;
+    case HVrhek_IV:
+       value = newSViv(he->refcounted_he_val.refcounted_he_u_iv);
+       break;
+    case HVrhek_UV:
+       value = newSVuv(he->refcounted_he_val.refcounted_he_u_uv);
+       break;
+    case HVrhek_PV:
+    case HVrhek_PV_UTF8:
+       /* Create a string SV that directly points to the bytes in our
+          structure.  */
+       value = newSV_type(SVt_PV);
+       SvPV_set(value, (char *) he->refcounted_he_data + 1);
+       SvCUR_set(value, he->refcounted_he_val.refcounted_he_u_len);
+       /* This stops anything trying to free it  */
+       SvLEN_set(value, 0);
+       SvPOK_on(value);
+       SvREADONLY_on(value);
+       if ((he->refcounted_he_data[0] & HVrhek_typemask) == HVrhek_PV_UTF8)
+           SvUTF8_on(value);
+       break;
+    default:
+       Perl_croak(aTHX_ "panic: refcounted_he_value bad flags %x",
+                  he->refcounted_he_data[0]);
+    }
+    return value;
+}
+
 /*
 =for apidoc refcounted_he_chain_2hv
 
-Generates an returns a C<HV *> by walking up the tree starting at the passed
+Generates and returns a C<HV *> by walking up the tree starting at the passed
 in C<struct refcounted_he *>.
 
 =cut
@@ -2534,6 +2619,7 @@ in C<struct refcounted_he *>.
 HV *
 Perl_refcounted_he_chain_2hv(pTHX_ const struct refcounted_he *chain)
 {
+    dVAR;
     HV *hv = newHV();
     U32 placeholders = 0;
     /* We could chase the chain once to get an idea of the number of keys,
@@ -2548,24 +2634,56 @@ Perl_refcounted_he_chain_2hv(pTHX_ const struct refcounted_he *chain)
     }
 
     while (chain) {
-       const U32 hash = HEK_HASH(chain->refcounted_he_he.hent_hek);
+#ifdef USE_ITHREADS
+       U32 hash = chain->refcounted_he_hash;
+#else
+       U32 hash = HEK_HASH(chain->refcounted_he_hek);
+#endif
        HE **oentry = &((HvARRAY(hv))[hash & max]);
        HE *entry = *oentry;
+       SV *value;
 
        for (; entry; entry = HeNEXT(entry)) {
            if (HeHASH(entry) == hash) {
-               goto next_please;
+               /* We might have a duplicate key here.  If so, entry is older
+                  than the key we've already put in the hash, so if they are
+                  the same, skip adding entry.  */
+#ifdef USE_ITHREADS
+               const STRLEN klen = HeKLEN(entry);
+               const char *const key = HeKEY(entry);
+               if (klen == chain->refcounted_he_keylen
+                   && (!!HeKUTF8(entry)
+                       == !!(chain->refcounted_he_data[0] & HVhek_UTF8))
+                   && memEQ(key, REF_HE_KEY(chain), klen))
+                   goto next_please;
+#else
+               if (HeKEY_hek(entry) == chain->refcounted_he_hek)
+                   goto next_please;
+               if (HeKLEN(entry) == HEK_LEN(chain->refcounted_he_hek)
+                   && HeKUTF8(entry) == HEK_UTF8(chain->refcounted_he_hek)
+                   && memEQ(HeKEY(entry), HEK_KEY(chain->refcounted_he_hek),
+                            HeKLEN(entry)))
+                   goto next_please;
+#endif
            }
        }
        assert (!entry);
        entry = new_HE();
 
-       HeKEY_hek(entry) = share_hek_hek(chain->refcounted_he_he.hent_hek);
-
-       HeVAL(entry) = chain->refcounted_he_he.he_valu.hent_val;
-       if (HeVAL(entry) == &PL_sv_placeholder)
+#ifdef USE_ITHREADS
+       HeKEY_hek(entry)
+           = share_hek_flags(REF_HE_KEY(chain),
+                             chain->refcounted_he_keylen,
+                             chain->refcounted_he_hash,
+                             (chain->refcounted_he_data[0]
+                              & (HVhek_UTF8|HVhek_WASUTF8)));
+#else
+       HeKEY_hek(entry) = share_hek_hek(chain->refcounted_he_hek);
+#endif
+       value = refcounted_he_value(chain);
+       if (value == &PL_sv_placeholder)
            placeholders++;
-       SvREFCNT_inc_void_NN(HeVAL(entry));
+       HeVAL(entry) = value;
 
        /* Link it into the chain.  */
        HeNEXT(entry) = *oentry;
@@ -2578,7 +2696,7 @@ Perl_refcounted_he_chain_2hv(pTHX_ const struct refcounted_he *chain)
        HvTOTALKEYS(hv)++;
 
     next_please:
-       chain = (struct refcounted_he *) chain->refcounted_he_he.hent_next;
+       chain = chain->refcounted_he_next;
     }
 
     if (placeholders) {
@@ -2590,20 +2708,80 @@ Perl_refcounted_he_chain_2hv(pTHX_ const struct refcounted_he *chain)
        flags, but it's probably not worth it, as this per-hash flag is only
        really meant as an optimisation for things like Storable.  */
     HvHASKFLAGS_on(hv);
-#ifdef DEBUGGING
-    Perl_hv_assert(aTHX_ hv);
-#endif
+    DEBUG_A(Perl_hv_assert(aTHX_ hv));
 
     return hv;
 }
 
+SV *
+Perl_refcounted_he_fetch(pTHX_ const struct refcounted_he *chain, SV *keysv,
+                        const char *key, STRLEN klen, int flags, U32 hash)
+{
+    dVAR;
+    /* Just to be awkward, if you're using this interface the UTF-8-or-not-ness
+       of your key has to exactly match that which is stored.  */
+    SV *value = &PL_sv_placeholder;
+
+    if (chain) {
+       /* No point in doing any of this if there's nothing to find.  */
+       bool is_utf8;
+
+       if (keysv) {
+           if (flags & HVhek_FREEKEY)
+               Safefree(key);
+           key = SvPV_const(keysv, klen);
+           flags = 0;
+           is_utf8 = (SvUTF8(keysv) != 0);
+       } else {
+           is_utf8 = ((flags & HVhek_UTF8) ? TRUE : FALSE);
+       }
+
+       if (!hash) {
+           if (keysv && (SvIsCOW_shared_hash(keysv))) {
+               hash = SvSHARED_HASH(keysv);
+           } else {
+               PERL_HASH(hash, key, klen);
+           }
+       }
+
+       for (; chain; chain = chain->refcounted_he_next) {
+#ifdef USE_ITHREADS
+           if (hash != chain->refcounted_he_hash)
+               continue;
+           if (klen != chain->refcounted_he_keylen)
+               continue;
+           if (memNE(REF_HE_KEY(chain),key,klen))
+               continue;
+           if (!!is_utf8 != !!(chain->refcounted_he_data[0] & HVhek_UTF8))
+               continue;
+#else
+           if (hash != HEK_HASH(chain->refcounted_he_hek))
+               continue;
+           if (klen != (STRLEN)HEK_LEN(chain->refcounted_he_hek))
+               continue;
+           if (memNE(HEK_KEY(chain->refcounted_he_hek),key,klen))
+               continue;
+           if (!!is_utf8 != !!HEK_UTF8(chain->refcounted_he_hek))
+               continue;
+#endif
+
+           value = sv_2mortal(refcounted_he_value(chain));
+           break;
+       }
+    }
+
+    if (flags & HVhek_FREEKEY)
+       Safefree(key);
+
+    return value;
+}
+
 /*
 =for apidoc refcounted_he_new
 
-Creates a new C<struct refcounted_he>. Assumes ownership of one reference
-to I<value>. As S<key> is copied into a shared hash key, all references remain
-the property of the caller. The C<struct refcounted_he> is returned with a
-reference count of 1.
+Creates a new C<struct refcounted_he>. As S<key> is copied, and value is
+stored in a compact form, all references remain the property of the caller.
+The C<struct refcounted_he> is returned with a reference count of 1.
 
 =cut
 */
@@ -2611,19 +2789,103 @@ reference count of 1.
 struct refcounted_he *
 Perl_refcounted_he_new(pTHX_ struct refcounted_he *const parent,
                       SV *const key, SV *const value) {
+    dVAR;
+    STRLEN key_len;
+    const char *key_p = SvPV_const(key, key_len);
+    STRLEN value_len = 0;
+    const char *value_p = NULL;
+    char value_type;
+    char flags;
+    bool is_utf8 = SvUTF8(key) ? TRUE : FALSE;
+
+    if (SvPOK(value)) {
+       value_type = HVrhek_PV;
+    } else if (SvIOK(value)) {
+       value_type = SvUOK((const SV *)value) ? HVrhek_UV : HVrhek_IV;
+    } else if (value == &PL_sv_placeholder) {
+       value_type = HVrhek_delete;
+    } else if (!SvOK(value)) {
+       value_type = HVrhek_undef;
+    } else {
+       value_type = HVrhek_PV;
+    }
+
+    if (value_type == HVrhek_PV) {
+       /* Do it this way so that the SvUTF8() test is after the SvPV, in case
+          the value is overloaded, and doesn't yet have the UTF-8flag set.  */
+       value_p = SvPV_const(value, value_len);
+       if (SvUTF8(value))
+           value_type = HVrhek_PV_UTF8;
+    }
+    flags = value_type;
+
+    if (is_utf8) {
+       /* Hash keys are always stored normalised to (yes) ISO-8859-1.
+          As we're going to be building hash keys from this value in future,
+          normalise it now.  */
+       key_p = (char*)bytes_from_utf8((const U8*)key_p, &key_len, &is_utf8);
+       flags |= is_utf8 ? HVhek_UTF8 : HVhek_WASUTF8;
+    }
+
+    return refcounted_he_new_common(parent, key_p, key_len, flags, value_type,
+                                   ((value_type == HVrhek_PV
+                                     || value_type == HVrhek_PV_UTF8) ?
+                                    (void *)value_p : (void *)value),
+                                   value_len);
+}
+
+static struct refcounted_he *
+S_refcounted_he_new_common(pTHX_ struct refcounted_he *const parent,
+                          const char *const key_p, const STRLEN key_len,
+                          const char flags, char value_type,
+                          const void *value, const STRLEN value_len) {
+    dVAR;
     struct refcounted_he *he;
     U32 hash;
-    STRLEN len;
-    const char *p = SvPV_const(key, len);
+    const bool is_pv = value_type == HVrhek_PV || value_type == HVrhek_PV_UTF8;
+    STRLEN key_offset = is_pv ? value_len + 2 : 1;
+
+    PERL_ARGS_ASSERT_REFCOUNTED_HE_NEW_COMMON;
 
-    PERL_HASH(hash, p, len);
+#ifdef USE_ITHREADS
+    he = (struct refcounted_he*)
+       PerlMemShared_malloc(sizeof(struct refcounted_he) - 1
+                            + key_len
+                            + key_offset);
+#else
+    he = (struct refcounted_he*)
+       PerlMemShared_malloc(sizeof(struct refcounted_he) - 1
+                            + key_offset);
+#endif
+
+    he->refcounted_he_next = parent;
 
-    Newx(he, 1, struct refcounted_he);
+    if (is_pv) {
+       Copy((char *)value, he->refcounted_he_data + 1, value_len + 1, char);
+       he->refcounted_he_val.refcounted_he_u_len = value_len;
+    } else if (value_type == HVrhek_IV) {
+       he->refcounted_he_val.refcounted_he_u_iv = SvIVX((const SV *)value);
+    } else if (value_type == HVrhek_UV) {
+       he->refcounted_he_val.refcounted_he_u_uv = SvUVX((const SV *)value);
+    }
+
+    PERL_HASH(hash, key_p, key_len);
 
-    he->refcounted_he_he.hent_next = (HE *)parent;
-    he->refcounted_he_he.he_valu.hent_val = value;
-    he->refcounted_he_he.hent_hek
-       = share_hek(p, SvUTF8(key) ? -(I32)len : len, hash);
+#ifdef USE_ITHREADS
+    he->refcounted_he_hash = hash;
+    he->refcounted_he_keylen = key_len;
+    Copy(key_p, he->refcounted_he_data + key_offset, key_len, char);
+#else
+    he->refcounted_he_hek = share_hek_flags(key_p, key_len, hash, flags);
+#endif
+
+    if (flags & HVhek_WASUTF8) {
+       /* If it was downgraded from UTF-8, then the pointer returned from
+          bytes_from_utf8 is an allocated pointer that we must free.  */
+       Safefree(key_p);
+    }
+
+    he->refcounted_he_data[0] = flags;
     he->refcounted_he_refcnt = 1;
 
     return he;
@@ -2641,94 +2903,72 @@ and C<refcounted_he_free> iterates onto the parent node.
 
 void
 Perl_refcounted_he_free(pTHX_ struct refcounted_he *he) {
+    dVAR;
+    PERL_UNUSED_CONTEXT;
+
     while (he) {
        struct refcounted_he *copy;
+       U32 new_count;
 
-       if (--he->refcounted_he_refcnt)
+       HINTS_REFCNT_LOCK;
+       new_count = --he->refcounted_he_refcnt;
+       HINTS_REFCNT_UNLOCK;
+       
+       if (new_count) {
            return;
+       }
 
-       unshare_hek_or_pvn (he->refcounted_he_he.hent_hek, 0, 0, 0);
-       SvREFCNT_dec(he->refcounted_he_he.he_valu.hent_val);
+#ifndef USE_ITHREADS
+       unshare_hek_or_pvn (he->refcounted_he_hek, 0, 0, 0);
+#endif
        copy = he;
-       he = (struct refcounted_he *) he->refcounted_he_he.hent_next;
-       Safefree(copy);
+       he = he->refcounted_he_next;
+       PerlMemShared_free(copy);
     }
 }
 
-
-/*
-=for apidoc refcounted_he_dup
-
-Duplicates the C<struct refcounted_he *> for a new thread.
-
-=cut
-*/
-
-#if defined(USE_ITHREADS)
-struct refcounted_he *
-Perl_refcounted_he_dup(pTHX_ const struct refcounted_he *const he,
-                       CLONE_PARAMS* param)
-{
-    struct refcounted_he *copy;
-
-    if (!he)
+const char *
+Perl_fetch_cop_label(pTHX_ struct refcounted_he *const chain, STRLEN *len,
+                    U32 *flags) {
+    if (!chain)
+       return NULL;
+#ifdef USE_ITHREADS
+    if (chain->refcounted_he_keylen != 1)
+       return NULL;
+    if (*REF_HE_KEY(chain) != ':')
+       return NULL;
+#else
+    if ((STRLEN)HEK_LEN(chain->refcounted_he_hek) != 1)
+       return NULL;
+    if (*HEK_KEY(chain->refcounted_he_hek) != ':')
+       return NULL;
+#endif
+    /* Stop anyone trying to really mess us up by adding their own value for
+       ':' into %^H  */
+    if ((chain->refcounted_he_data[0] & HVrhek_typemask) != HVrhek_PV
+       && (chain->refcounted_he_data[0] & HVrhek_typemask) != HVrhek_PV_UTF8)
        return NULL;
 
-    /* look for it in the table first */
-    copy = (struct refcounted_he *)ptr_table_fetch(PL_ptr_table, he);
-    if (copy)
-       return copy;
-
-    /* create anew and remember what it is */
-    Newx(copy, 1, struct refcounted_he);
-    ptr_table_store(PL_ptr_table, he, copy);
-
-    copy->refcounted_he_he.hent_next
-       = (HE *)Perl_refcounted_he_dup(aTHX_
-                                      (struct refcounted_he *)
-                                      he->refcounted_he_he.hent_next,
-                                      param);
-    copy->refcounted_he_he.he_valu.hent_val
-       = SvREFCNT_inc(sv_dup(he->refcounted_he_he.he_valu.hent_val, param));
-    copy->refcounted_he_he.hent_hek
-       = hek_dup(he->refcounted_he_he.hent_hek, param);
-    copy->refcounted_he_refcnt = he->refcounted_he_refcnt;
-    return copy;
+    if (len)
+       *len = chain->refcounted_he_val.refcounted_he_u_len;
+    if (flags) {
+       *flags = ((chain->refcounted_he_data[0] & HVrhek_typemask)
+                 == HVrhek_PV_UTF8) ? SVf_UTF8 : 0;
+    }
+    return chain->refcounted_he_data + 1;
 }
 
-/*
-=for apidoc refcounted_he_copy
-
-Copies a chain of C<struct refcounted_he *>. Used by C<pp_entereval>.
-
-=cut
-*/
-
+/* As newSTATEOP currently gets passed plain char* labels, we will only provide
+   that interface. Once it works out how to pass in length and UTF-8 ness, this
+   function will need superseding.  */
 struct refcounted_he *
-Perl_refcounted_he_copy(pTHX_ const struct refcounted_he * he)
+Perl_store_cop_label(pTHX_ struct refcounted_he *const chain, const char *label)
 {
-    struct refcounted_he *copy;
-    HEK *hek;
-    /* This is much easier to express recursively than iteratively.  */
-    if (!he)
-       return NULL;
+    PERL_ARGS_ASSERT_STORE_COP_LABEL;
 
-    Newx(copy, 1, struct refcounted_he);
-    copy->refcounted_he_he.hent_next
-       = (HE *)Perl_refcounted_he_copy(aTHX_
-                                      (struct refcounted_he *)
-                                      he->refcounted_he_he.hent_next);
-    copy->refcounted_he_he.he_valu.hent_val
-       = newSVsv(he->refcounted_he_he.he_valu.hent_val);
-    hek = he->refcounted_he_he.hent_hek;
-    copy->refcounted_he_he.hent_hek
-       = share_hek(HEK_KEY(hek),
-                   HEK_UTF8(hek) ? -(I32)HEK_LEN(hek) : HEK_LEN(hek),
-                   HEK_HASH(hek));
-    copy->refcounted_he_refcnt = 1;
-    return copy;
+    return refcounted_he_new_common(chain, ":", 1, HVrhek_PV, HVrhek_PV,
+                                   label, strlen(label));
 }
-#endif
 
 /*
 =for apidoc hv_assert
@@ -2743,63 +2983,64 @@ Check that a hash is in an internally consistent state.
 void
 Perl_hv_assert(pTHX_ HV *hv)
 {
-  dVAR;
-  HE* entry;
-  int withflags = 0;
-  int placeholders = 0;
-  int real = 0;
-  int bad = 0;
-  const I32 riter = HvRITER_get(hv);
-  HE *eiter = HvEITER_get(hv);
-
-  (void)hv_iterinit(hv);
-
-  while ((entry = hv_iternext_flags(hv, HV_ITERNEXT_WANTPLACEHOLDERS))) {
-    /* sanity check the values */
-    if (HeVAL(entry) == &PL_sv_placeholder) {
-      placeholders++;
-    } else {
-      real++;
-    }
-    /* sanity check the keys */
-    if (HeSVKEY(entry)) {
-      /*EMPTY*/ /* Don't know what to check on SV keys.  */
-    } else if (HeKUTF8(entry)) {
-      withflags++;
-       if (HeKWASUTF8(entry)) {
-        PerlIO_printf(Perl_debug_log,
-                      "hash key has both WASUFT8 and UTF8: '%.*s'\n",
-                      (int) HeKLEN(entry),  HeKEY(entry));
-        bad = 1;
-       }
-    } else if (HeKWASUTF8(entry)) {
-      withflags++;
-    }
-  }
-  if (!SvTIED_mg((SV*)hv, PERL_MAGIC_tied)) {
-    if (HvUSEDKEYS(hv) != real) {
-      PerlIO_printf(Perl_debug_log, "Count %d key(s), but hash reports %d\n",
-                   (int) real, (int) HvUSEDKEYS(hv));
-      bad = 1;
-    }
-    if (HvPLACEHOLDERS_get(hv) != placeholders) {
-      PerlIO_printf(Perl_debug_log,
-                   "Count %d placeholder(s), but hash reports %d\n",
-                   (int) placeholders, (int) HvPLACEHOLDERS_get(hv));
-      bad = 1;
-    }
-  }
-  if (withflags && ! HvHASKFLAGS(hv)) {
-    PerlIO_printf(Perl_debug_log,
-                 "Hash has HASKFLAGS off but I count %d key(s) with flags\n",
-                 withflags);
-    bad = 1;
-  }
-  if (bad) {
-    sv_dump((SV *)hv);
-  }
-  HvRITER_set(hv, riter);              /* Restore hash iterator state */
-  HvEITER_set(hv, eiter);
+    dVAR;
+    HE* entry;
+    int withflags = 0;
+    int placeholders = 0;
+    int real = 0;
+    int bad = 0;
+    const I32 riter = HvRITER_get(hv);
+    HE *eiter = HvEITER_get(hv);
+
+    PERL_ARGS_ASSERT_HV_ASSERT;
+
+    (void)hv_iterinit(hv);
+
+    while ((entry = hv_iternext_flags(hv, HV_ITERNEXT_WANTPLACEHOLDERS))) {
+       /* sanity check the values */
+       if (HeVAL(entry) == &PL_sv_placeholder)
+           placeholders++;
+       else
+           real++;
+       /* sanity check the keys */
+       if (HeSVKEY(entry)) {
+           NOOP;   /* Don't know what to check on SV keys.  */
+       } else if (HeKUTF8(entry)) {
+           withflags++;
+           if (HeKWASUTF8(entry)) {
+               PerlIO_printf(Perl_debug_log,
+                           "hash key has both WASUTF8 and UTF8: '%.*s'\n",
+                           (int) HeKLEN(entry),  HeKEY(entry));
+               bad = 1;
+           }
+       } else if (HeKWASUTF8(entry))
+           withflags++;
+    }
+    if (!SvTIED_mg((const SV *)hv, PERL_MAGIC_tied)) {
+       static const char bad_count[] = "Count %d %s(s), but hash reports %d\n";
+       const int nhashkeys = HvUSEDKEYS(hv);
+       const int nhashplaceholders = HvPLACEHOLDERS_get(hv);
+
+       if (nhashkeys != real) {
+           PerlIO_printf(Perl_debug_log, bad_count, real, "keys", nhashkeys );
+           bad = 1;
+       }
+       if (nhashplaceholders != placeholders) {
+           PerlIO_printf(Perl_debug_log, bad_count, placeholders, "placeholder", nhashplaceholders );
+           bad = 1;
+       }
+    }
+    if (withflags && ! HvHASKFLAGS(hv)) {
+       PerlIO_printf(Perl_debug_log,
+                   "Hash has HASKFLAGS off but I count %d key(s) with flags\n",
+                   withflags);
+       bad = 1;
+    }
+    if (bad) {
+       sv_dump(MUTABLE_SV(hv));
+    }
+    HvRITER_set(hv, riter);            /* Restore hash iterator state */
+    HvEITER_set(hv, eiter);
 }
 
 #endif