This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Make CBuilder and ParseXS clean up their temp test files
[perl5.git] / hv.c
diff --git a/hv.c b/hv.c
index b2235fd..06e3a47 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, by Larry Wall and others
+ *    2000, 2001, 2002, 2003, 2004, 2005, 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.
 
 /* 
 =head1 Hash Manipulation Functions
+
+A HV structure represents a Perl hash. It consists mainly of an array
+of pointers, each of which points to a linked list of HE structures. The
+array is indexed by the hash function of the key, so each linked list
+represents all the hash entries with the same hash value. Each HE contains
+a pointer to the actual value, plus a pointer to a HEK structure which
+holds the key and hash value.
+
+=cut
+
 */
 
 #include "EXTERN.h"
 
 #define HV_MAX_LENGTH_BEFORE_SPLIT 14
 
-STATIC HE*
-S_new_he(pTHX)
-{
-    HE* he;
-    LOCK_SV_MUTEX;
-    if (!PL_he_root)
-       more_he();
-    he = PL_he_root;
-    PL_he_root = HeNEXT(he);
-    UNLOCK_SV_MUTEX;
-    return he;
-}
-
-STATIC void
-S_del_he(pTHX_ HE *p)
-{
-    LOCK_SV_MUTEX;
-    HeNEXT(p) = (HE*)PL_he_root;
-    PL_he_root = p;
-    UNLOCK_SV_MUTEX;
-}
+static const char S_strtab_error[]
+    = "Cannot modify shared string table in hv_%s";
 
 STATIC void
 S_more_he(pTHX)
 {
-    register HE* he;
-    register HE* heend;
-    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];
+    HE* he;
+    HE* heend;
+    Newx(he, PERL_ARENA_SIZE/sizeof(HE), HE);
+    HeNEXT(he) = PL_he_arenaroot;
+    PL_he_arenaroot = he;
+
+    heend = &he[PERL_ARENA_SIZE / sizeof(HE) - 1];
     PL_he_root = ++he;
     while (he < heend) {
        HeNEXT(he) = (HE*)(he + 1);
@@ -72,24 +61,49 @@ S_more_he(pTHX)
 
 #else
 
+STATIC HE*
+S_new_he(pTHX)
+{
+    HE* he;
+    LOCK_SV_MUTEX;
+    if (!PL_he_root)
+       S_more_he(aTHX);
+    he = PL_he_root;
+    PL_he_root = HeNEXT(he);
+    UNLOCK_SV_MUTEX;
+    return he;
+}
+
 #define new_HE() new_he()
-#define del_HE(p) del_he(p)
+#define del_HE(p) \
+    STMT_START { \
+       LOCK_SV_MUTEX; \
+       HeNEXT(p) = (HE*)PL_he_root; \
+       PL_he_root = p; \
+       UNLOCK_SV_MUTEX; \
+    } STMT_END
+
+
 
 #endif
 
 STATIC HEK *
 S_save_hek_flags(pTHX_ const char *str, I32 len, U32 hash, int flags)
 {
+    const int flags_masked = flags & HVhek_MASK;
     char *k;
     register HEK *hek;
 
-    New(54, k, HEK_BASESIZE + len + 2, char);
+    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;
+    HEK_FLAGS(hek) = (unsigned char)flags_masked;
+
+    if (flags & HVhek_FREEKEY)
+       Safefree(str);
     return hek;
 }
 
@@ -99,11 +113,10 @@ S_save_hek_flags(pTHX_ const char *str, I32 len, U32 hash, int flags)
 void
 Perl_free_tied_hv_pool(pTHX)
 {
-    HE *ohe;
     HE *he = PL_hv_fetch_ent_mh;
     while (he) {
+       HE * const ohe = he;
        Safefree(HeKEY_hek(he));
-       ohe = he;
        he = HeNEXT(he);
        del_HE(ohe);
     }
@@ -111,6 +124,26 @@ Perl_free_tied_hv_pool(pTHX)
 }
 
 #if defined(USE_ITHREADS)
+HEK *
+Perl_hek_dup(pTHX_ HEK *source, CLONE_PARAMS* param)
+{
+    HEK *shared = (HEK*)ptr_table_fetch(PL_ptr_table, source);
+
+    PERL_UNUSED_ARG(param);
+
+    if (shared) {
+       /* We already shared this hash key.  */
+       (void)share_hek_hek(shared);
+    }
+    else {
+       shared
+           = share_hek_flags(HEK_KEY(source), HEK_LEN(source),
+                             HEK_HASH(source), HEK_FLAGS(source));
+       ptr_table_store(PL_ptr_table, source, shared);
+    }
+    return shared;
+}
+
 HE *
 Perl_he_dup(pTHX_ HE *e, bool shared, CLONE_PARAMS* param)
 {
@@ -130,13 +163,28 @@ Perl_he_dup(pTHX_ HE *e, bool shared, CLONE_PARAMS* param)
     HeNEXT(ret) = he_dup(HeNEXT(e),shared, param);
     if (HeKLEN(e) == HEf_SVKEY) {
        char *k;
-       New(54, k, HEK_BASESIZE + sizeof(SV*), char);
+       Newx(k, HEK_BASESIZE + sizeof(SV*), char);
        HeKEY_hek(ret) = (HEK*)k;
        HeKEY_sv(ret) = SvREFCNT_inc(sv_dup(HeKEY_sv(e), param));
     }
-    else if (shared)
-       HeKEY_hek(ret) = share_hek_flags(HeKEY(e), HeKLEN(e), HeHASH(e),
-                                         HeKFLAGS(e));
+    else if (shared) {
+       /* This is hek_dup inlined, which seems to be important for speed
+          reasons.  */
+       HEK * const source = HeKEY_hek(e);
+       HEK *shared = (HEK*)ptr_table_fetch(PL_ptr_table, source);
+
+       if (shared) {
+           /* We already shared this hash key.  */
+           (void)share_hek_hek(shared);
+       }
+       else {
+           shared
+               = share_hek_flags(HEK_KEY(source), HEK_LEN(source),
+                                 HEK_HASH(source), HEK_FLAGS(source));
+           ptr_table_store(PL_ptr_table, source, shared);
+       }
+       HeKEY_hek(ret) = shared;
+    }
     else
        HeKEY_hek(ret) = save_hek_flags(HeKEY(e), HeKLEN(e), HeHASH(e),
                                         HeKFLAGS(e));
@@ -149,7 +197,7 @@ static void
 S_hv_notallowed(pTHX_ int flags, const char *key, I32 klen,
                const char *msg)
 {
-    SV *sv = sv_newmortal(), *esv = sv_newmortal();
+    SV * const sv = sv_newmortal();
     if (!(flags & HVhek_FREEKEY)) {
        sv_setpvn(sv, key, klen);
     }
@@ -161,13 +209,132 @@ S_hv_notallowed(pTHX_ int flags, const char *key, I32 klen,
     if (flags & HVhek_UTF8) {
        SvUTF8_on(sv);
     }
-    Perl_sv_setpvf(aTHX_ esv, "Attempt to %s a restricted hash", msg);
-    Perl_croak(aTHX_ SvPVX(esv), sv);
+    Perl_croak(aTHX_ msg, 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
+
+Stores an SV in a hash.  The hash key is specified as C<key> and C<klen> is
+the length of the key.  The C<hash> parameter is the precomputed hash
+value; if it is zero then Perl will compute it.  The return value will be
+NULL if the operation failed or if the value did not need to be actually
+stored within the hash (as in the case of tied hashes).  Otherwise it can
+be dereferenced to get the original C<SV*>.  Note that the caller is
+responsible for suitably incrementing the reference count of C<val> before
+the call, and decrementing it if the function returned NULL.  Effectively
+a successful hv_store takes ownership of one reference to C<val>.  This is
+usually what you want; a newly created SV has a reference count of one, so
+if all your code does is create SVs then store them in a hash, hv_store
+will own the only reference to the new SV, and your code doesn't need to do
+anything further to tidy up.  hv_store is not implemented as a call to
+hv_store_ent, and does not create a temporary SV for the key, so if your
+key data is not already in SV form then use 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
+*/
+
+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;
+}
+
+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>
+parameter is the precomputed hash value; if it is zero then Perl will
+compute it.  The return value is the new hash entry so created.  It will be
+NULL if the operation failed or if the value did not need to be actually
+stored within the hash (as in the case of tied hashes).  Otherwise the
+contents of the return value can be accessed using the C<He?> macros
+described here.  Note that the caller is responsible for suitably
+incrementing the reference count of C<val> before the call, and
+decrementing it if the function returned NULL.  Effectively a successful
+hv_store_ent takes ownership of one reference to C<val>.  This is
+usually what you want; a newly created SV has a reference count of one, so
+if all your code does is create SVs then store them in a hash, hv_store
+will own the only reference to the new SV, and your code doesn't need to do
+anything further to tidy up.  Note that hv_store_ent only reads the C<key>;
+unlike C<val> it does not take ownership of it, so maintaining the correct
+reference count on C<key> is entirely the caller's responsibility.  hv_store
+is not implemented as a call to hv_store_ent, and does not create a temporary
+SV for the key, so if your key data is not already in SV form then use
+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
+*/
+
+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
 
@@ -182,9 +349,6 @@ information on how to use this function on tied hashes.
 =cut
 */
 
-#define HV_FETCH_LVALUE  0x01
-#define HV_FETCH_JUST_SV 0x02
-
 SV**
 Perl_hv_fetch(pTHX_ HV *hv, const char *key, I32 klen_i32, I32 lval)
 {
@@ -200,10 +364,28 @@ Perl_hv_fetch(pTHX_ HV *hv, const char *key, I32 klen_i32, I32 lval)
        flags = 0;
     }
     hek = hv_fetch_common (hv, NULL, key, klen, flags,
-                          HV_FETCH_JUST_SV | (lval ? HV_FETCH_LVALUE : 0), 0);
+                          HV_FETCH_JUST_SV | (lval ? HV_FETCH_LVALUE : 0),
+                          Nullsv, 0);
     return hek ? &HeVAL(hek) : NULL;
 }
 
+/*
+=for apidoc hv_exists_ent
+
+Returns a boolean indicating whether the specified hash key exists. C<hash>
+can be a valid precomputed hash value, or 0 to ask for it to be
+computed.
+
+=cut
+*/
+
+bool
+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 */
 /*
@@ -226,105 +408,218 @@ information on how to use this function on tied hashes.
 HE *
 Perl_hv_fetch_ent(pTHX_ HV *hv, SV *keysv, I32 lval, register U32 hash)
 {
-    return hv_fetch_common(hv, keysv, NULL, 0, 0, lval ? HV_FETCH_LVALUE : 0,
-                          hash);
+    return hv_fetch_common(hv, keysv, NULL, 0, 0, 
+                          (lval ? HV_FETCH_LVALUE : 0), Nullsv, hash);
 }
 
-HE *
+STATIC HE *
 S_hv_fetch_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
-                 int flags, int action, register U32 hash)
+                 int flags, int action, SV *val, register U32 hash)
 {
-    register XPVHV* xhv;
-    register HE *entry;
+    dVAR;
+    XPVHV* xhv;
+    HE *entry;
+    HE **oentry;
     SV *sv;
     bool is_utf8;
-    const char *keysave;
     int masked_flags;
 
     if (!hv)
        return 0;
 
     if (keysv) {
-       key = SvPV(keysv, klen);
+       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);
     }
-    keysave = key;
 
-    if (SvRMAGICAL(hv)) {
-       if (mg_find((SV*)hv, PERL_MAGIC_tied) || SvGMAGICAL((SV*)hv)) {
-           sv = sv_newmortal();
+    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
-              HV_FETCH_JUST_SV is true.  */
+               /* XXX 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);
+               if (!keysv) {
+                   keysv = newSVpvn(key, klen);
+                   if (is_utf8) {
+                       SvUTF8_on(keysv);
+                   }
+               } else {
+                   keysv = newSVsv(keysv);
                }
-           } else {
-               keysv = newSVsv(keysv);
+               mg_copy((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;
+               if (entry)
+                   PL_hv_fetch_ent_mh = HeNEXT(entry);
+               else {
+                   char *k;
+                   entry = new_HE();
+                   Newx(k, HEK_BASESIZE + sizeof(SV*), char);
+                   HeKEY_hek(entry) = (HEK*)k;
+               }
+               HeNEXT(entry) = Nullhe;
+               HeSVKEY_set(entry, keysv);
+               HeVAL(entry) = sv;
+               sv_upgrade(sv, SVt_PVLV);
+               LvTYPE(sv) = 'T';
+                /* so we can free entry when freeing sv */
+               LvTARG(sv) = (SV*)entry;
+
+               /* XXX remove at some point? */
+               if (flags & HVhek_FREEKEY)
+                   Safefree(key);
+
+               return entry;
            }
-           mg_copy((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;
-           if (entry)
-               PL_hv_fetch_ent_mh = HeNEXT(entry);
-           else {
-               char *k;
-               entry = new_HE();
-               New(54, k, HEK_BASESIZE + sizeof(SV*), char);
-               HeKEY_hek(entry) = (HEK*)k;
+#ifdef ENV_IS_CASELESS
+           else if (mg_find((SV*)hv, PERL_MAGIC_env)) {
+               U32 i;
+               for (i = 0; i < klen; ++i)
+                   if (isLOWER(key[i])) {
+                       /* Would be nice if we had a routine to do the
+                          copy and upercase in a single pass through.  */
+                       const char *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, Nullsv, nkey, klen,
+                                               HVhek_FREEKEY, /* free nkey */
+                                               0 /* non-LVAL fetch */,
+                                               Nullsv /* no value */,
+                                               0 /* compute hash */);
+                       if (!entry && (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(61,0), hash);
+                       } else {
+                           if (flags & HVhek_FREEKEY)
+                               Safefree(key);
+                       }
+                       return entry;
+                   }
            }
-           HeNEXT(entry) = Nullhe;
-           HeSVKEY_set(entry, keysv);
-           HeVAL(entry) = sv;
-           sv_upgrade(sv, SVt_PVLV);
-           LvTYPE(sv) = 'T';
-           LvTARG(sv) = (SV*)entry; /* so we can free entry when freeing sv */
+#endif
+       } /* ISFETCH */
+       else if (SvRMAGICAL(hv) && (action & HV_FETCH_ISEXISTS)) {
+           if (mg_find((SV*)hv, PERL_MAGIC_tied) || SvGMAGICAL((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();
+               sv = sv_newmortal();
+
+               if (keysv || is_utf8) {
+                   if (!keysv) {
+                       keysv = newSVpvn(key, klen);
+                       SvUTF8_on(keysv);
+                   } else {
+                       keysv = newSVsv(keysv);
+                   }
+                   mg_copy((SV*)hv, sv, (char *)sv_2mortal(keysv), HEf_SVKEY);
+               } else {
+                   mg_copy((SV*)hv, sv, key, klen);
+               }
+               if (flags & HVhek_FREEKEY)
+                   Safefree(key);
+               magic_existspack(svret, mg_find(sv, PERL_MAGIC_tiedelem));
+               /* 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;
+               }
+#ifdef ENV_IS_CASELESS
+           else if (mg_find((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.  */
+               key = savepvn(key,klen);
+               key = (const char*)strupr((char*)key);
+               is_utf8 = 0;
+               hash = 0;
+               keysv = 0;
 
-           /* XXX remove at some point? */
-            if (flags & HVhek_FREEKEY)
-                Safefree(key);
+               if (flags & HVhek_FREEKEY) {
+                   Safefree(keysave);
+               }
+               flags |= HVhek_FREEKEY;
+           }
+#endif
+       } /* ISEXISTS */
+       else if (action & HV_FETCH_ISSTORE) {
+           bool needs_copy;
+           bool needs_store;
+           hv_magic_check (hv, &needs_copy, &needs_store);
+           if (needs_copy) {
+               const bool save_taint = PL_tainted;
+               if (keysv || is_utf8) {
+                   if (!keysv) {
+                       keysv = newSVpvn(key, klen);
+                       SvUTF8_on(keysv);
+                   }
+                   if (PL_tainting)
+                       PL_tainted = SvTAINTED(keysv);
+                   keysv = sv_2mortal(newSVsv(keysv));
+                   mg_copy((SV*)hv, val, (char*)keysv, HEf_SVKEY);
+               } else {
+                   mg_copy((SV*)hv, val, key, klen);
+               }
 
-           return entry;
-       }
-#ifdef ENV_IS_CASELESS
-       else if (mg_find((SV*)hv, PERL_MAGIC_env)) {
-           U32 i;
-           for (i = 0; i < klen; ++i)
-               if (isLOWER(key[i])) {
-                   SV *nkeysv = sv_2mortal(newSVpvn(key,klen));
-                   (void)strupr(SvPVX(nkeysv));
-                   entry = hv_fetch_common(hv, nkeysv, NULL, 0, 0, 0);
-                   if (!entry && (action & HV_FETCH_LVALUE))
-                       entry = hv_store_ent(hv, keysv, NEWSV(61,0), hash);
-
-                   /* XXX remove at some point? */
+               TAINT_IF(save_taint);
+               if (!HvARRAY(hv) && !needs_store) {
                    if (flags & HVhek_FREEKEY)
                        Safefree(key);
-
-                   return entry;
+                   return Nullhe;
+               }
+#ifdef ENV_IS_CASELESS
+               else if (mg_find((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.  */
+                   key = savepvn(key,klen);
+                   key = (const char*)strupr((char*)key);
+                   is_utf8 = 0;
+                   hash = 0;
+                   keysv = 0;
+
+                   if (flags & HVhek_FREEKEY) {
+                       Safefree(keysave);
+                   }
+                   flags |= HVhek_FREEKEY;
                }
-       }
 #endif
-    }
+           }
+       } /* ISSTORE */
+    } /* SvMAGICAL */
 
-    xhv = (XPVHV*)SvANY(hv);
-    if (!xhv->xhv_array /* !HvARRAY(hv) */) {
-       if ((action & HV_FETCH_LVALUE)
+    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))
 #endif
-                                                                 )
-           Newz(503, xhv->xhv_array /* HvARRAY(hv) */,
+                                                                 ) {
+           char *array;
+           Newxz(array,
                 PERL_HV_ARRAY_ALLOC_BYTES(xhv->xhv_max+1 /* HvMAX(hv)+1 */),
                 char);
+           HvARRAY(hv) = (HE**)array;
+       }
+#ifdef DYNAMIC_ENV_FETCH
+       else if (action & HV_FETCH_ISEXISTS) {
+           /* for an %ENV exists, if we do an insert it's by a recursive
+              store call, so avoid creating HvARRAY(hv) right now.  */
+       }
+#endif
        else {
            /* XXX remove at some point? */
             if (flags & HVhek_FREEKEY)
@@ -335,28 +630,30 @@ S_hv_fetch_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
     }
 
     if (is_utf8) {
-       int oldflags = flags;
+       char * const keysave = (char * const)key;
        key = (char*)bytes_from_utf8((U8*)key, &klen, &is_utf8);
         if (is_utf8)
            flags |= HVhek_UTF8;
        else
            flags &= ~HVhek_UTF8;
-        if (key != keysave)
+        if (key != keysave) {
+           if (flags & HVhek_FREEKEY)
+               Safefree(keysave);
             flags |= HVhek_WASUTF8 | HVhek_FREEKEY;
-       if (oldflags & HVhek_FREEKEY)
-           Safefree(keysave);
-
+       }
     }
 
     if (HvREHASH(hv)) {
        PERL_HASH_INTERNAL(hash, key, klen);
-       /* Yes, you do need this even though you are not "storing" because
+       /* We don't have a pointer to the hv, so we have to replicate the
+          flag into every HEK, so that hv_iterkeysv can see it.  */
+       /* And yes, you do need this even though you are not "storing" because
           you can flip the flags below if doing an lval lookup.  (And that
           was put in to give the semantics Andreas was expecting.)  */
        flags |= HVhek_REHASH;
     } else if (!hash) {
         if (keysv && (SvIsCOW_shared_hash(keysv))) {
-            hash = SvUVX(keysv);
+            hash = SvSHARED_HASH(keysv);
         } else {
             PERL_HASH(hash, key, klen);
         }
@@ -364,8 +661,13 @@ S_hv_fetch_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
 
     masked_flags = (flags & HVhek_MASK);
 
-    /* entry = (HvARRAY(hv))[hash & (I32) HvMAX(hv)]; */
-    entry = ((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max];
+#ifdef DYNAMIC_ENV_FETCH
+    if (!HvARRAY(hv)) entry = Null(HE*);
+    else
+#endif
+    {
+       entry = (HvARRAY(hv))[hash & (I32) HvMAX(hv)];
+    }
     for (; entry; entry = HeNEXT(entry)) {
        if (HeHASH(entry) != hash)              /* strings can't be equal */
            continue;
@@ -375,75 +677,183 @@ S_hv_fetch_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
            continue;
        if ((HeKFLAGS(entry) ^ masked_flags) & HVhek_UTF8)
            continue;
-        if ((action & HV_FETCH_LVALUE) && HeKFLAGS(entry) != masked_flags) {
-            /* We match if HVhek_UTF8 bit in our flags and hash key's match.
-               But if entry was set previously with HVhek_WASUTF8 and key now
-               doesn't (or vice versa) then we should change the key's flag,
-               as this is assignment.  */
-            if (HvSHAREKEYS(hv)) {
-                /* Need to swap the key we have for a key with the flags we
-                   need. As keys are shared we can't just write to the flag,
-                   so we share the new one, unshare the old one.  */
-                HEK *new_hek = share_hek_flags(key, klen, hash, masked_flags);
-                unshare_hek (HeKEY_hek(entry));
-                HeKEY_hek(entry) = new_hek;
-            }
-            else
-                HeKFLAGS(entry) = masked_flags;
-            if (masked_flags & HVhek_ENABLEHVKFLAGS)
-                HvHASKFLAGS_on(hv);
-        }
-       /* if we find a placeholder, we pretend we haven't found anything */
-       if (HeVAL(entry) == &PL_sv_placeholder)
-           break;
-       if (flags & HVhek_FREEKEY)
-           Safefree(key);
-       return entry;
-    }
-#ifdef DYNAMIC_ENV_FETCH  /* %ENV lookup?  If so, try to fetch the value now */
-    if (SvRMAGICAL((SV*)hv) && mg_find((SV*)hv, PERL_MAGIC_env)) {
+
+        if (action & (HV_FETCH_LVALUE|HV_FETCH_ISSTORE)) {
+           if (HeKFLAGS(entry) != masked_flags) {
+               /* We match if HVhek_UTF8 bit in our flags and hash key's
+                  match.  But if entry was set previously with HVhek_WASUTF8
+                  and key now doesn't (or vice versa) then we should change
+                  the key's flag, as this is assignment.  */
+               if (HvSHAREKEYS(hv)) {
+                   /* Need to swap the key we have for a key with the flags we
+                      need. As keys are shared we can't just write to the
+                      flag, so we share the new one, unshare the old one.  */
+                   HEK *new_hek = share_hek_flags(key, klen, hash,
+                                                  masked_flags);
+                   unshare_hek (HeKEY_hek(entry));
+                   HeKEY_hek(entry) = new_hek;
+               }
+               else if (hv == PL_strtab) {
+                   /* PL_strtab is usually the only hash without HvSHAREKEYS,
+                      so putting this test here is cheap  */
+                   if (flags & HVhek_FREEKEY)
+                       Safefree(key);
+                   Perl_croak(aTHX_ S_strtab_error,
+                              action & HV_FETCH_LVALUE ? "fetch" : "store");
+               }
+               else
+                   HeKFLAGS(entry) = masked_flags;
+               if (masked_flags & HVhek_ENABLEHVKFLAGS)
+                   HvHASKFLAGS_on(hv);
+           }
+           if (HeVAL(entry) == &PL_sv_placeholder) {
+               /* yes, can store into placeholder slot */
+               if (action & HV_FETCH_LVALUE) {
+                   if (SvMAGICAL(hv)) {
+                       /* This preserves behaviour with the old hv_fetch
+                          implementation which at this point would bail out
+                          with a break; (at "if we find a placeholder, we
+                          pretend we haven't found anything")
+
+                          That break mean that if a placeholder were found, it
+                          caused a call into hv_store, which in turn would
+                          check magic, and if there is no magic end up pretty
+                          much back at this point (in hv_store's code).  */
+                       break;
+                   }
+                   /* LVAL fetch which actaully needs a store.  */
+                   val = NEWSV(61,0);
+                   HvPLACEHOLDERS(hv)--;
+               } else {
+                   /* store */
+                   if (val != &PL_sv_placeholder)
+                       HvPLACEHOLDERS(hv)--;
+               }
+               HeVAL(entry) = val;
+           } else if (action & HV_FETCH_ISSTORE) {
+               SvREFCNT_dec(HeVAL(entry));
+               HeVAL(entry) = val;
+           }
+       } else if (HeVAL(entry) == &PL_sv_placeholder) {
+           /* if we find a placeholder, we pretend we haven't found
+              anything */
+           break;
+       }
+       if (flags & HVhek_FREEKEY)
+           Safefree(key);
+       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)) {
        unsigned long len;
-       char *env = PerlEnv_ENVgetenv_len(key,&len);
+       const char * const env = PerlEnv_ENVgetenv_len(key,&len);
        if (env) {
-           /* XXX remove once common API complete  */
-           if (!keysv) {
-               nkeysv = sv_2mortal(newSVpvn(key,klen));
-           }
-
            sv = newSVpvn(env,len);
            SvTAINTED_on(sv);
-           if (flags & HVhek_FREEKEY)
-               Safefree(key);
-           return hv_store_ent(hv,keysv,sv,hash);
+           return hv_fetch_common(hv,keysv,key,klen,flags,HV_FETCH_ISSTORE,sv,
+                                  hash);
        }
     }
 #endif
-    if (!entry && SvREADONLY(hv)) {
+
+    if (!entry && SvREADONLY(hv) && !(action & HV_FETCH_ISEXISTS)) {
        S_hv_notallowed(aTHX_ flags, key, klen,
-                       "access disallowed key '%"SVf"' in"
-                       );
+                       "Attempt to access disallowed key '%"SVf"' in"
+                       " a restricted hash");
+    }
+    if (!(action & (HV_FETCH_LVALUE|HV_FETCH_ISSTORE))) {
+       /* Not doing some form of store, so return failure.  */
+       if (flags & HVhek_FREEKEY)
+           Safefree(key);
+       return 0;
     }
     if (action & HV_FETCH_LVALUE) {
-       /* XXX remove once common API complete  */
-       if (!keysv) {
-           keysv = sv_2mortal(newSVpvn(key,klen));
+       val = NEWSV(61,0);
+       if (SvMAGICAL(hv)) {
+           /* At this point the old hv_fetch code would call to hv_store,
+              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);
+           /* XXX Surely that could leak if the fetch-was-store fails?
+              Just like the hv_fetch.  */
        }
     }
 
-    if (flags & HVhek_FREEKEY)
-       Safefree(key);
-    if (action & HV_FETCH_LVALUE) {
-       /* gonna assign to this, so it better be there */
-       sv = NEWSV(61,0);
-       return hv_store_ent(hv,keysv,sv,hash);
+    /* Welcome to hv_store...  */
+
+    if (!HvARRAY(hv)) {
+       /* Not sure if we can get here.  I think the only case of oentry being
+          NULL is for %ENV with dynamic env fetch.  But that should disappear
+          with magic in the previous code.  */
+       char *array;
+       Newxz(array,
+            PERL_HV_ARRAY_ALLOC_BYTES(xhv->xhv_max+1 /* HvMAX(hv)+1 */),
+            char);
+       HvARRAY(hv) = (HE**)array;
+    }
+
+    oentry = &(HvARRAY(hv))[hash & (I32) xhv->xhv_max];
+
+    entry = new_HE();
+    /* share_hek_flags will do the free for us.  This might be considered
+       bad API design.  */
+    if (HvSHAREKEYS(hv))
+       HeKEY_hek(entry) = share_hek_flags(key, klen, hash, flags);
+    else if (hv == PL_strtab) {
+       /* PL_strtab is usually the only hash without HvSHAREKEYS, so putting
+          this test here is cheap  */
+       if (flags & HVhek_FREEKEY)
+           Safefree(key);
+       Perl_croak(aTHX_ S_strtab_error,
+                  action & HV_FETCH_LVALUE ? "fetch" : "store");
+    }
+    else                                       /* gotta do the real thing */
+       HeKEY_hek(entry) = save_hek_flags(key, klen, hash, flags);
+    HeVAL(entry) = val;
+    HeNEXT(entry) = *oentry;
+    *oentry = entry;
+
+    if (val == &PL_sv_placeholder)
+       HvPLACEHOLDERS(hv)++;
+    if (masked_flags & HVhek_ENABLEHVKFLAGS)
+       HvHASKFLAGS_on(hv);
+
+    {
+       const HE *counter = HeNEXT(entry);
+
+       xhv->xhv_keys++; /* HvKEYS(hv)++ */
+       if (!counter) {                         /* initial entry? */
+           xhv->xhv_fill++; /* HvFILL(hv)++ */
+       } else if (xhv->xhv_keys > (IV)xhv->xhv_max) {
+           hsplit(hv);
+       } else if(!HvREHASH(hv)) {
+           U32 n_links = 1;
+
+           while ((counter = HeNEXT(counter)))
+               n_links++;
+
+           if (n_links > HV_MAX_LENGTH_BEFORE_SPLIT) {
+               /* Use only the old HvKEYS(hv) > HvMAX(hv) condition to limit
+                  bucket splits on a rehashed hash, as we're not going to
+                  split it again, and if someone is lucky (evil) enough to
+                  get all the keys in one list they could exhaust our memory
+                  as we repeatedly double the number of buckets on every
+                  entry. Linear search feels a less worse thing to do.  */
+               hsplit(hv);
+           }
+       }
     }
-    return 0;
+
+    return entry;
 }
 
 STATIC void
 S_hv_magic_check(pTHX_ HV *hv, bool *needs_copy, bool *needs_store)
 {
-    MAGIC *mg = SvMAGIC(hv);
+    const MAGIC *mg = SvMAGIC(hv);
     *needs_copy = FALSE;
     *needs_store = TRUE;
     while (mg) {
@@ -453,6 +863,7 @@ S_hv_magic_check(pTHX_ HV *hv, bool *needs_copy, bool *needs_store)
            case PERL_MAGIC_tied:
            case PERL_MAGIC_sig:
                *needs_store = FALSE;
+               return; /* We've set all there is to set. */
            }
        }
        mg = mg->mg_moremagic;
@@ -460,270 +871,32 @@ S_hv_magic_check(pTHX_ HV *hv, bool *needs_copy, bool *needs_store)
 }
 
 /*
-=for apidoc hv_store
-
-Stores an SV in a hash.  The hash key is specified as C<key> and C<klen> is
-the length of the key.  The C<hash> parameter is the precomputed hash
-value; if it is zero then Perl will compute it.  The return value will be
-NULL if the operation failed or if the value did not need to be actually
-stored within the hash (as in the case of tied hashes).  Otherwise it can
-be dereferenced to get the original C<SV*>.  Note that the caller is
-responsible for suitably incrementing the reference count of C<val> before
-the call, and decrementing it if the function returned NULL.  Effectively
-a successful hv_store takes ownership of one reference to C<val>.  This is
-usually what you want; a newly created SV has a reference count of one, so
-if all your code does is create SVs then store them in a hash, hv_store
-will own the only reference to the new SV, and your code doesn't need to do
-anything further to tidy up.  hv_store is not implemented as a call to
-hv_store_ent, and does not create a temporary SV for the key, so if your
-key data is not already in SV form then use 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
-*/
-
-SV**
-Perl_hv_store(pTHX_ HV *hv, const char *key, I32 klen, SV *val, U32 hash)
-{
-    HE *hek = hv_store_common (hv, NULL, key, klen, 0, val, hash);
-    return hek ? &HeVAL(hek) : NULL;
-}
-
-SV**
-Perl_hv_store_flags(pTHX_ HV *hv, const char *key, I32 klen, SV *val,
-                 register U32 hash, int flags)
-{
-    HE *hek = hv_store_common (hv, NULL, key, klen, flags, 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>
-parameter is the precomputed hash value; if it is zero then Perl will
-compute it.  The return value is the new hash entry so created.  It will be
-NULL if the operation failed or if the value did not need to be actually
-stored within the hash (as in the case of tied hashes).  Otherwise the
-contents of the return value can be accessed using the C<He?> macros
-described here.  Note that the caller is responsible for suitably
-incrementing the reference count of C<val> before the call, and
-decrementing it if the function returned NULL.  Effectively a successful
-hv_store_ent takes ownership of one reference to C<val>.  This is
-usually what you want; a newly created SV has a reference count of one, so
-if all your code does is create SVs then store them in a hash, hv_store
-will own the only reference to the new SV, and your code doesn't need to do
-anything further to tidy up.  Note that hv_store_ent only reads the C<key>;
-unlike C<val> it does not take ownership of it, so maintaining the correct
-reference count on C<key> is entirely the caller's responsibility.  hv_store
-is not implemented as a call to hv_store_ent, and does not create a temporary
-SV for the key, so if your key data is not already in SV form then use
-hv_store in preference to hv_store_ent.
+=for apidoc hv_scalar
 
-See L<perlguts/"Understanding the Magic of Tied Hashes and Arrays"> for more
-information on how to use this function on tied hashes.
+Evaluates the hash in scalar context and returns the result. Handles magic when the hash is tied.
 
 =cut
 */
 
-HE *
-Perl_hv_store_ent(pTHX_ HV *hv, SV *keysv, SV *val, U32 hash)
-{
-  return hv_store_common(hv, keysv, NULL, 0, 0, val, hash);
-}
-
-HE *
-S_hv_store_common(pTHX_ HV *hv, SV *keysv, const char *key, I32 klen_i32,
-                 int flags, SV *val, U32 hash)
+SV *
+Perl_hv_scalar(pTHX_ HV *hv)
 {
-    XPVHV* xhv;
-    STRLEN klen;
-    U32 n_links;
-    HE *entry;
-    HE **oentry;
-    bool is_utf8;
-    const char *keysave;
-
-    if (!hv)
-       return 0;
-
-    if (keysv) {
-       key = SvPV(keysv, klen);
-       is_utf8 = (SvUTF8(keysv) != 0);
-    } else {
-       if (klen_i32 < 0) {
-           klen = -klen_i32;
-           is_utf8 = TRUE;
-       } else {
-           klen = klen_i32;
-           /* XXX Need to fix this one level out.  */
-           is_utf8 = (flags & HVhek_UTF8) ? TRUE : FALSE;
-       }
-    }
-    keysave = key;
-
-    xhv = (XPVHV*)SvANY(hv);
-    if (SvMAGICAL(hv)) {
-       bool needs_copy;
-       bool needs_store;
-       hv_magic_check (hv, &needs_copy, &needs_store);
-       if (needs_copy) {
-           bool save_taint = PL_tainted;       
-           if (keysv || is_utf8) {
-               if (!keysv) {
-                   keysv = newSVpvn(key, klen);
-                   SvUTF8_on(keysv);
-               }
-               if (PL_tainting)
-                   PL_tainted = SvTAINTED(keysv);
-               keysv = sv_2mortal(newSVsv(keysv));
-               mg_copy((SV*)hv, val, (char*)keysv, HEf_SVKEY);
-           } else {
-               mg_copy((SV*)hv, val, key, klen);
-           }
-
-           TAINT_IF(save_taint);
-           if (!xhv->xhv_array /* !HvARRAY(hv) */ && !needs_store) {
-                if (flags & HVhek_FREEKEY)
-                    Safefree(key);
-               return Nullhe;
-           }
-#ifdef ENV_IS_CASELESS
-           else if (mg_find((SV*)hv, PERL_MAGIC_env)) {
-               key = savepvn(key,klen);
-               key = (const char*)strupr((char*)key);
-               hash = 0;
-
-                if (flags & HVhek_FREEKEY)
-                    Safefree(keysave);
-               keysave = key;
-           }
-#endif
-       }
-    }
-
-
-    if (flags & HVhek_PLACEHOLD) {
-        /* We have been requested to insert a placeholder. Currently
-           only Storable is allowed to do this.  */
-        val = &PL_sv_placeholder;
-    }
-
-    if (is_utf8) {
-       key = (char*)bytes_from_utf8((U8*)key, &klen, &is_utf8);
-
-       if (flags & HVhek_FREEKEY) {
-           /* This shouldn't happen if our caller does what we expect,
-              but strictly the API allows it.  */
-           Safefree(keysave);
-       }
-
-        if (is_utf8)
-            flags |= HVhek_UTF8;
-        if (key != keysave)
-            flags |= HVhek_WASUTF8 | HVhek_FREEKEY;
-        HvHASKFLAGS_on((SV*)hv);
-    }
-
-    if (HvREHASH(hv)) {
-       /* We don't have a pointer to the hv, so we have to replicate the
-          flag into every HEK, so that hv_iterkeysv can see it.  */
-       flags |= HVhek_REHASH;
-       PERL_HASH_INTERNAL(hash, key, klen);
-    } else if (!hash) {
-        if (keysv && SvIsCOW_shared_hash(keysv)) {
-            hash = SvUVX(keysv);
-        } else {
-            PERL_HASH(hash, key, klen);
-        }
-    }
-
-    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];
-    n_links = 0;
-    entry = *oentry;
-    for (; entry; ++n_links, entry = HeNEXT(entry)) {
-       if (HeHASH(entry) != hash)              /* strings can't be equal */
-           continue;
-       if (HeKLEN(entry) != (I32)klen)
-           continue;
-       if (HeKEY(entry) != key && memNE(HeKEY(entry),key,klen))        /* is this it? */
-           continue;
-       if ((HeKFLAGS(entry) ^ flags) & HVhek_UTF8)
-           continue;
-       if (HeVAL(entry) == &PL_sv_placeholder)
-           xhv->xhv_placeholders--; /* yes, can store into placeholder slot */
-       else
-           SvREFCNT_dec(HeVAL(entry));
-       HeVAL(entry) = val;
-       if (val == &PL_sv_placeholder)
-           xhv->xhv_placeholders++;
-
-        if (HeKFLAGS(entry) != flags) {
-            /* We match if HVhek_UTF8 bit in our flags and hash key's match.
-               But if entry was set previously with HVhek_WASUTF8 and key now
-               doesn't (or vice versa) then we should change the key's flag,
-               as this is assignment.  */
-            if (HvSHAREKEYS(hv)) {
-                /* Need to swap the key we have for a key with the flags we
-                   need. As keys are shared we can't just write to the flag,
-                   so we share the new one, unshare the old one.  */
-                int flags_nofree = flags & ~HVhek_FREEKEY;
-                HEK *new_hek = share_hek_flags(key, klen, hash, flags_nofree);
-                unshare_hek (HeKEY_hek(entry));
-                HeKEY_hek(entry) = new_hek;
-            }
-            else
-                HeKFLAGS(entry) = flags;
-        }
-        if (flags & HVhek_FREEKEY)
-           Safefree(key);
-       return entry;
-    }
-
-    if (SvREADONLY(hv)) {
-       S_hv_notallowed(aTHX_ flags, key, klen,
-                       "access disallowed key '%"SVf"' to"
-                       );
-    }
-
-    entry = new_HE();
-    /* share_hek_flags will do the free for us.  This might be considered
-       bad API design.  */
-    if (HvSHAREKEYS(hv))
-       HeKEY_hek(entry) = share_hek_flags(key, klen, hash, flags);
-    else                                       /* gotta do the real thing */
-       HeKEY_hek(entry) = save_hek_flags(key, klen, hash, flags);
-    HeVAL(entry) = val;
-    HeNEXT(entry) = *oentry;
-    *oentry = entry;
-
-    if (val == &PL_sv_placeholder)
-       xhv->xhv_placeholders++;
-
-    xhv->xhv_keys++; /* HvKEYS(hv)++ */
-    if (!n_links) {                            /* initial entry? */
-       xhv->xhv_fill++; /* HvFILL(hv)++ */
-    } else if ((xhv->xhv_keys > (IV)xhv->xhv_max)
-              || ((n_links > HV_MAX_LENGTH_BEFORE_SPLIT) && !HvREHASH(hv))) {
-       /* Use only the old HvKEYS(hv) > HvMAX(hv) condition to limit bucket
-          splits on a rehashed hash, as we're not going to split it again,
-          and if someone is lucky (evil) enough to get all the keys in one
-          list they could exhaust our memory as we repeatedly double the
-          number of buckets on every entry. Linear search feels a less worse
-          thing to do.  */
-        hsplit(hv);
-    }
-
-    return entry;
+    MAGIC *mg;
+    SV *sv;
+    
+    if ((SvRMAGICAL(hv) && (mg = mg_find((SV*)hv, PERL_MAGIC_tied)))) {
+        sv = magic_scalarpack(hv, mg);
+        return sv;
+    } 
+
+    sv = sv_newmortal();
+    if (HvFILL((HV*)hv)) 
+        Perl_sv_setpvf(aTHX_ sv, "%ld/%ld",
+                (long)HvFILL(hv), (long)HvMAX(hv) + 1);
+    else
+        sv_setiv(sv, 0);
+    
+    return sv;
 }
 
 /*
@@ -769,321 +942,108 @@ Perl_hv_delete_ent(pTHX_ HV *hv, SV *keysv, I32 flags, U32 hash)
     return hv_delete_common(hv, keysv, NULL, 0, 0, flags, hash);
 }
 
-SV *
+STATIC SV *
 S_hv_delete_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
                   int k_flags, I32 d_flags, U32 hash)
 {
+    dVAR;
     register XPVHV* xhv;
-    register I32 i;
     register HE *entry;
     register HE **oentry;
+    HE *const *first_entry;
     SV *sv;
     bool is_utf8;
-    const char *keysave;
     int masked_flags;
 
     if (!hv)
        return Nullsv;
 
     if (keysv) {
-       key = SvPV(keysv, klen);
+       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);
     }
-    keysave = key;
 
     if (SvRMAGICAL(hv)) {
        bool needs_copy;
        bool needs_store;
        hv_magic_check (hv, &needs_copy, &needs_store);
 
-       if (needs_copy) {
-           entry = hv_fetch_common(hv, keysv, key, klen,
-                                   k_flags & ~HVhek_FREEKEY, HV_FETCH_LVALUE,
-                                   hash);
-           sv = entry ? HeVAL(entry) : NULL;
-           if (sv) {
-               if (SvMAGICAL(sv)) {
-                   mg_clear(sv);
-               }
-               if (!needs_store) {
-                   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, PERL_MAGIC_env)) {
-               /* XXX This code isn't UTF8 clean.  */
-               keysv = sv_2mortal(newSVpvn(key,klen));
-               keysave = key = strupr(SvPVX(keysv));
-               is_utf8 = 0;
-               k_flags = 0;
-               hash = 0;
-           }
-#endif
-       }
-    }
-    xhv = (XPVHV*)SvANY(hv);
-    if (!xhv->xhv_array /* !HvARRAY(hv) */)
-       return Nullsv;
-
-    if (is_utf8) {
-       key = (char*)bytes_from_utf8((U8*)key, &klen, &is_utf8);
-
-       if (k_flags & HVhek_FREEKEY) {
-           /* This shouldn't happen if our caller does what we expect,
-              but strictly the API allows it.  */
-           Safefree(keysave);
-       }
-
-        if (is_utf8)
-            k_flags |= HVhek_UTF8;
-       else
-            k_flags &= ~HVhek_UTF8;
-        if (key != keysave)
-            k_flags |= HVhek_WASUTF8 | HVhek_FREEKEY;
-        HvHASKFLAGS_on((SV*)hv);
-    }
-
-    if (HvREHASH(hv)) {
-       PERL_HASH_INTERNAL(hash, key, klen);
-    } else if (!hash) {
-        if (keysv && (SvIsCOW_shared_hash(keysv))) {
-            hash = SvUVX(keysv);
-        } else {
-            PERL_HASH(hash, key, klen);
-        }
-       PERL_HASH(hash, key, klen);
-    }
-
-    masked_flags = (k_flags & HVhek_MASK);
-
-    /* oentry = &(HvARRAY(hv))[hash & (I32) HvMAX(hv)]; */
-    oentry = &((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max];
-    entry = *oentry;
-    i = 1;
-    for (; entry; i=0, oentry = &HeNEXT(entry), entry = *oentry) {
-       if (HeHASH(entry) != hash)              /* strings can't be equal */
-           continue;
-       if (HeKLEN(entry) != (I32)klen)
-           continue;
-       if (HeKEY(entry) != key && memNE(HeKEY(entry),key,klen))        /* is this it? */
-           continue;
-       if ((HeKFLAGS(entry) ^ masked_flags) & HVhek_UTF8)
-           continue;
-        if (k_flags & HVhek_FREEKEY)
-            Safefree(key);
-
-       /* if placeholder is here, it's already been deleted.... */
-       if (HeVAL(entry) == &PL_sv_placeholder)
-       {
-           if (SvREADONLY(hv))
-               return Nullsv; /* if still SvREADONLY, leave it deleted. */
-
-           /* okay, really delete the placeholder. */
-           *oentry = HeNEXT(entry);
-           if (i && !*oentry)
-               xhv->xhv_fill--; /* HvFILL(hv)-- */
-           if (entry == xhv->xhv_eiter /* HvEITER(hv) */)
-               HvLAZYDEL_on(hv);
-           else
-               hv_free_ent(hv, entry);
-           xhv->xhv_keys--; /* HvKEYS(hv)-- */
-          if (xhv->xhv_keys == 0)
-               HvHASKFLAGS_off(hv);
-           xhv->xhv_placeholders--;
-           return Nullsv;
-       }
-       else if (SvREADONLY(hv) && HeVAL(entry) && SvREADONLY(HeVAL(entry))) {
-           S_hv_notallowed(aTHX_ k_flags, key, klen,
-                           "delete readonly key '%"SVf"' from"
-                           );
-       }
-
-       if (d_flags & G_DISCARD)
-           sv = Nullsv;
-       else {
-           sv = sv_2mortal(HeVAL(entry));
-           HeVAL(entry) = &PL_sv_placeholder;
-       }
-
-       /*
-        * If a restricted hash, rather than really deleting the entry, put
-        * a placeholder there. This marks the key as being "approved", so
-        * we can still access via not-really-existing key without raising
-        * an error.
-        */
-       if (SvREADONLY(hv)) {
-           HeVAL(entry) = &PL_sv_placeholder;
-           /* We'll be saving this slot, so the number of allocated keys
-            * doesn't go down, but the number placeholders goes up */
-           xhv->xhv_placeholders++; /* HvPLACEHOLDERS(hv)++ */
-       } else {
-           *oentry = HeNEXT(entry);
-           if (i && !*oentry)
-               xhv->xhv_fill--; /* HvFILL(hv)-- */
-           if (entry == xhv->xhv_eiter /* HvEITER(hv) */)
-               HvLAZYDEL_on(hv);
-           else
-               hv_free_ent(hv, entry);
-           xhv->xhv_keys--; /* HvKEYS(hv)-- */
-           if (xhv->xhv_keys == 0)
-               HvHASKFLAGS_off(hv);
-       }
-       return sv;
-    }
-    if (SvREADONLY(hv)) {
-        S_hv_notallowed(aTHX_ k_flags, key, klen,
-                       "delete disallowed key '%"SVf"' from"
-                       );
-    }
-
-    if (k_flags & HVhek_FREEKEY)
-       Safefree(key);
-    return Nullsv;
-}
-
-/*
-=for apidoc hv_exists
-
-Returns a boolean indicating whether the specified hash key exists.  The
-C<klen> is the length of the key.
-
-=cut
-*/
-
-bool
-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_exists_common(hv, NULL, key, klen, flags, 0);
-}
-
-/*
-=for apidoc hv_exists_ent
-
-Returns a boolean indicating whether the specified hash key exists. C<hash>
-can be a valid precomputed hash value, or 0 to ask for it to be
-computed.
-
-=cut
-*/
-
-bool
-Perl_hv_exists_ent(pTHX_ HV *hv, SV *keysv, U32 hash)
-{
-    return hv_exists_common(hv, keysv, NULL, 0, 0, hash);
-}
-
-bool
-S_hv_exists_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
-                  int k_flags, U32 hash)
-{
-    register XPVHV* xhv;
-    register HE *entry;
-    SV *sv;
-    bool is_utf8;
-    const char *keysave;
-    int masked_flags;
-
-    if (!hv)
-       return 0;
-
-    if (keysv) {
-       key = SvPV(keysv, klen);
-       k_flags = 0;
-       is_utf8 = (SvUTF8(keysv) != 0);
-    } else {
-       is_utf8 = ((k_flags & HVhek_UTF8) ? TRUE : FALSE);
-    }
-    keysave = key;
-
-    if (SvRMAGICAL(hv)) {
-       if (mg_find((SV*)hv, PERL_MAGIC_tied) || SvGMAGICAL((SV*)hv)) {
-           SV* svret;
-
-           if (keysv || is_utf8) {
-               if (!keysv) {
-                   keysv = newSVpvn(key, klen);
-                   SvUTF8_on(keysv);
-               } else {
-                   keysv = newSVsv(keysv);
+       if (needs_copy) {
+           entry = hv_fetch_common(hv, keysv, key, klen,
+                                   k_flags & ~HVhek_FREEKEY, HV_FETCH_LVALUE,
+                                   Nullsv, hash);
+           sv = entry ? HeVAL(entry) : NULL;
+           if (sv) {
+               if (SvMAGICAL(sv)) {
+                   mg_clear(sv);
+               }
+               if (!needs_store) {
+                   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 */
                }
-               key = (char *)sv_2mortal(keysv);
-               klen = HEf_SVKEY;
-           }
-
-           /* I don't understand why hv_exists_ent has svret and sv,
-              whereas hv_exists only had one.  */
-           svret = sv_newmortal();
-           sv = sv_newmortal();
-           mg_copy((SV*)hv, sv, key, klen);
-           magic_existspack(svret, mg_find(sv, PERL_MAGIC_tiedelem));
-           return (bool)SvTRUE(svret);
-       }
 #ifdef ENV_IS_CASELESS
-       else if (mg_find((SV*)hv, PERL_MAGIC_env)) {
-           /* XXX This code isn't UTF8 clean.  */
-           keysv = sv_2mortal(newSVpvn(key,klen));
-           keysave = key = strupr(SvPVX(keysv));
-           is_utf8 = 0;
-           hash = 0;
-       }
+               else if (mg_find((SV*)hv, PERL_MAGIC_env)) {
+                   /* XXX This code isn't UTF8 clean.  */
+                   keysv = sv_2mortal(newSVpvn(key,klen));
+                   if (k_flags & HVhek_FREEKEY) {
+                       Safefree(key);
+                   }
+                   key = strupr(SvPVX(keysv));
+                   is_utf8 = 0;
+                   k_flags = 0;
+                   hash = 0;
+               }
 #endif
+           }
+       }
     }
-
     xhv = (XPVHV*)SvANY(hv);
-#ifndef DYNAMIC_ENV_FETCH
-    if (!xhv->xhv_array /* !HvARRAY(hv) */)
-       return 0;
-#endif
+    if (!HvARRAY(hv))
+       return Nullsv;
 
     if (is_utf8) {
+       const char *keysave = key;
        key = (char*)bytes_from_utf8((U8*)key, &klen, &is_utf8);
 
-       if (k_flags & HVhek_FREEKEY) {
-           /* This shouldn't happen if our caller does what we expect,
-              but strictly the API allows it.  */
-           Safefree(keysave);
-       }
-
         if (is_utf8)
             k_flags |= HVhek_UTF8;
        else
             k_flags &= ~HVhek_UTF8;
-        if (key != keysave)
-            k_flags |= HVhek_WASUTF8 | HVhek_FREEKEY;
+        if (key != keysave) {
+           if (k_flags & HVhek_FREEKEY) {
+               /* This shouldn't happen if our caller does what we expect,
+                  but strictly the API allows it.  */
+               Safefree(keysave);
+           }
+           k_flags |= HVhek_WASUTF8 | HVhek_FREEKEY;
+       }
+        HvHASKFLAGS_on((SV*)hv);
     }
 
     if (HvREHASH(hv)) {
        PERL_HASH_INTERNAL(hash, key, klen);
-    } else if (!hash)
-       PERL_HASH(hash, key, klen);
+    } else if (!hash) {
+        if (keysv && (SvIsCOW_shared_hash(keysv))) {
+            hash = SvSHARED_HASH(keysv);
+        } else {
+            PERL_HASH(hash, key, klen);
+        }
+    }
 
     masked_flags = (k_flags & HVhek_MASK);
 
-#ifdef DYNAMIC_ENV_FETCH
-    if (!xhv->xhv_array /* !HvARRAY(hv) */) entry = Null(HE*);
-    else
-#endif
-    /* entry = (HvARRAY(hv))[hash & (I32) HvMAX(hv)]; */
-    entry = ((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max];
-    for (; entry; entry = HeNEXT(entry)) {
+    first_entry = oentry = &(HvARRAY(hv))[hash & (I32) HvMAX(hv)];
+    entry = *oentry;
+    for (; entry; oentry = &HeNEXT(entry), entry = *oentry) {
        if (HeHASH(entry) != hash)              /* strings can't be equal */
            continue;
        if (HeKLEN(entry) != (I32)klen)
@@ -1092,79 +1052,139 @@ S_hv_exists_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
            continue;
        if ((HeKFLAGS(entry) ^ masked_flags) & HVhek_UTF8)
            continue;
-       if (k_flags & HVhek_FREEKEY)
-           Safefree(key);
-       /* If we find the key, but the value is a placeholder, return false. */
+
+       if (hv == PL_strtab) {
+           if (k_flags & HVhek_FREEKEY)
+               Safefree(key);
+           Perl_croak(aTHX_ S_strtab_error, "delete");
+       }
+
+       /* if placeholder is here, it's already been deleted.... */
        if (HeVAL(entry) == &PL_sv_placeholder)
-           return FALSE;
-       return TRUE;
-    }
-#ifdef DYNAMIC_ENV_FETCH  /* is it out there? */
-    if (SvRMAGICAL((SV*)hv) && mg_find((SV*)hv, PERL_MAGIC_env)) {
-       unsigned long len;
-       char *env = PerlEnv_ENVgetenv_len(key,&len);
-       if (env) {
-           sv = newSVpvn(env,len);
-           SvTAINTED_on(sv);
-           (void)hv_store_ent(hv,keysv,sv,hash);
-            if (k_flags & HVhek_FREEKEY)
-                Safefree(key);
-           return TRUE;
+       {
+         if (k_flags & HVhek_FREEKEY)
+            Safefree(key);
+         return Nullsv;
+       }
+       else if (SvREADONLY(hv) && HeVAL(entry) && SvREADONLY(HeVAL(entry))) {
+           S_hv_notallowed(aTHX_ k_flags, key, klen,
+                           "Attempt to delete readonly key '%"SVf"' from"
+                           " a restricted hash");
+       }
+        if (k_flags & HVhek_FREEKEY)
+            Safefree(key);
+
+       if (d_flags & G_DISCARD)
+           sv = Nullsv;
+       else {
+           sv = sv_2mortal(HeVAL(entry));
+           HeVAL(entry) = &PL_sv_placeholder;
+       }
+
+       /*
+        * If a restricted hash, rather than really deleting the entry, put
+        * a placeholder there. This marks the key as being "approved", so
+        * we can still access via not-really-existing key without raising
+        * an error.
+        */
+       if (SvREADONLY(hv)) {
+           SvREFCNT_dec(HeVAL(entry));
+           HeVAL(entry) = &PL_sv_placeholder;
+           /* We'll be saving this slot, so the number of allocated keys
+            * doesn't go down, but the number placeholders goes up */
+           HvPLACEHOLDERS(hv)++;
+       } else {
+           *oentry = HeNEXT(entry);
+           if(!*first_entry) {
+               xhv->xhv_fill--; /* HvFILL(hv)-- */
+           }
+           if (SvOOK(hv) && entry == HvAUX(hv)->xhv_eiter /* HvEITER(hv) */)
+               HvLAZYDEL_on(hv);
+           else
+               hv_free_ent(hv, entry);
+           xhv->xhv_keys--; /* HvKEYS(hv)-- */
+           if (xhv->xhv_keys == 0)
+               HvHASKFLAGS_off(hv);
        }
+       return sv;
     }
-#endif
+    if (SvREADONLY(hv)) {
+        S_hv_notallowed(aTHX_ k_flags, key, klen,
+                       "Attempt to delete disallowed key '%"SVf"' from"
+                       " a restricted hash");
+    }
+
     if (k_flags & HVhek_FREEKEY)
-        Safefree(key);
-    return FALSE;
+       Safefree(key);
+    return Nullsv;
 }
 
-
 STATIC void
 S_hsplit(pTHX_ HV *hv)
 {
     register XPVHV* xhv = (XPVHV*)SvANY(hv);
-    I32 oldsize = (I32) xhv->xhv_max+1; /* HvMAX(hv)+1 (sick) */
+    const 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; /* HvARRAY(hv) */
+    char *a = (char*) HvARRAY(hv);
     register HE **aep;
-    register HE **bep;
-    register HE *entry;
     register HE **oentry;
     int longest_chain = 0;
     int was_shared;
 
+    /*PerlIO_printf(PerlIO_stderr(), "hsplit called for %p which had %d\n",
+      hv, (int) oldsize);*/
+
+    if (HvPLACEHOLDERS_get(hv) && !SvREADONLY(hv)) {
+      /* Can make this clear any placeholders first for non-restricted hashes,
+        even though Storable rebuilds restricted hashes by putting in all the
+        placeholders (first) before turning on the readonly flag, because
+        Storable always pre-splits the hash.  */
+      hv_clear_placeholders(hv);
+    }
+              
     PL_nomemok = TRUE;
 #if defined(STRANGE_MALLOC) || defined(MYMALLOC)
-    Renew(a, PERL_HV_ARRAY_ALLOC_BYTES(newsize), char);
+    Renew(a, PERL_HV_ARRAY_ALLOC_BYTES(newsize)
+         + (SvOOK(hv) ? sizeof(struct xpvhv_aux) : 0), char);
     if (!a) {
       PL_nomemok = FALSE;
       return;
     }
+    if (SvOOK(hv)) {
+       Copy(&a[oldsize * sizeof(HE*)], &a[newsize * sizeof(HE*)], 1, struct xpvhv_aux);
+    }
 #else
-    New(2, a, PERL_HV_ARRAY_ALLOC_BYTES(newsize), char);
+    Newx(a, PERL_HV_ARRAY_ALLOC_BYTES(newsize)
+       + (SvOOK(hv) ? sizeof(struct xpvhv_aux) : 0), char);
     if (!a) {
       PL_nomemok = FALSE;
       return;
     }
-    Copy(xhv->xhv_array /* HvARRAY(hv) */, a, oldsize * sizeof(HE*), char);
+    Copy(HvARRAY(hv), a, oldsize * sizeof(HE*), char);
+    if (SvOOK(hv)) {
+       Copy(HvAUX(hv), &a[newsize * sizeof(HE*)], 1, struct xpvhv_aux);
+    }
     if (oldsize >= 64) {
-       offer_nice_chunk(xhv->xhv_array /* HvARRAY(hv) */,
-                       PERL_HV_ARRAY_ALLOC_BYTES(oldsize));
+       offer_nice_chunk(HvARRAY(hv),
+                        PERL_HV_ARRAY_ALLOC_BYTES(oldsize)
+                        + (SvOOK(hv) ? sizeof(struct xpvhv_aux) : 0));
     }
     else
-       Safefree(xhv->xhv_array /* HvARRAY(hv) */);
+       Safefree(HvARRAY(hv));
 #endif
 
     PL_nomemok = FALSE;
     Zero(&a[oldsize * sizeof(HE*)], (newsize-oldsize) * sizeof(HE*), char);    /* zero 2nd half*/
     xhv->xhv_max = --newsize;  /* HvMAX(hv) = --newsize */
-    xhv->xhv_array = a;                /* HvARRAY(hv) = a */
+    HvARRAY(hv) = (HE**) a;
     aep = (HE**)a;
 
     for (i=0; i<oldsize; i++,aep++) {
        int left_length = 0;
        int right_length = 0;
+       register HE *entry;
+       register HE **bep;
 
        if (!*aep)                              /* non-existent */
            continue;
@@ -1213,29 +1233,35 @@ S_hsplit(pTHX_ HV *hv)
       longest_chain, HvTOTALKEYS(hv), HvFILL(hv),  1+HvMAX(hv));*/
 
     ++newsize;
-    Newz(2, a, PERL_HV_ARRAY_ALLOC_BYTES(newsize), char);
+    Newxz(a, PERL_HV_ARRAY_ALLOC_BYTES(newsize)
+        + (SvOOK(hv) ? sizeof(struct xpvhv_aux) : 0), char);
+    if (SvOOK(hv)) {
+       Copy(HvAUX(hv), &a[newsize * sizeof(HE*)], 1, struct xpvhv_aux);
+    }
+
     was_shared = HvSHAREKEYS(hv);
 
     xhv->xhv_fill = 0;
     HvSHAREKEYS_off(hv);
     HvREHASH_on(hv);
 
-    aep = (HE **) xhv->xhv_array;
+    aep = HvARRAY(hv);
 
     for (i=0; i<newsize; i++,aep++) {
-       entry = *aep;
+       register HE *entry = *aep;
        while (entry) {
            /* We're going to trash this HE's next pointer when we chain it
               into the new hash below, so store where we go next.  */
-           HE *next = HeNEXT(entry);
+           HE * const next = HeNEXT(entry);
            UV hash;
+           HE **bep;
 
            /* Rehash it */
            PERL_HASH_INTERNAL(hash, HeKEY(entry), HeKLEN(entry));
 
            if (was_shared) {
                /* Unshare it.  */
-               HEK *new_hek
+               HEK * const new_hek
                    = save_hek_flags(HeKEY(entry), HeKLEN(entry),
                                     hash, HeKFLAGS(entry));
                unshare_hek (HeKEY_hek(entry));
@@ -1258,18 +1284,17 @@ S_hsplit(pTHX_ HV *hv)
            entry = next;
        }
     }
-    Safefree (xhv->xhv_array);
-    xhv->xhv_array = a;                /* HvARRAY(hv) = a */
+    Safefree (HvARRAY(hv));
+    HvARRAY(hv) = (HE **)a;
 }
 
 void
 Perl_hv_ksplit(pTHX_ HV *hv, IV newmax)
 {
     register XPVHV* xhv = (XPVHV*)SvANY(hv);
-    I32 oldsize = (I32) xhv->xhv_max+1; /* HvMAX(hv)+1 (sick) */
+    const I32 oldsize = (I32) xhv->xhv_max+1; /* HvMAX(hv)+1 (sick) */
     register I32 newsize;
     register I32 i;
-    register I32 j;
     register char *a;
     register HE **aep;
     register HE *entry;
@@ -1286,37 +1311,46 @@ Perl_hv_ksplit(pTHX_ HV *hv, IV newmax)
     if (newsize < newmax)
        return;                                 /* overflow detection */
 
-    a = xhv->xhv_array; /* HvARRAY(hv) */
+    a = (char *) HvARRAY(hv);
     if (a) {
        PL_nomemok = TRUE;
 #if defined(STRANGE_MALLOC) || defined(MYMALLOC)
-       Renew(a, PERL_HV_ARRAY_ALLOC_BYTES(newsize), char);
+       Renew(a, PERL_HV_ARRAY_ALLOC_BYTES(newsize)
+             + (SvOOK(hv) ? sizeof(struct xpvhv_aux) : 0), char);
        if (!a) {
          PL_nomemok = FALSE;
          return;
        }
+       if (SvOOK(hv)) {
+           Copy(&a[oldsize * sizeof(HE*)], &a[newsize * sizeof(HE*)], 1, struct xpvhv_aux);
+       }
 #else
-       New(2, a, PERL_HV_ARRAY_ALLOC_BYTES(newsize), char);
+       Newx(a, PERL_HV_ARRAY_ALLOC_BYTES(newsize)
+           + (SvOOK(hv) ? sizeof(struct xpvhv_aux) : 0), char);
        if (!a) {
          PL_nomemok = FALSE;
          return;
        }
-       Copy(xhv->xhv_array /* HvARRAY(hv) */, a, oldsize * sizeof(HE*), char);
+       Copy(HvARRAY(hv), a, oldsize * sizeof(HE*), char);
+       if (SvOOK(hv)) {
+           Copy(HvAUX(hv), &a[newsize * sizeof(HE*)], 1, struct xpvhv_aux);
+       }
        if (oldsize >= 64) {
-           offer_nice_chunk(xhv->xhv_array /* HvARRAY(hv) */,
-                           PERL_HV_ARRAY_ALLOC_BYTES(oldsize));
+           offer_nice_chunk(HvARRAY(hv),
+                            PERL_HV_ARRAY_ALLOC_BYTES(oldsize)
+                            + (SvOOK(hv) ? sizeof(struct xpvhv_aux) : 0));
        }
        else
-           Safefree(xhv->xhv_array /* HvARRAY(hv) */);
+           Safefree(HvARRAY(hv));
 #endif
        PL_nomemok = FALSE;
        Zero(&a[oldsize * sizeof(HE*)], (newsize-oldsize) * sizeof(HE*), char); /* zero 2nd half*/
     }
     else {
-       Newz(0, a, PERL_HV_ARRAY_ALLOC_BYTES(newsize), char);
+       Newxz(a, PERL_HV_ARRAY_ALLOC_BYTES(newsize), char);
     }
     xhv->xhv_max = --newsize;  /* HvMAX(hv) = --newsize */
-    xhv->xhv_array = a;        /* HvARRAY(hv) = a */
+    HvARRAY(hv) = (HE **) a;
     if (!xhv->xhv_fill /* !HvFILL(hv) */)      /* skip rest if no entries */
        return;
 
@@ -1325,6 +1359,7 @@ Perl_hv_ksplit(pTHX_ HV *hv, IV newmax)
        if (!*aep)                              /* non-existent */
            continue;
        for (oentry = aep, entry = *aep; entry; entry = *oentry) {
+           register I32 j;
            if ((j = (HeHASH(entry) & newsize)) != i) {
                j -= i;
                *oentry = HeNEXT(entry);
@@ -1352,10 +1387,9 @@ Creates a new HV.  The reference count is set to 1.
 HV *
 Perl_newHV(pTHX)
 {
-    register HV *hv;
     register XPVHV* xhv;
+    HV * const hv = (HV*)NEWSV(502,0);
 
-    hv = (HV*)NEWSV(502,0);
     sv_upgrade((SV *)hv, SVt_PVHV);
     xhv = (XPVHV*)SvANY(hv);
     SvPOK_off(hv);
@@ -1366,15 +1400,13 @@ Perl_newHV(pTHX)
 
     xhv->xhv_max    = 7;       /* HvMAX(hv) = 7 (start with 8 buckets) */
     xhv->xhv_fill   = 0;       /* HvFILL(hv) = 0 */
-    xhv->xhv_pmroot = 0;       /* HvPMROOT(hv) = 0 */
-    (void)hv_iterinit(hv);     /* so each() will start off right */
     return hv;
 }
 
 HV *
 Perl_newHVhv(pTHX_ HV *ohv)
 {
-    HV *hv = newHV();
+    HV * const hv = newHV();
     STRLEN hv_max, hv_fill;
 
     if (!ohv || (hv_fill = HvFILL(ohv)) == 0)
@@ -1384,15 +1416,16 @@ Perl_newHVhv(pTHX_ HV *ohv)
     if (!SvMAGICAL((SV *)ohv)) {
        /* It's an ordinary hash, so copy it fast. AMS 20010804 */
        STRLEN i;
-       bool shared = !!HvSHAREKEYS(ohv);
-       HE **ents, **oents = (HE **)HvARRAY(ohv);
+       const bool shared = !!HvSHAREKEYS(ohv);
+       HE **ents, ** const oents = (HE **)HvARRAY(ohv);
        char *a;
-       New(0, a, PERL_HV_ARRAY_ALLOC_BYTES(hv_max+1), char);
+       Newx(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];
+           HE *prev = NULL, *ent = NULL;
+           HE *oent = oents[i];
 
            if (!oent) {
                ents[i] = NULL;
@@ -1400,11 +1433,11 @@ Perl_newHVhv(pTHX_ HV *ohv)
            }
 
            /* 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(oent);
-                int flags  = HeKFLAGS(oent);
+           for (; oent; oent = HeNEXT(oent)) {
+               const U32 hash   = HeHASH(oent);
+               const char * const key = HeKEY(oent);
+               const STRLEN len = HeKLEN(oent);
+               const int flags  = HeKFLAGS(oent);
 
                ent = new_HE();
                HeVAL(ent)     = newSVsv(HeVAL(oent));
@@ -1424,12 +1457,12 @@ Perl_newHVhv(pTHX_ HV *ohv)
        HvFILL(hv)  = hv_fill;
        HvTOTALKEYS(hv)  = HvTOTALKEYS(ohv);
        HvARRAY(hv) = ents;
-    }
+    } /* not magical */
     else {
        /* Iterate over ohv, copying keys and values one at a time. */
        HE *entry;
-       I32 riter = HvRITER(ohv);
-       HE *eiter = HvEITER(ohv);
+       const I32 riter = HvRITER_get(ohv);
+       HE * const eiter = HvEITER_get(ohv);
 
        /* Can we use fewer buckets? (hv_max is always 2^n-1) */
        while (hv_max && hv_max + 1 >= hv_fill * 2)
@@ -1442,8 +1475,8 @@ Perl_newHVhv(pTHX_ HV *ohv)
                            newSVsv(HeVAL(entry)), HeHASH(entry),
                            HeKFLAGS(entry));
        }
-       HvRITER(ohv) = riter;
-       HvEITER(ohv) = eiter;
+       HvRITER_set(ohv, riter);
+       HvEITER_set(ohv, eiter);
     }
 
     return hv;
@@ -1457,7 +1490,7 @@ Perl_hv_free_ent(pTHX_ HV *hv, register HE *entry)
     if (!entry)
        return;
     val = HeVAL(entry);
-    if (val && isGV(val) && GvCVu(val) && HvNAME(hv))
+    if (val && isGV(val) && GvCVu(val) && HvNAME_get(hv))
        PL_sub_generation++;    /* may be deletion of method from stash */
     SvREFCNT_dec(val);
     if (HeKLEN(entry) == HEf_SVKEY) {
@@ -1476,18 +1509,12 @@ Perl_hv_delayfree_ent(pTHX_ HV *hv, register HE *entry)
 {
     if (!entry)
        return;
-    if (isGV(HeVAL(entry)) && GvCVu(HeVAL(entry)) && HvNAME(hv))
-       PL_sub_generation++;    /* may be deletion of method from stash */
-    sv_2mortal(HeVAL(entry));  /* free between statements */
+    /* SvREFCNT_inc to counter the SvREFCNT_dec in hv_free_ent  */
+    sv_2mortal(SvREFCNT_inc(HeVAL(entry)));    /* free between statements */
     if (HeKLEN(entry) == HEf_SVKEY) {
-       sv_2mortal(HeKEY_sv(entry));
-       Safefree(HeKEY_hek(entry));
+       sv_2mortal(SvREFCNT_inc(HeKEY_sv(entry)));
     }
-    else if (HvSHAREKEYS(hv))
-       unshare_hek(HeKEY_hek(entry));
-    else
-       Safefree(HeKEY_hek(entry));
-    del_HE(entry);
+    hv_free_ent(hv, entry);
 }
 
 /*
@@ -1501,6 +1528,7 @@ Clears a hash, making it empty.
 void
 Perl_hv_clear(pTHX_ HV *hv)
 {
+    dVAR;
     register XPVHV* xhv;
     if (!hv)
        return;
@@ -1509,12 +1537,11 @@ Perl_hv_clear(pTHX_ HV *hv)
 
     xhv = (XPVHV*)SvANY(hv);
 
-    if (SvREADONLY(hv) && xhv->xhv_array != NULL) {
+    if (SvREADONLY(hv) && HvARRAY(hv) != NULL) {
        /* restricted hash: convert all keys to placeholders */
-       I32 i;
-       HE* entry;
-       for (i = 0; i <= (I32) xhv->xhv_max; i++) {
-           entry = ((HE**)xhv->xhv_array)[i];
+       STRLEN i;
+       for (i = 0; i <= xhv->xhv_max; i++) {
+           HE *entry = (HvARRAY(hv))[i];
            for (; entry; entry = HeNEXT(entry)) {
                /* not already placeholder */
                if (HeVAL(entry) != &PL_sv_placeholder) {
@@ -1526,17 +1553,17 @@ Perl_hv_clear(pTHX_ HV *hv)
                    }
                    SvREFCNT_dec(HeVAL(entry));
                    HeVAL(entry) = &PL_sv_placeholder;
-                   xhv->xhv_placeholders++; /* HvPLACEHOLDERS(hv)++ */
+                   HvPLACEHOLDERS(hv)++;
                }
            }
        }
-       return;
+       goto reset;
     }
 
     hfreeentries(hv);
-    xhv->xhv_placeholders = 0; /* HvPLACEHOLDERS(hv) = 0 */
-    if (xhv->xhv_array /* HvARRAY(hv) */)
-       (void)memzero(xhv->xhv_array /* HvARRAY(hv) */,
+    HvPLACEHOLDERS_set(hv, 0);
+    if (HvARRAY(hv))
+       (void)memzero(HvARRAY(hv),
                      (xhv->xhv_max+1 /* HvMAX(hv)+1 */) * sizeof(HE*));
 
     if (SvRMAGICAL(hv))
@@ -1544,6 +1571,10 @@ Perl_hv_clear(pTHX_ HV *hv)
 
     HvHASKFLAGS_off(hv);
     HvREHASH_off(hv);
+    reset:
+    if (SvOOK(hv)) {
+       HvEITER_set(hv, NULL);
+    }
 }
 
 /*
@@ -1553,7 +1584,7 @@ Clears any placeholders from a hash.  If a restricted hash has any of its keys
 marked as readonly and the key is subsequently deleted, the key is not actually
 deleted but is marked by assigning it a value of &PL_sv_placeholder.  This tags
 it so it will be ignored by future operations such as iterating over the hash,
-but will still allow the hash to have a value reaasigned to the key at some
+but will still allow the hash to have a value reassigned to the key at some
 future point.  This function clears any such placeholder keys from the hash.
 See Hash::Util::lock_keys() for an example of its use.
 
@@ -1563,42 +1594,50 @@ See Hash::Util::lock_keys() for an example of its use.
 void
 Perl_hv_clear_placeholders(pTHX_ HV *hv)
 {
-    I32 items;
-    items = (I32)HvPLACEHOLDERS(hv);
-    if (items) {
-        HE *entry;
-        I32 riter = HvRITER(hv);
-        HE *eiter = HvEITER(hv);
-        hv_iterinit(hv);
-        /* This may look suboptimal with the items *after* the iternext, but
-           it's quite deliberate. We only get here with items==0 if we've
-           just deleted the last placeholder in the hash. If we've just done
-           that then it means that the hash is in lazy delete mode, and the
-           HE is now only referenced in our iterator. If we just quit the loop
-           and discarded our iterator then the HE leaks. So we do the && the
-           other way to ensure iternext is called just one more time, which
-           has the side effect of triggering the lazy delete.  */
-        while ((entry = hv_iternext_flags(hv, HV_ITERNEXT_WANTPLACEHOLDERS))
-            && items) {
-            SV *val = hv_iterval(hv, entry);
-
-            if (val == &PL_sv_placeholder) {
-
-                /* It seems that I have to go back in the front of the hash
-                   API to delete a hash, even though I have a HE structure
-                   pointing to the very entry I want to delete, and could hold
-                   onto the previous HE that points to it. And it's easier to
-                   go in with SVs as I can then specify the precomputed hash,
-                   and don't have fun and games with utf8 keys.  */
-                SV *key = hv_iterkeysv(entry);
-
-                hv_delete_ent (hv, key, G_DISCARD, HeHASH(entry));
-                items--;
-            }
-        }
-        HvRITER(hv) = riter;
-        HvEITER(hv) = eiter;
-    }
+    dVAR;
+    I32 items = (I32)HvPLACEHOLDERS_get(hv);
+    I32 i;
+
+    if (items == 0)
+       return;
+
+    i = HvMAX(hv);
+    do {
+       /* Loop down the linked list heads  */
+       bool first = 1;
+       HE **oentry = &(HvARRAY(hv))[i];
+       HE *entry = *oentry;
+
+       if (!entry)
+           continue;
+
+       for (; entry; entry = *oentry) {
+           if (HeVAL(entry) == &PL_sv_placeholder) {
+               *oentry = HeNEXT(entry);
+               if (first && !*oentry)
+                   HvFILL(hv)--; /* This linked list is now empty.  */
+               if (HvEITER_get(hv))
+                   HvLAZYDEL_on(hv);
+               else
+                   hv_free_ent(hv, entry);
+
+               if (--items == 0) {
+                   /* Finished.  */
+                   HvTOTALKEYS(hv) -= (IV)HvPLACEHOLDERS_get(hv);
+                   if (HvKEYS(hv) == 0)
+                       HvHASKFLAGS_off(hv);
+                   HvPLACEHOLDERS_set(hv, 0);
+                   return;
+               }
+           } else {
+               oentry = &HeNEXT(entry);
+               first = 0;
+           }
+       }
+    } while (--i >= 0);
+    /* You can't get here, hence assertion should always fail.  */
+    assert (items == 0);
+    assert (0);
 }
 
 STATIC void
@@ -1606,28 +1645,30 @@ S_hfreeentries(pTHX_ HV *hv)
 {
     register HE **array;
     register HE *entry;
-    register HE *oentry = Null(HE*);
     I32 riter;
     I32 max;
+    struct xpvhv_aux *iter;
 
-    if (!hv)
-       return;
     if (!HvARRAY(hv))
        return;
 
+    iter =  SvOOK(hv) ? HvAUX(hv) : 0;
+
     riter = 0;
     max = HvMAX(hv);
     array = HvARRAY(hv);
     /* make everyone else think the array is empty, so that the destructors
      * called for freed entries can't recusively mess with us */
     HvARRAY(hv) = Null(HE**); 
+    SvFLAGS(hv) &= ~SVf_OOK;
+
     HvFILL(hv) = 0;
     ((XPVHV*) SvANY(hv))->xhv_keys = 0;
 
     entry = array[0];
     for (;;) {
        if (entry) {
-           oentry = entry;
+           register HE * const oentry = entry;
            entry = HeNEXT(entry);
            hv_free_ent(hv, oentry);
        }
@@ -1637,8 +1678,35 @@ S_hfreeentries(pTHX_ HV *hv)
            entry = array[riter];
        }
     }
+
+    if (SvOOK(hv)) {
+       /* Someone attempted to iterate or set the hash name while we had
+          the array set to 0.  */
+       assert(HvARRAY(hv));
+
+       if (HvAUX(hv)->xhv_name)
+           unshare_hek_or_pvn(HvAUX(hv)->xhv_name, 0, 0, 0);
+       /* SvOOK_off calls sv_backoff, which isn't correct.  */
+
+       Safefree(HvARRAY(hv));
+       HvARRAY(hv) = 0;
+       SvFLAGS(hv) &= ~SVf_OOK;
+    }
+
+    /* FIXME - things will still go horribly wrong (or at least leak) if
+       people attempt to add elements to the hash while we're undef()ing it  */
+    if (iter) {
+       entry = iter->xhv_eiter; /* HvEITER(hv) */
+       if (entry && HvLAZYDEL(hv)) {   /* was deleted earlier? */
+           HvLAZYDEL_off(hv);
+           hv_free_ent(hv, entry);
+       }
+       iter->xhv_riter = -1;   /* HvRITER(hv) = -1 */
+       iter->xhv_eiter = Null(HE*); /* HvEITER(hv) = Null(HE*) */
+       SvFLAGS(hv) |= SVf_OOK;
+    }
+
     HvARRAY(hv) = array;
-    (void)hv_iterinit(hv);
 }
 
 /*
@@ -1653,26 +1721,52 @@ void
 Perl_hv_undef(pTHX_ HV *hv)
 {
     register XPVHV* xhv;
+    const char *name;
     if (!hv)
        return;
     DEBUG_A(Perl_hv_assert(aTHX_ hv));
     xhv = (XPVHV*)SvANY(hv);
     hfreeentries(hv);
-    Safefree(xhv->xhv_array /* HvARRAY(hv) */);
-    if (HvNAME(hv)) {
+    if ((name = HvNAME_get(hv))) {
         if(PL_stashcache)
-           hv_delete(PL_stashcache, HvNAME(hv), strlen(HvNAME(hv)), G_DISCARD);
-       Safefree(HvNAME(hv));
-       HvNAME(hv) = 0;
+           hv_delete(PL_stashcache, name, HvNAMELEN_get(hv), G_DISCARD);
+       Perl_hv_name_set(aTHX_ hv, Nullch, 0, 0);
     }
+    SvFLAGS(hv) &= ~SVf_OOK;
+    Safefree(HvARRAY(hv));
     xhv->xhv_max   = 7;        /* HvMAX(hv) = 7 (it's a normal hash) */
-    xhv->xhv_array = 0;        /* HvARRAY(hv) = 0 */
-    xhv->xhv_placeholders = 0; /* HvPLACEHOLDERS(hv) = 0 */
+    HvARRAY(hv) = 0;
+    HvPLACEHOLDERS_set(hv, 0);
 
     if (SvRMAGICAL(hv))
        mg_clear((SV*)hv);
 }
 
+static struct xpvhv_aux*
+S_hv_auxinit(pTHX_ HV *hv) {
+    struct xpvhv_aux *iter;
+    char *array;
+
+    if (!HvARRAY(hv)) {
+       Newxz(array, PERL_HV_ARRAY_ALLOC_BYTES(HvMAX(hv) + 1)
+           + sizeof(struct xpvhv_aux), char);
+    } else {
+       array = (char *) HvARRAY(hv);
+       Renew(array, PERL_HV_ARRAY_ALLOC_BYTES(HvMAX(hv) + 1)
+             + sizeof(struct xpvhv_aux), char);
+    }
+    HvARRAY(hv) = (HE**) array;
+    /* SvOOK_on(hv) attacks the IV flags.  */
+    SvFLAGS(hv) |= SVf_OOK;
+    iter = HvAUX(hv);
+
+    iter->xhv_riter = -1;      /* HvRITER(hv) = -1 */
+    iter->xhv_eiter = Null(HE*); /* HvEITER(hv) = Null(HE*) */
+    iter->xhv_name = 0;
+
+    return iter;
+}
+
 /*
 =for apidoc hv_iterinit
 
@@ -1691,22 +1785,111 @@ value, you can get it through the macro C<HvFILL(tb)>.
 I32
 Perl_hv_iterinit(pTHX_ HV *hv)
 {
-    register XPVHV* xhv;
     HE *entry;
 
     if (!hv)
        Perl_croak(aTHX_ "Bad hash");
-    xhv = (XPVHV*)SvANY(hv);
-    entry = xhv->xhv_eiter; /* HvEITER(hv) */
-    if (entry && HvLAZYDEL(hv)) {      /* was deleted earlier? */
-       HvLAZYDEL_off(hv);
-       hv_free_ent(hv, entry);
+
+    if (SvOOK(hv)) {
+       struct xpvhv_aux *iter = HvAUX(hv);
+       entry = iter->xhv_eiter; /* HvEITER(hv) */
+       if (entry && HvLAZYDEL(hv)) {   /* was deleted earlier? */
+           HvLAZYDEL_off(hv);
+           hv_free_ent(hv, entry);
+       }
+       iter->xhv_riter = -1;   /* HvRITER(hv) = -1 */
+       iter->xhv_eiter = Null(HE*); /* HvEITER(hv) = Null(HE*) */
+    } else {
+       S_hv_auxinit(aTHX_ hv);
     }
-    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 XHvTOTALKEYS(xhv);
+    return HvTOTALKEYS(hv);
+}
+
+I32 *
+Perl_hv_riter_p(pTHX_ HV *hv) {
+    struct xpvhv_aux *iter;
+
+    if (!hv)
+       Perl_croak(aTHX_ "Bad hash");
+
+    iter = SvOOK(hv) ? HvAUX(hv) : S_hv_auxinit(aTHX_ hv);
+    return &(iter->xhv_riter);
+}
+
+HE **
+Perl_hv_eiter_p(pTHX_ HV *hv) {
+    struct xpvhv_aux *iter;
+
+    if (!hv)
+       Perl_croak(aTHX_ "Bad hash");
+
+    iter = SvOOK(hv) ? HvAUX(hv) : S_hv_auxinit(aTHX_ hv);
+    return &(iter->xhv_eiter);
+}
+
+void
+Perl_hv_riter_set(pTHX_ HV *hv, I32 riter) {
+    struct xpvhv_aux *iter;
+
+    if (!hv)
+       Perl_croak(aTHX_ "Bad hash");
+
+    if (SvOOK(hv)) {
+       iter = HvAUX(hv);
+    } else {
+       if (riter == -1)
+           return;
+
+       iter = S_hv_auxinit(aTHX_ hv);
+    }
+    iter->xhv_riter = riter;
+}
+
+void
+Perl_hv_eiter_set(pTHX_ HV *hv, HE *eiter) {
+    struct xpvhv_aux *iter;
+
+    if (!hv)
+       Perl_croak(aTHX_ "Bad hash");
+
+    if (SvOOK(hv)) {
+       iter = HvAUX(hv);
+    } else {
+       /* 0 is the default so don't go malloc()ing a new structure just to
+          hold 0.  */
+       if (!eiter)
+           return;
+
+       iter = S_hv_auxinit(aTHX_ hv);
+    }
+    iter->xhv_eiter = eiter;
+}
+
+void
+Perl_hv_name_set(pTHX_ HV *hv, const char *name, I32 len, int flags)
+{
+    struct xpvhv_aux *iter;
+    U32 hash;
+
+    PERL_UNUSED_ARG(flags);
+
+    if (SvOOK(hv)) {
+       iter = HvAUX(hv);
+       if (iter->xhv_name) {
+           unshare_hek_or_pvn(iter->xhv_name, 0, 0, 0);
+       }
+    } else {
+       if (name == 0)
+           return;
+
+       iter = S_hv_auxinit(aTHX_ hv);
+    }
+    PERL_HASH(hash, name, len);
+    iter->xhv_name = name ? share_hek(name, len, hash) : 0;
 }
+
 /*
 =for apidoc hv_iternext
 
@@ -1747,15 +1930,26 @@ insufficiently abstracted for any change to be tidy.
 HE *
 Perl_hv_iternext_flags(pTHX_ HV *hv, I32 flags)
 {
+    dVAR;
     register XPVHV* xhv;
     register HE *entry;
     HE *oldentry;
     MAGIC* mg;
+    struct xpvhv_aux *iter;
 
     if (!hv)
        Perl_croak(aTHX_ "Bad hash");
     xhv = (XPVHV*)SvANY(hv);
-    oldentry = entry = xhv->xhv_eiter; /* HvEITER(hv) */
+
+    if (!SvOOK(hv)) {
+       /* Too many things (well, pp_each at least) merrily assume that you can
+          call iv_iternext without calling hv_iterinit, so we'll have to deal
+          with it.  */
+       hv_iterinit(hv);
+    }
+    iter = HvAUX(hv);
+
+    oldentry = entry = iter->xhv_eiter; /* HvEITER(hv) */
 
     if ((mg = SvTIED_mg((SV*)hv, PERL_MAGIC_tied))) {
        SV *key = sv_newmortal();
@@ -1768,9 +1962,9 @@ Perl_hv_iternext_flags(pTHX_ HV *hv, I32 flags)
            HEK *hek;
 
            /* one HE per MAGICAL hash */
-           xhv->xhv_eiter = entry = new_HE(); /* HvEITER(hv) = new_HE() */
+           iter->xhv_eiter = entry = new_HE(); /* HvEITER(hv) = new_HE() */
            Zero(entry, 1, HE);
-           Newz(54, k, HEK_BASESIZE + sizeof(SV*), char);
+           Newxz(k, HEK_BASESIZE + sizeof(SV*), char);
            hek = (HEK*)k;
            HeKEY_hek(entry) = hek;
            HeKLEN(entry) = HEf_SVKEY;
@@ -1785,7 +1979,7 @@ Perl_hv_iternext_flags(pTHX_ HV *hv, I32 flags)
            SvREFCNT_dec(HeVAL(entry));
        Safefree(HeKEY_hek(entry));
        del_HE(entry);
-       xhv->xhv_eiter = Null(HE*); /* HvEITER(hv) = Null(HE*) */
+       iter->xhv_eiter = Null(HE*); /* HvEITER(hv) = Null(HE*) */
        return Null(HE*);
     }
 #ifdef DYNAMIC_ENV_FETCH  /* set up %ENV for iteration */
@@ -1793,10 +1987,9 @@ Perl_hv_iternext_flags(pTHX_ HV *hv, I32 flags)
        prime_env_iter();
 #endif
 
-    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);
+    /* hv_iterint now ensures this.  */
+    assert (HvARRAY(hv));
+
     /* At start of hash, entry is NULL.  */
     if (entry)
     {
@@ -1814,14 +2007,13 @@ Perl_hv_iternext_flags(pTHX_ HV *hv, I32 flags)
     while (!entry) {
        /* OK. Come to the end of the current list.  Grab the next one.  */
 
-       xhv->xhv_riter++; /* HvRITER(hv)++ */
-       if (xhv->xhv_riter > (I32)xhv->xhv_max /* HvRITER(hv) > HvMAX(hv) */) {
+       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.  */
-           xhv->xhv_riter = -1; /* HvRITER(hv) = -1 */
+           iter->xhv_riter = -1; /* HvRITER(hv) = -1 */
            break;
        }
-       /* entry = (HvARRAY(hv))[HvRITER(hv)]; */
-       entry = ((HE**)xhv->xhv_array)[xhv->xhv_riter];
+       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.
@@ -1842,7 +2034,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);*/
 
-    xhv->xhv_eiter = entry; /* HvEITER(hv) = entry */
+    iter->xhv_eiter = entry; /* HvEITER(hv) = entry */
     return entry;
 }
 
@@ -1884,39 +2076,7 @@ see C<hv_iterinit>.
 SV *
 Perl_hv_iterkeysv(pTHX_ register HE *entry)
 {
-    if (HeKLEN(entry) != HEf_SVKEY) {
-        HEK *hek = HeKEY_hek(entry);
-        int flags = HEK_FLAGS(hek);
-        SV *sv;
-
-        if (flags & HVhek_WASUTF8) {
-            /* Trouble :-)
-               Andreas would like keys he put in as utf8 to come back as utf8
-            */
-            STRLEN utf8_len = HEK_LEN(hek);
-            U8 *as_utf8 = bytes_to_utf8 ((U8*)HEK_KEY(hek), &utf8_len);
-
-            sv = newSVpvn ((char*)as_utf8, utf8_len);
-            SvUTF8_on (sv);
-           Safefree (as_utf8); /* bytes_to_utf8() allocates a new string */
-       } else if (flags & HVhek_REHASH) {
-           /* We don't have a pointer to the hv, so we have to replicate the
-              flag into every HEK. This hv is using custom a hasing
-              algorithm. Hence we can't return a shared string scalar, as
-              that would contain the (wrong) hash value, and might get passed
-              into an hv routine with a regular hash  */
-
-            sv = newSVpvn (HEK_KEY(hek), HEK_LEN(hek));
-           if (HEK_UTF8(hek))
-               SvUTF8_on (sv);
-       } else {
-            sv = newSVpvn_share(HEK_KEY(hek),
-                                (HEK_UTF8(hek) ? -HEK_LEN(hek) : HEK_LEN(hek)),
-                                HEK_HASH(hek));
-        }
-        return sv_2mortal(sv);
-    }
-    return sv_mortalcopy(HeKEY_sv(entry));
+    return sv_2mortal(newSVhek(HeKEY_hek(entry)));
 }
 
 /*
@@ -1936,7 +2096,8 @@ Perl_hv_iterval(pTHX_ HV *hv, register HE *entry)
            SV* sv = sv_newmortal();
            if (HeKLEN(entry) == HEf_SVKEY)
                mg_copy((SV*)hv, sv, (char*)HeKEY_sv(entry), HEf_SVKEY);
-           else mg_copy((SV*)hv, sv, HeKEY(entry), HeKLEN(entry));
+           else
+               mg_copy((SV*)hv, sv, HeKEY(entry), HeKLEN(entry));
            return sv;
        }
     }
@@ -2007,18 +2168,36 @@ Perl_unshare_hek(pTHX_ HEK *hek)
    are used.  If so, len and hash must both be valid for str.
  */
 STATIC void
-S_unshare_hek_or_pvn(pTHX_ HEK *hek, const char *str, I32 len, U32 hash)
+S_unshare_hek_or_pvn(pTHX_ const HEK *hek, const char *str, I32 len, U32 hash)
 {
     register XPVHV* xhv;
     register HE *entry;
     register HE **oentry;
-    register I32 i = 1;
-    I32 found = 0;
+    HE **first;
+    bool found = 0;
     bool is_utf8 = FALSE;
     int k_flags = 0;
-    const char *save = str;
+    const char * const save = str;
+    struct shared_he *he = 0;
 
     if (hek) {
+       /* Find the shared he which is just before us in memory.  */
+       he = (struct shared_he *)(((char *)hek)
+                                 - STRUCT_OFFSET(struct shared_he,
+                                                 shared_he_hek));
+
+       /* Assert that the caller passed us a genuine (or at least consistent)
+          shared hek  */
+       assert (he->shared_he_he.hent_hek == hek);
+
+       LOCK_STRTAB_MUTEX;
+       if (he->shared_he_he.hent_val - 1) {
+           --he->shared_he_he.hent_val;
+           UNLOCK_STRTAB_MUTEX;
+           return;
+       }
+       UNLOCK_STRTAB_MUTEX;
+
         hash = HEK_HASH(hek);
     } else if (len < 0) {
         STRLEN tmplen = -len;
@@ -2040,18 +2219,18 @@ S_unshare_hek_or_pvn(pTHX_ HEK *hek, 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];
-    if (hek) {
-        for (entry = *oentry; entry; i=0, oentry = &HeNEXT(entry), entry = *oentry) {
-            if (HeKEY_hek(entry) != hek)
+    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;
         }
     } else {
-        int flags_masked = k_flags & HVhek_MASK;
-        for (entry = *oentry; entry; i=0, oentry = &HeNEXT(entry), entry = *oentry) {
+        const int flags_masked = k_flags & HVhek_MASK;
+        for (entry = *oentry; entry; oentry = &HeNEXT(entry), entry = *oentry) {
             if (HeHASH(entry) != hash)         /* strings can't be equal */
                 continue;
             if (HeKLEN(entry) != len)
@@ -2068,10 +2247,11 @@ S_unshare_hek_or_pvn(pTHX_ HEK *hek, const char *str, I32 len, U32 hash)
     if (found) {
         if (--HeVAL(entry) == Nullsv) {
             *oentry = HeNEXT(entry);
-            if (i && !*oentry)
+            if (!*first) {
+               /* There are now no entries in our slot.  */
                 xhv->xhv_fill--; /* HvFILL(hv)-- */
-            Safefree(HeKEY_hek(entry));
-            del_HE(entry);
+           }
+            Safefree(entry);
             xhv->xhv_keys--; /* HvKEYS(hv)-- */
         }
     }
@@ -2079,9 +2259,10 @@ S_unshare_hek_or_pvn(pTHX_ HEK *hek, const char *str, I32 len, U32 hash)
     UNLOCK_STRTAB_MUTEX;
     if (!found && ckWARN_d(WARN_INTERNAL))
        Perl_warner(aTHX_ packWARN(WARN_INTERNAL),
-                    "Attempt to free non-existent shared string '%s'%s",
+                    "Attempt to free non-existent shared string '%s'%s"
+                    pTHX__FORMAT,
                     hek ? HEK_KEY(hek) : str,
-                    (k_flags & HVhek_UTF8) ? " (utf8)" : "");
+                    ((k_flags & HVhek_UTF8) ? " (utf8)" : "") pTHX__VALUE);
     if (k_flags & HVhek_FREEKEY)
        Safefree(str);
 }
@@ -2095,7 +2276,7 @@ Perl_share_hek(pTHX_ const char *str, I32 len, register U32 hash)
 {
     bool is_utf8 = FALSE;
     int flags = 0;
-    const char *save = str;
+    const char * const save = str;
 
     if (len < 0) {
       STRLEN tmplen = -len;
@@ -2120,12 +2301,10 @@ Perl_share_hek(pTHX_ const char *str, I32 len, register U32 hash)
 STATIC HEK *
 S_share_hek_flags(pTHX_ const char *str, I32 len, register U32 hash, int flags)
 {
-    register XPVHV* xhv;
     register HE *entry;
     register HE **oentry;
-    register I32 i = 1;
     I32 found = 0;
-    int flags_masked = flags & HVhek_MASK;
+    const int flags_masked = flags & HVhek_MASK;
 
     /* what follows is the moral equivalent of:
 
@@ -2135,12 +2314,11 @@ 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
     */
-    xhv = (XPVHV*)SvANY(PL_strtab);
+    register XPVHV * const 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)) {
+    oentry = &(HvARRAY(PL_strtab))[hash & (I32) HvMAX(PL_strtab)];
+    for (entry = *oentry; entry; entry = HeNEXT(entry)) {
        if (HeHASH(entry) != hash)              /* strings can't be equal */
            continue;
        if (HeKLEN(entry) != len)
@@ -2153,13 +2331,41 @@ S_share_hek_flags(pTHX_ const char *str, I32 len, register U32 hash, int flags)
        break;
     }
     if (!found) {
-       entry = new_HE();
-       HeKEY_hek(entry) = save_hek_flags(str, len, hash, flags);
+       /* What used to be head of the list.
+          If this is NULL, then we're the first entry for this slot, which
+          means we need to increate fill.  */
+       const HE *old_first = *oentry;
+       struct shared_he *new_entry;
+       HEK *hek;
+       char *k;
+
+       /* We don't actually store a HE from the arena and a regular HEK.
+          Instead we allocate one chunk of memory big enough for both,
+          and put the HEK straight after the HE. This way we can find the
+          HEK directly from the HE.
+       */
+
+       Newx(k, STRUCT_OFFSET(struct shared_he,
+                               shared_he_hek.hek_key[0]) + len + 2, char);
+       new_entry = (struct shared_he *)k;
+       entry = &(new_entry->shared_he_he);
+       hek = &(new_entry->shared_he_hek);
+
+       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;
+
+       /* Still "point" to the HEK, so that other code need not know what
+          we're up to.  */
+       HeKEY_hek(entry) = hek;
        HeVAL(entry) = Nullsv;
        HeNEXT(entry) = *oentry;
        *oentry = entry;
+
        xhv->xhv_keys++; /* HvKEYS(hv)++ */
-       if (i) {                                /* initial entry? */
+       if (!old_first) {                       /* initial entry? */
            xhv->xhv_fill++; /* HvFILL(hv)++ */
        } else if (xhv->xhv_keys > (IV)xhv->xhv_max /* HvKEYS(hv) > HvMAX(hv) */) {
                hsplit(PL_strtab);
@@ -2175,6 +2381,46 @@ S_share_hek_flags(pTHX_ const char *str, I32 len, register U32 hash, int flags)
     return HeKEY_hek(entry);
 }
 
+I32 *
+Perl_hv_placeholders_p(pTHX_ HV *hv)
+{
+    dVAR;
+    MAGIC *mg = mg_find((SV*)hv, PERL_MAGIC_rhash);
+
+    if (!mg) {
+       mg = sv_magicext((SV*)hv, 0, PERL_MAGIC_rhash, 0, 0, 0);
+
+       if (!mg) {
+           Perl_die(aTHX_ "panic: hv_placeholders_p");
+       }
+    }
+    return &(mg->mg_len);
+}
+
+
+I32
+Perl_hv_placeholders_get(pTHX_ HV *hv)
+{
+    dVAR;
+    MAGIC * const mg = mg_find((SV*)hv, PERL_MAGIC_rhash);
+
+    return mg ? mg->mg_len : 0;
+}
+
+void
+Perl_hv_placeholders_set(pTHX_ HV *hv, I32 ph)
+{
+    dVAR;
+    MAGIC * const mg = mg_find((SV*)hv, PERL_MAGIC_rhash);
+
+    if (mg) {
+       mg->mg_len = ph;
+    } else if (ph) {
+       if (!sv_magicext((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.  */
+}
 
 /*
 =for apidoc hv_assert
@@ -2187,13 +2433,14 @@ 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;
-  I32 riter = HvRITER(hv);
-  HE *eiter = HvEITER(hv);
+  const I32 riter = HvRITER_get(hv);
+  HE *eiter = HvEITER_get(hv);
 
   (void)hv_iterinit(hv);
 
@@ -2225,10 +2472,10 @@ Perl_hv_assert(pTHX_ HV *hv)
                    (int) real, (int) HvUSEDKEYS(hv));
       bad = 1;
     }
-    if (HvPLACEHOLDERS(hv) != placeholders) {
+    if (HvPLACEHOLDERS_get(hv) != placeholders) {
       PerlIO_printf(Perl_debug_log,
                    "Count %d placeholder(s), but hash reports %d\n",
-                   (int) placeholders, (int) HvPLACEHOLDERS(hv));
+                   (int) placeholders, (int) HvPLACEHOLDERS_get(hv));
       bad = 1;
     }
   }
@@ -2241,6 +2488,16 @@ Perl_hv_assert(pTHX_ HV *hv)
   if (bad) {
     sv_dump((SV *)hv);
   }
-  HvRITER(hv) = riter;         /* Restore hash iterator state */
-  HvEITER(hv) = eiter;
+  HvRITER_set(hv, riter);              /* Restore hash iterator state */
+  HvEITER_set(hv, eiter);
 }
+
+/*
+ * Local variables:
+ * c-indentation-style: bsd
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ *
+ * ex: set ts=8 sts=4 sw=4 noet:
+ */