This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
speed up AV and HV clearing/undeffing
[perl5.git] / hv.c
diff --git a/hv.c b/hv.c
index c34d4d3..de06148 100644 (file)
--- a/hv.c
+++ b/hv.c
@@ -18,7 +18,6 @@
 
 /* 
 =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
@@ -35,7 +34,8 @@ holds the key and hash value.
 #define PERL_HASH_INTERNAL_ACCESS
 #include "perl.h"
 
-#define HV_MAX_LENGTH_BEFORE_SPLIT 14
+#define DO_HSPLIT(xhv) ((xhv)->xhv_keys > (xhv)->xhv_max) /* HvTOTALKEYS(hv) > HvMAX(hv) */
+#define HV_FILL_THRESHOLD 31
 
 static const char S_strtab_error[]
     = "Cannot modify shared string table in hv_%s";
@@ -50,7 +50,6 @@ static const char S_strtab_error[]
 STATIC HE*
 S_new_he(pTHX)
 {
-    dVAR;
     HE* he;
     void ** const root = &PL_body_roots[HE_SVSLOT];
 
@@ -81,8 +80,7 @@ S_save_hek_flags(const char *str, I32 len, U32 hash, int flags)
     HEK *hek;
 
     PERL_ARGS_ASSERT_SAVE_HEK_FLAGS;
-
-    Newx(k, HEK_BASESIZE + len + 2, char);
+    Newx(k, HEK_BASESIZE + len + 1, char);
     hek = (HEK*)k;
     Copy(str, HEK_KEY(hek), len, char);
     HEK_KEY(hek)[len] = 0;
@@ -101,7 +99,6 @@ S_save_hek_flags(const char *str, I32 len, U32 hash, int flags)
 void
 Perl_free_tied_hv_pool(pTHX)
 {
-    dVAR;
     HE *he = PL_hv_fetch_ent_mh;
     while (he) {
        HE * const ohe = he;
@@ -224,19 +221,19 @@ 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
+C<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
+the call, and decrementing it if the function returned C<NULL>.  Effectively
+a successful C<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
+if all your code does is create SVs then store them in a hash, C<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.
+anything further to tidy up.  C<hv_store> is not implemented as a call to
+C<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 C<hv_store> in preference to
+C<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.
@@ -246,22 +243,22 @@ information on how to use this function on tied hashes.
 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
+C<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
+C<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
+if all your code does is create SVs then store them in a hash, C<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>;
+anything further to tidy up.  Note that C<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
+reference count on C<key> is entirely the caller's responsibility.  C<hv_store>
+is not implemented as a call to C<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.
+C<hv_store> in preference to C<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.
@@ -337,7 +334,7 @@ Perl_hv_common_key_len(pTHX_ HV *hv, const char *key, I32 klen_i32,
 
 void *
 Perl_hv_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
-              int flags, int action, SV *val, register U32 hash)
+              int flags, int action, SV *val, U32 hash)
 {
     dVAR;
     XPVHV* xhv;
@@ -347,6 +344,7 @@ Perl_hv_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
     bool is_utf8;
     int masked_flags;
     const int return_svp = action & HV_FETCH_JUST_SV;
+    HEK *keysv_hek = NULL;
 
     if (!hv)
        return NULL;
@@ -497,7 +495,11 @@ Perl_hv_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
                }
                if (flags & HVhek_FREEKEY)
                    Safefree(key);
-               magic_existspack(svret, mg_find(sv, PERL_MAGIC_tiedelem));
+               {
+                  MAGIC * const mg = mg_find(sv, PERL_MAGIC_tiedelem);
+                  if (mg)
+                    magic_existspack(svret, mg);
+               }
                /* This cast somewhat evil, but I'm merely using NULL/
                   not NULL to return the boolean exists.
                   And I know hv is not NULL.  */
@@ -526,7 +528,7 @@ Perl_hv_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
            bool needs_store;
            hv_magic_check (hv, &needs_copy, &needs_store);
            if (needs_copy) {
-               const bool save_taint = TAINT_get; /* Unused var warning under NO_TAINT_SUPPORT */
+               const bool save_taint = TAINT_get;
                if (keysv || is_utf8) {
                    if (!keysv) {
                        keysv = newSVpvn_utf8(key, klen, TRUE);
@@ -540,6 +542,9 @@ Perl_hv_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
                }
 
                TAINT_IF(save_taint);
+#ifdef NO_TAINT_SUPPORT
+                PERL_UNUSED_VAR(save_taint);
+#endif
                if (!needs_store) {
                    if (flags & HVhek_FREEKEY)
                        Safefree(key);
@@ -613,12 +618,13 @@ Perl_hv_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
        }
     }
 
-    if (!hash) {
-        if (keysv && (SvIsCOW_shared_hash(keysv)))
-            hash = SvSHARED_HASH(keysv);
-        else
-            PERL_HASH(hash, key, klen);
+    if (keysv && (SvIsCOW_shared_hash(keysv))) {
+        if (HvSHAREKEYS(hv))
+            keysv_hek  = SvSHARED_HEK_FROM_PV(SvPVX_const(keysv));
+        hash = SvSHARED_HASH(keysv);
     }
+    else if (!hash)
+        PERL_HASH(hash, key, klen);
 
     masked_flags = (flags & HVhek_MASK);
 
@@ -629,16 +635,48 @@ Perl_hv_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
     {
        entry = (HvARRAY(hv))[hash & (I32) HvMAX(hv)];
     }
+
+    if (!entry)
+        goto not_found;
+
+    if (keysv_hek) {
+        /* keysv is actually a HEK in disguise, so we can match just by
+         * comparing the HEK pointers in the HE chain. There is a slight
+         * caveat: on something like "\x80", which has both plain and utf8
+         * representations, perl's hashes do encoding-insensitive lookups,
+         * but preserve the encoding of the stored key. Thus a particular
+         * key could map to two different HEKs in PL_strtab. We only
+         * conclude 'not found' if all the flags are the same; otherwise
+         * we fall back to a full search (this should only happen in rare
+         * cases).
+         */
+        int keysv_flags = HEK_FLAGS(keysv_hek);
+        HE  *orig_entry = entry;
+
+        for (; entry; entry = HeNEXT(entry)) {
+            HEK *hek = HeKEY_hek(entry);
+            if (hek == keysv_hek)
+                goto found;
+            if (HEK_FLAGS(hek) != keysv_flags)
+                break; /* need to do full match */
+        }
+        if (!entry)
+            goto not_found;
+        /* failed on shortcut - do full search loop */
+        entry = orig_entry;
+    }
+
     for (; entry; 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? */
+       if (memNE(HeKEY(entry),key,klen))       /* is this it? */
            continue;
        if ((HeKFLAGS(entry) ^ masked_flags) & HVhek_UTF8)
            continue;
 
+      found:
         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
@@ -703,10 +741,12 @@ Perl_hv_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
        if (flags & HVhek_FREEKEY)
            Safefree(key);
        if (return_svp) {
-           return entry ? (void *) &HeVAL(entry) : NULL;
+            return (void *) &HeVAL(entry);
        }
        return entry;
     }
+
+  not_found:
 #ifdef DYNAMIC_ENV_FETCH  /* %ENV lookup?  If so, try to fetch the value now */
     if (!(action & HV_FETCH_ISSTORE) 
        && SvRMAGICAL((const SV *)hv)
@@ -746,7 +786,8 @@ Perl_hv_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
               recursive call would call the key conversion routine again.
               However, as we replace the original key with the converted
               key, this would result in a double conversion, which would show
-              up as a bug if the conversion routine is not idempotent.  */
+              up as a bug if the conversion routine is not idempotent.
+              Hence the use of HV_DISABLE_UVAR_XKEY.  */
            return hv_common(hv, keysv, key, klen, flags,
                             HV_FETCH_ISSTORE|HV_DISABLE_UVAR_XKEY|return_svp,
                             val, hash);
@@ -786,37 +827,76 @@ Perl_hv_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
     else                                       /* gotta do the real thing */
        HeKEY_hek(entry) = save_hek_flags(key, klen, hash, flags);
     HeVAL(entry) = val;
-    HeNEXT(entry) = *oentry;
-    *oentry = entry;
+
+#ifdef PERL_HASH_RANDOMIZE_KEYS
+    /* This logic semi-randomizes the insert order in a bucket.
+     * Either we insert into the top, or the slot below the top,
+     * making it harder to see if there is a collision. We also
+     * reset the iterator randomizer if there is one.
+     */
+    if ( *oentry && PL_HASH_RAND_BITS_ENABLED) {
+        PL_hash_rand_bits++;
+        PL_hash_rand_bits= ROTL_UV(PL_hash_rand_bits,1);
+        if ( PL_hash_rand_bits & 1 ) {
+            HeNEXT(entry) = HeNEXT(*oentry);
+            HeNEXT(*oentry) = entry;
+        } else {
+            HeNEXT(entry) = *oentry;
+            *oentry = entry;
+        }
+    } else
+#endif
+    {
+        HeNEXT(entry) = *oentry;
+        *oentry = entry;
+    }
+#ifdef PERL_HASH_RANDOMIZE_KEYS
+    if (SvOOK(hv)) {
+        /* Currently this makes various tests warn in annoying ways.
+         * So Silenced for now. - Yves | bogus end of comment =>* /
+        if (HvAUX(hv)->xhv_riter != -1) {
+            Perl_ck_warner_d(aTHX_ packWARN(WARN_INTERNAL),
+                             "[TESTING] Inserting into a hash during each() traversal results in undefined behavior"
+                             pTHX__FORMAT
+                             pTHX__VALUE);
+        }
+        */
+        if (PL_HASH_RAND_BITS_ENABLED) {
+            if (PL_HASH_RAND_BITS_ENABLED == 1)
+                PL_hash_rand_bits += (PTRV)entry + 1;  /* we don't bother to use ptr_hash here */
+            PL_hash_rand_bits= ROTL_UV(PL_hash_rand_bits,1);
+        }
+        HvAUX(hv)->xhv_rand= (U32)PL_hash_rand_bits;
+    }
+#endif
 
     if (val == &PL_sv_placeholder)
        HvPLACEHOLDERS(hv)++;
     if (masked_flags & HVhek_ENABLEHVKFLAGS)
        HvHASKFLAGS_on(hv);
 
-    {
-       const HE *counter = HeNEXT(entry);
-
-       xhv->xhv_keys++; /* HvTOTALKEYS(hv)++ */
-       if (!counter) {                         /* initial entry? */
-       } else if (xhv->xhv_keys > xhv->xhv_max) {
-               /* Use only the old HvUSEDKEYS(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);
-        } else {
-           U32 n_links = 1;
-
-           while ((counter = HeNEXT(counter)))
-               n_links++;
-
-           if (n_links > HV_MAX_LENGTH_BEFORE_SPLIT) {
-               hsplit(hv);
-           }
-       }
+    xhv->xhv_keys++; /* HvTOTALKEYS(hv)++ */
+    if ( DO_HSPLIT(xhv) ) {
+        const STRLEN oldsize = xhv->xhv_max + 1;
+        const U32 items = (U32)HvPLACEHOLDERS_get(hv);
+
+        if (items /* hash has placeholders  */
+            && !SvREADONLY(hv) /* but is not a restricted hash */) {
+            /* If this hash previously was a "restricted hash" and had
+               placeholders, but the "restricted" flag has been turned off,
+               then the placeholders no longer serve any useful purpose.
+               However, they have the downsides of taking up RAM, and adding
+               extra steps when finding used values. It's safe to clear them
+               at this point, 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.
+               If we're lucky, then we may clear sufficient placeholders to
+               avoid needing to split the hash at all.  */
+            clear_placeholders(hv, items);
+            if (DO_HSPLIT(xhv))
+                hsplit(hv, oldsize, oldsize * 2);
+        } else
+            hsplit(hv, oldsize, oldsize * 2);
     }
 
     if (return_svp) {
@@ -849,7 +929,14 @@ S_hv_magic_check(HV *hv, bool *needs_copy, bool *needs_store)
 /*
 =for apidoc hv_scalar
 
-Evaluates the hash in scalar context and returns the result. Handles magic when the hash is tied.
+Evaluates the hash in scalar context and returns the result.
+
+When the hash is tied dispatches through to the SCALAR method,
+otherwise returns a mortal SV containing the number of keys
+in the hash.
+
+Note, prior to 5.25 this function returned what is now
+returned by the hv_bucket_ratio() function.
 
 =cut
 */
@@ -868,7 +955,41 @@ Perl_hv_scalar(pTHX_ HV *hv)
     }
 
     sv = sv_newmortal();
-    if (HvTOTALKEYS((const HV *)hv)) 
+    sv_setuv(sv, HvUSEDKEYS(hv));
+
+    return sv;
+}
+
+/*
+=for apidoc hv_bucket_ratio
+
+If the hash is tied dispatches through to the SCALAR tied method,
+otherwise if the hash contains no keys returns 0, otherwise returns
+a mortal sv containing a string specifying the number of used buckets,
+followed by a slash, followed by the number of available buckets.
+
+This function is expensive, it must scan all of the buckets
+to determine which are used, and the count is NOT cached.
+In a large hash this could be a lot of buckets.
+
+=cut
+*/
+
+SV *
+Perl_hv_bucket_ratio(pTHX_ HV *hv)
+{
+    SV *sv;
+
+    PERL_ARGS_ASSERT_HV_BUCKET_RATIO;
+
+    if (SvRMAGICAL(hv)) {
+        MAGIC * const mg = mg_find((const SV *)hv, PERL_MAGIC_tied);
+        if (mg)
+            return magic_scalarpack(hv, mg);
+    }
+
+    sv = sv_newmortal();
+    if (HvUSEDKEYS((const HV *)hv))
         Perl_sv_setpvf(aTHX_ sv, "%ld/%ld",
                 (long)HvFILL(hv), (long)HvMAX(hv) + 1);
     else
@@ -884,15 +1005,15 @@ Deletes a key/value pair in the hash.  The value's SV is removed from
 the hash, made mortal, and returned to the caller.  The absolute
 value of C<klen> is the length of the key.  If C<klen> is negative the
 key is assumed to be in UTF-8-encoded Unicode.  The C<flags> value
-will normally be zero; if set to G_DISCARD then NULL will be returned.
-NULL will also be returned if the key is not found.
+will normally be zero; if set to C<G_DISCARD> then C<NULL> will be returned.
+C<NULL> will also be returned if the key is not found.
 
 =for apidoc hv_delete_ent
 
 Deletes a key/value pair in the hash.  The value SV is removed from the hash,
 made mortal, and returned to the caller.  The C<flags> value will normally be
-zero; if set to G_DISCARD then NULL will be returned.  NULL will also be
-returned if the key is not found.  C<hash> can be a valid precomputed hash
+zero; if set to C<G_DISCARD> then C<NULL> will be returned.  C<NULL> will also
+be returned if the key is not found.  C<hash> can be a valid precomputed hash
 value, or 0 to ask for it to be computed.
 
 =cut
@@ -906,8 +1027,14 @@ S_hv_delete_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
     XPVHV* xhv;
     HE *entry;
     HE **oentry;
+    HE **first_entry;
     bool is_utf8 = (k_flags & HVhek_UTF8) ? TRUE : FALSE;
     int masked_flags;
+    HEK *keysv_hek = NULL;
+    U8 mro_changes = 0; /* 1 = isa; 2 = package moved */
+    SV *sv;
+    GV *gv = NULL;
+    HV *stash = NULL;
 
     if (SvRMAGICAL(hv)) {
        bool needs_copy;
@@ -972,32 +1099,60 @@ S_hv_delete_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
         HvHASKFLAGS_on(MUTABLE_SV(hv));
     }
 
-    if (!hash) {
-        if (keysv && (SvIsCOW_shared_hash(keysv)))
-            hash = SvSHARED_HASH(keysv);
-        else
-            PERL_HASH(hash, key, klen);
+    if (keysv && (SvIsCOW_shared_hash(keysv))) {
+        if (HvSHAREKEYS(hv))
+            keysv_hek  = SvSHARED_HEK_FROM_PV(SvPVX_const(keysv));
+        hash = SvSHARED_HASH(keysv);
     }
+    else if (!hash)
+        PERL_HASH(hash, key, klen);
 
     masked_flags = (k_flags & HVhek_MASK);
 
-    oentry = &(HvARRAY(hv))[hash & (I32) HvMAX(hv)];
+    first_entry = oentry = &(HvARRAY(hv))[hash & (I32) HvMAX(hv)];
     entry = *oentry;
-    for (; entry; oentry = &HeNEXT(entry), entry = *oentry) {
-       SV *sv;
-       U8 mro_changes = 0; /* 1 = isa; 2 = package moved */
-       GV *gv = NULL;
-       HV *stash = NULL;
 
+    if (!entry)
+        goto not_found;
+
+    if (keysv_hek) {
+        /* keysv is actually a HEK in disguise, so we can match just by
+         * comparing the HEK pointers in the HE chain. There is a slight
+         * caveat: on something like "\x80", which has both plain and utf8
+         * representations, perl's hashes do encoding-insensitive lookups,
+         * but preserve the encoding of the stored key. Thus a particular
+         * key could map to two different HEKs in PL_strtab. We only
+         * conclude 'not found' if all the flags are the same; otherwise
+         * we fall back to a full search (this should only happen in rare
+         * cases).
+         */
+        int keysv_flags = HEK_FLAGS(keysv_hek);
+
+        for (; entry; oentry = &HeNEXT(entry), entry = *oentry) {
+            HEK *hek = HeKEY_hek(entry);
+            if (hek == keysv_hek)
+                goto found;
+            if (HEK_FLAGS(hek) != keysv_flags)
+                break; /* need to do full match */
+        }
+        if (!entry)
+            goto not_found;
+        /* failed on shortcut - do full search loop */
+        oentry = first_entry;
+        entry = *oentry;
+    }
+
+    for (; entry; 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? */
+       if (memNE(HeKEY(entry),key,klen))       /* is this it? */
            continue;
        if ((HeKFLAGS(entry) ^ masked_flags) & HVhek_UTF8)
            continue;
 
+      found:
        if (hv == PL_strtab) {
            if (k_flags & HVhek_FREEKEY)
                Safefree(key);
@@ -1010,8 +1165,7 @@ S_hv_delete_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
                Safefree(key);
            return NULL;
        }
-       if (SvREADONLY(hv) && HeVAL(entry) && SvREADONLY(HeVAL(entry))
-        && !SvIsCOW(HeVAL(entry))) {
+       if (SvREADONLY(hv) && HeVAL(entry) && SvREADONLY(HeVAL(entry))) {
            hv_notallowed(k_flags, key, klen,
                            "Attempt to delete readonly key '%"SVf"' from"
                            " a restricted hash");
@@ -1044,8 +1198,73 @@ S_hv_delete_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
                         sv_2mortal((SV *)gv)
                        );
                }
-               else if (klen == 3 && strnEQ(key, "ISA", 3))
+               else if (klen == 3 && strEQs(key, "ISA") && GvAV(gv)) {
+                    AV *isa = GvAV(gv);
+                    MAGIC *mg = mg_find((SV*)isa, PERL_MAGIC_isa);
+
                    mro_changes = 1;
+                    if (mg) {
+                        if (mg->mg_obj == (SV*)gv) {
+                            /* This is the only stash this ISA was used for.
+                             * The isaelem magic asserts if there's no
+                             * isa magic on the array, so explicitly
+                             * remove the magic on both the array and its
+                             * elements.  @ISA shouldn't be /too/ large.
+                             */
+                            SV **svp, **end;
+                        strip_magic:
+                            svp = AvARRAY(isa);
+                            end = svp + AvFILLp(isa)+1;
+                            while (svp < end) {
+                                if (*svp)
+                                    mg_free_type(*svp, PERL_MAGIC_isaelem);
+                                ++svp;
+                            }
+                            mg_free_type((SV*)GvAV(gv), PERL_MAGIC_isa);
+                        }
+                        else {
+                            /* mg_obj is an array of stashes
+                               Note that the array doesn't keep a reference
+                               count on the stashes.
+                             */
+                            AV *av = (AV*)mg->mg_obj;
+                            SV **svp, **arrayp;
+                            SSize_t index;
+                            SSize_t items;
+
+                            assert(SvTYPE(mg->mg_obj) == SVt_PVAV);
+
+                            /* remove the stash from the magic array */
+                            arrayp = svp = AvARRAY(av);
+                            items = AvFILLp(av) + 1;
+                            if (items == 1) {
+                                assert(*arrayp == (SV *)gv);
+                                mg->mg_obj = NULL;
+                                /* avoid a double free on the last stash */
+                                AvFILLp(av) = -1;
+                                /* The magic isn't MGf_REFCOUNTED, so release
+                                 * the array manually.
+                                 */
+                                SvREFCNT_dec_NN(av);
+                                goto strip_magic;
+                            }
+                            else {
+                                while (items--) {
+                                    if (*svp == (SV*)gv)
+                                        break;
+                                    ++svp;
+                                }
+                                index = svp - arrayp;
+                                assert(index >= 0 && index <= AvFILLp(av));
+                                if (index < AvFILLp(av)) {
+                                    arrayp[index] = arrayp[AvFILLp(av)];
+                                }
+                                arrayp[AvFILLp(av)] = NULL;
+                                --AvFILLp(av);
+                            }
+                        }
+                    }
+                }
        }
 
        sv = d_flags & G_DISCARD ? HeVAL(entry) : sv_2mortal(HeVAL(entry));
@@ -1093,6 +1312,8 @@ S_hv_delete_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
 
        return sv;
     }
+
+  not_found:
     if (SvREADONLY(hv)) {
        hv_notallowed(k_flags, key, klen,
                        "Attempt to delete disallowed key '%"SVf"' from"
@@ -1104,96 +1325,133 @@ S_hv_delete_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
     return NULL;
 }
 
+
 STATIC void
-S_hsplit(pTHX_ HV *hv)
+S_hsplit(pTHX_ HV *hv, STRLEN const oldsize, STRLEN newsize)
 {
-    dVAR;
-    XPVHV* const xhv = (XPVHV*)SvANY(hv);
-    const I32 oldsize = (I32) xhv->xhv_max+1; /* HvMAX(hv)+1 (sick) */
-    I32 newsize = oldsize * 2;
-    I32 i;
+    STRLEN i = 0;
     char *a = (char*) HvARRAY(hv);
     HE **aep;
 
-    PERL_ARGS_ASSERT_HSPLIT;
+    bool do_aux= (
+        /* already have an HvAUX(hv) so we have to move it */
+        SvOOK(hv) ||
+        /* no HvAUX() but array we are going to allocate is large enough
+         * there is no point in saving the space for the iterator, and
+         * speeds up later traversals. */
+        ( ( hv != PL_strtab ) && ( newsize >= PERL_HV_ALLOC_AUX_SIZE ) )
+    );
 
-    /*PerlIO_printf(PerlIO_stderr(), "hsplit called for %p which had %d\n",
-      (void*)hv, (int) oldsize);*/
+    PERL_ARGS_ASSERT_HSPLIT;
 
-    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)
-         + (SvOOK(hv) ? sizeof(struct xpvhv_aux) : 0), char);
-    if (!a) {
-      PL_nomemok = FALSE;
-      return;
-    }
-    if (SvOOK(hv)) {
-       Move(&a[oldsize * sizeof(HE*)], &a[newsize * sizeof(HE*)], 1, struct xpvhv_aux);
-    }
-#else
-    Newx(a, PERL_HV_ARRAY_ALLOC_BYTES(newsize)
-       + (SvOOK(hv) ? sizeof(struct xpvhv_aux) : 0), char);
+          + (do_aux ? sizeof(struct xpvhv_aux) : 0), char);
+    PL_nomemok = FALSE;
     if (!a) {
-      PL_nomemok = FALSE;
       return;
     }
-    Copy(HvARRAY(hv), a, oldsize * sizeof(HE*), char);
-    if (SvOOK(hv)) {
-       Copy(HvAUX(hv), &a[newsize * sizeof(HE*)], 1, struct xpvhv_aux);
+
+#ifdef PERL_HASH_RANDOMIZE_KEYS
+    /* the idea of this is that we create a "random" value by hashing the address of
+     * the array, we then use the low bit to decide if we insert at the top, or insert
+     * second from top. After each such insert we rotate the hashed value. So we can
+     * use the same hashed value over and over, and in normal build environments use
+     * very few ops to do so. ROTL32() should produce a single machine operation. */
+    if (PL_HASH_RAND_BITS_ENABLED) {
+        if (PL_HASH_RAND_BITS_ENABLED == 1)
+            PL_hash_rand_bits += ptr_hash((PTRV)a);
+        PL_hash_rand_bits = ROTL_UV(PL_hash_rand_bits,1);
     }
-    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 */
     HvARRAY(hv) = (HE**) a;
-    aep = (HE**)a;
+    HvMAX(hv) = newsize - 1;
+    /* before we zero the newly added memory, we
+     * need to deal with the aux struct that may be there
+     * or have been allocated by us*/
+    if (do_aux) {
+        struct xpvhv_aux *const dest
+            = (struct xpvhv_aux*) &a[newsize * sizeof(HE*)];
+        if (SvOOK(hv)) {
+            /* alread have an aux, copy the old one in place. */
+            Move(&a[oldsize * sizeof(HE*)], dest, 1, struct xpvhv_aux);
+            /* we reset the iterator's xhv_rand as well, so they get a totally new ordering */
+#ifdef PERL_HASH_RANDOMIZE_KEYS
+            dest->xhv_rand = (U32)PL_hash_rand_bits;
+#endif
+        } else {
+            /* no existing aux structure, but we allocated space for one
+             * so initialize it properly. This unrolls hv_auxinit() a bit,
+             * since we have to do the realloc anyway. */
+            /* first we set the iterator's xhv_rand so it can be copied into lastrand below */
+#ifdef PERL_HASH_RANDOMIZE_KEYS
+            dest->xhv_rand = (U32)PL_hash_rand_bits;
+#endif
+            /* this is the "non realloc" part of the hv_auxinit() */
+            (void)hv_auxinit_internal(dest);
+            /* Turn on the OOK flag */
+            SvOOK_on(hv);
+        }
+    }
+    /* now we can safely clear the second half */
+    Zero(&a[oldsize * sizeof(HE*)], (newsize-oldsize) * sizeof(HE*), char);    /* zero 2nd half*/
+
+    if (!HvTOTALKEYS(hv))       /* skip rest if no entries */
+        return;
 
-    for (i=0; i<oldsize; i++,aep++) {
-       HE **oentry = aep;
-       HE *entry = *aep;
-       HE **bep;
+    newsize--;
+    aep = (HE**)a;
+    do {
+       HE **oentry = aep + i;
+       HE *entry = aep[i];
 
        if (!entry)                             /* non-existent */
            continue;
-       bep = aep+oldsize;
        do {
-           if ((HeHASH(entry) & newsize) != (U32)i) {
+            U32 j = (HeHASH(entry) & newsize);
+           if (j != (U32)i) {
                *oentry = HeNEXT(entry);
-               HeNEXT(entry) = *bep;
-               *bep = entry;
+#ifdef PERL_HASH_RANDOMIZE_KEYS
+                /* if the target cell is empty or PL_HASH_RAND_BITS_ENABLED is false
+                 * insert to top, otherwise rotate the bucket rand 1 bit,
+                 * and use the new low bit to decide if we insert at top,
+                 * or next from top. IOW, we only rotate on a collision.*/
+                if (aep[j] && PL_HASH_RAND_BITS_ENABLED) {
+                    PL_hash_rand_bits+= ROTL32(HeHASH(entry), 17);
+                    PL_hash_rand_bits= ROTL_UV(PL_hash_rand_bits,1);
+                    if (PL_hash_rand_bits & 1) {
+                        HeNEXT(entry)= HeNEXT(aep[j]);
+                        HeNEXT(aep[j])= entry;
+                    } else {
+                        /* Note, this is structured in such a way as the optimizer
+                        * should eliminate the duplicated code here and below without
+                        * us needing to explicitly use a goto. */
+                        HeNEXT(entry) = aep[j];
+                        aep[j] = entry;
+                    }
+                } else
+#endif
+                {
+                    /* see comment above about duplicated code */
+                    HeNEXT(entry) = aep[j];
+                    aep[j] = entry;
+                }
            }
            else {
                oentry = &HeNEXT(entry);
            }
            entry = *oentry;
        } while (entry);
-       /* I think we don't actually need to keep track of the longest length,
-          merely flag if anything is too long. But for the moment while
-          developing this code I'll track it.  */
-    }
+    } while (i++ < oldsize);
 }
 
 void
 Perl_hv_ksplit(pTHX_ HV *hv, IV newmax)
 {
-    dVAR;
     XPVHV* xhv = (XPVHV*)SvANY(hv);
     const I32 oldsize = (I32) xhv->xhv_max+1; /* HvMAX(hv)+1 (sick) */
     I32 newsize;
-    I32 i;
     char *a;
-    HE **aep;
 
     PERL_ARGS_ASSERT_HV_KSPLIT;
 
@@ -1210,63 +1468,28 @@ Perl_hv_ksplit(pTHX_ HV *hv, IV newmax)
 
     a = (char *) HvARRAY(hv);
     if (a) {
-       PL_nomemok = TRUE;
-#if defined(STRANGE_MALLOC) || defined(MYMALLOC)
-       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
-       Newx(a, PERL_HV_ARRAY_ALLOC_BYTES(newsize)
-           + (SvOOK(hv) ? sizeof(struct xpvhv_aux) : 0), char);
-       if (!a) {
-         PL_nomemok = FALSE;
-         return;
-       }
-       Copy(HvARRAY(hv), a, oldsize * sizeof(HE*), char);
-       if (SvOOK(hv)) {
-           Copy(HvAUX(hv), &a[newsize * sizeof(HE*)], 1, struct xpvhv_aux);
-       }
-       Safefree(HvARRAY(hv));
-#endif
-       PL_nomemok = FALSE;
-       Zero(&a[oldsize * sizeof(HE*)], (newsize-oldsize) * sizeof(HE*), char); /* zero 2nd half*/
-    }
-    else {
-       Newxz(a, PERL_HV_ARRAY_ALLOC_BYTES(newsize), char);
+        hsplit(hv, oldsize, newsize);
+    } else {
+        Newxz(a, PERL_HV_ARRAY_ALLOC_BYTES(newsize), char);
+        xhv->xhv_max = --newsize;
+        HvARRAY(hv) = (HE **) a;
     }
-    xhv->xhv_max = --newsize;  /* HvMAX(hv) = --newsize */
-    HvARRAY(hv) = (HE **) a;
-    if (!xhv->xhv_keys /* !HvTOTALKEYS(hv) */) /* skip rest if no entries */
-       return;
+}
 
-    aep = (HE**)a;
-    for (i=0; i<oldsize; i++,aep++) {
-       HE **oentry = aep;
-       HE *entry = *aep;
+/* IMO this should also handle cases where hv_max is smaller than hv_keys
+ * as tied hashes could play silly buggers and mess us around. We will
+ * do the right thing during hv_store() afterwards, but still - Yves */
+#define HV_SET_MAX_ADJUSTED_FOR_KEYS(hv,hv_max,hv_keys) STMT_START {\
+    /* Can we use fewer buckets? (hv_max is always 2^n-1) */        \
+    if (hv_max < PERL_HASH_DEFAULT_HvMAX) {                         \
+        hv_max = PERL_HASH_DEFAULT_HvMAX;                           \
+    } else {                                                        \
+        while (hv_max > PERL_HASH_DEFAULT_HvMAX && hv_max + 1 >= hv_keys * 2) \
+            hv_max = hv_max / 2;                                    \
+    }                                                               \
+    HvMAX(hv) = hv_max;                                             \
+} STMT_END
 
-       if (!entry)                             /* non-existent */
-           continue;
-       do {
-           I32 j = (HeHASH(entry) & newsize);
-
-           if (j != i) {
-               j -= i;
-               *oentry = HeNEXT(entry);
-               HeNEXT(entry) = aep[j];
-               aep[j] = entry;
-           }
-           else
-               oentry = &HeNEXT(entry);
-           entry = *oentry;
-       } while (entry);
-    }
-}
 
 HV *
 Perl_newHVhv(pTHX_ HV *ohv)
@@ -1329,12 +1552,9 @@ Perl_newHVhv(pTHX_ HV *ohv)
        HE *entry;
        const I32 riter = HvRITER_get(ohv);
        HE * const eiter = HvEITER_get(ohv);
-       STRLEN hv_fill = HvFILL(ohv);
+        STRLEN hv_keys = HvTOTALKEYS(ohv);
 
-       /* Can we use fewer buckets? (hv_max is always 2^n-1) */
-       while (hv_max && hv_max + 1 >= hv_fill * 2)
-           hv_max = hv_max / 2;
-       HvMAX(hv) = hv_max;
+        HV_SET_MAX_ADJUSTED_FOR_KEYS(hv,hv_max,hv_keys);
 
        hv_iterinit(ohv);
        while ((entry = hv_iternext_flags(ohv, 0))) {
@@ -1357,10 +1577,10 @@ Perl_newHVhv(pTHX_ HV *ohv)
 /*
 =for apidoc Am|HV *|hv_copy_hints_hv|HV *ohv
 
-A specialised version of L</newHVhv> for copying C<%^H>.  I<ohv> must be
+A specialised version of L</newHVhv> for copying C<%^H>.  C<ohv> must be
 a pointer to a hash (which may have C<%^H> magic, but should be generally
 non-magical), or C<NULL> (interpreted as an empty hash).  The content
-of I<ohv> is copied to a new hash, which has the C<%^H>-specific magic
+of C<ohv> is copied to a new hash, which has the C<%^H>-specific magic
 added to it.  A pointer to the new hash is returned.
 
 =cut
@@ -1373,7 +1593,7 @@ Perl_hv_copy_hints_hv(pTHX_ HV *const ohv)
 
     if (ohv) {
        STRLEN hv_max = HvMAX(ohv);
-       STRLEN hv_fill = HvFILL(ohv);
+        STRLEN hv_keys = HvTOTALKEYS(ohv);
        HE *entry;
        const I32 riter = HvRITER_get(ohv);
        HE * const eiter = HvEITER_get(ohv);
@@ -1381,9 +1601,7 @@ Perl_hv_copy_hints_hv(pTHX_ HV *const ohv)
        ENTER;
        SAVEFREESV(hv);
 
-       while (hv_max && hv_max + 1 >= hv_fill * 2)
-           hv_max = hv_max / 2;
-       HvMAX(hv) = hv_max;
+        HV_SET_MAX_ADJUSTED_FOR_KEYS(hv,hv_max,hv_keys);
 
        hv_iterinit(ohv);
        while ((entry = hv_iternext_flags(ohv, 0))) {
@@ -1397,7 +1615,7 @@ Perl_hv_copy_hints_hv(pTHX_ HV *const ohv)
            else {
                (void)hv_common(hv, heksv, HeKEY(entry), HeKLEN(entry),
                                 HeKFLAGS(entry), HV_FETCH_ISSTORE|HV_FETCH_JUST_SV, sv, HeHASH(entry));
-               SvREFCNT_dec(heksv);
+               SvREFCNT_dec_NN(heksv);
            }
        }
        HvRITER_set(ohv, riter);
@@ -1409,18 +1627,16 @@ Perl_hv_copy_hints_hv(pTHX_ HV *const ohv)
     hv_magic(hv, NULL, PERL_MAGIC_hints);
     return hv;
 }
+#undef HV_SET_MAX_ADJUSTED_FOR_KEYS
 
 /* like hv_free_ent, but returns the SV rather than freeing it */
 STATIC SV*
-S_hv_free_ent_ret(pTHX_ HV *hv, register HE *entry)
+S_hv_free_ent_ret(pTHX_ HV *hv, HE *entry)
 {
-    dVAR;
     SV *val;
 
     PERL_ARGS_ASSERT_HV_FREE_ENT_RET;
 
-    if (!entry)
-       return NULL;
     val = HeVAL(entry);
     if (HeKLEN(entry) == HEf_SVKEY) {
        SvREFCNT_dec(HeKEY_sv(entry));
@@ -1436,9 +1652,8 @@ S_hv_free_ent_ret(pTHX_ HV *hv, register HE *entry)
 
 
 void
-Perl_hv_free_ent(pTHX_ HV *hv, register HE *entry)
+Perl_hv_free_ent(pTHX_ HV *hv, HE *entry)
 {
-    dVAR;
     SV *val;
 
     PERL_ARGS_ASSERT_HV_FREE_ENT;
@@ -1451,10 +1666,8 @@ Perl_hv_free_ent(pTHX_ HV *hv, register HE *entry)
 
 
 void
-Perl_hv_delayfree_ent(pTHX_ HV *hv, register HE *entry)
+Perl_hv_delayfree_ent(pTHX_ HV *hv, HE *entry)
 {
-    dVAR;
-
     PERL_ARGS_ASSERT_HV_DELAYFREE_ENT;
 
     if (!entry)
@@ -1473,8 +1686,8 @@ Perl_hv_delayfree_ent(pTHX_ HV *hv, register HE *entry)
 Frees the all the elements of a hash, leaving it empty.
 The XS equivalent of C<%hash = ()>.  See also L</hv_undef>.
 
-If any destructors are triggered as a result, the hv itself may
-be freed.
+See L</av_clear> for a note about the hash possibly being invalid on
+return.
 
 =cut
 */
@@ -1483,6 +1696,8 @@ void
 Perl_hv_clear(pTHX_ HV *hv)
 {
     dVAR;
+    SSize_t orig_ix;
+
     XPVHV* xhv;
     if (!hv)
        return;
@@ -1491,8 +1706,10 @@ Perl_hv_clear(pTHX_ HV *hv)
 
     xhv = (XPVHV*)SvANY(hv);
 
-    ENTER;
-    SAVEFREESV(SvREFCNT_inc_simple_NN(hv));
+    /* avoid hv being freed when calling destructors below */
+    EXTEND_MORTAL(1);
+    PL_tmps_stack[++PL_tmps_ix] = SvREFCNT_inc_simple_NN(hv);
+    orig_ix = PL_tmps_ix;
     if (SvREADONLY(hv) && HvARRAY(hv) != NULL) {
        /* restricted hash: convert all keys to placeholders */
        STRLEN i;
@@ -1501,14 +1718,15 @@ Perl_hv_clear(pTHX_ HV *hv)
            for (; entry; entry = HeNEXT(entry)) {
                /* not already placeholder */
                if (HeVAL(entry) != &PL_sv_placeholder) {
-                   if (HeVAL(entry) && SvREADONLY(HeVAL(entry))
-                    && !SvIsCOW(HeVAL(entry))) {
-                       SV* const keysv = hv_iterkeysv(entry);
-                       Perl_croak(aTHX_
-                                  "Attempt to delete readonly key '%"SVf"' from a restricted hash",
-                                  (void*)keysv);
+                   if (HeVAL(entry)) {
+                       if (SvREADONLY(HeVAL(entry))) {
+                           SV* const keysv = hv_iterkeysv(entry);
+                           Perl_croak_nocontext(
+                               "Attempt to delete readonly key '%"SVf"' from a restricted hash",
+                               (void*)keysv);
+                       }
+                       SvREFCNT_dec_NN(HeVAL(entry));
                    }
-                   SvREFCNT_dec(HeVAL(entry));
                    HeVAL(entry) = &PL_sv_placeholder;
                    HvPLACEHOLDERS(hv)++;
                }
@@ -1529,7 +1747,12 @@ Perl_hv_clear(pTHX_ HV *hv)
             mro_isa_changed_in(hv);
        HvEITER_set(hv, NULL);
     }
-    LEAVE;
+    /* disarm hv's premature free guard */
+    if (LIKELY(PL_tmps_ix == orig_ix))
+        PL_tmps_ix--;
+    else
+        PL_tmps_stack[orig_ix] = &PL_sv_undef;
+    SvREFCNT_dec_NN(hv);
 }
 
 /*
@@ -1537,11 +1760,12 @@ Perl_hv_clear(pTHX_ HV *hv)
 
 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
+deleted but is marked by assigning it a value of C<&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 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.
+See C<L<Hash::Util::lock_keys()|Hash::Util/lock_keys>> for an example of its
+use.
 
 =cut
 */
@@ -1549,7 +1773,6 @@ See Hash::Util::lock_keys() for an example of its use.
 void
 Perl_hv_clear_placeholders(pTHX_ HV *hv)
 {
-    dVAR;
     const U32 items = (U32)HvPLACEHOLDERS_get(hv);
 
     PERL_ARGS_ASSERT_HV_CLEAR_PLACEHOLDERS;
@@ -1589,8 +1812,10 @@ S_clear_placeholders(pTHX_ HV *hv, U32 items)
 
                if (--items == 0) {
                    /* Finished.  */
-                   HvTOTALKEYS(hv) -= (IV)HvPLACEHOLDERS_get(hv);
-                   if (HvUSEDKEYS(hv) == 0)
+                   I32 placeholders = HvPLACEHOLDERS_get(hv);
+                   HvTOTALKEYS(hv) -= (IV)placeholders;
+                   /* HvUSEDKEYS expanded */
+                   if ((HvTOTALKEYS(hv) - placeholders) == 0)
                        HvHASKFLAGS_off(hv);
                    HvPLACEHOLDERS_set(hv, 0);
                    return;
@@ -1602,7 +1827,7 @@ S_clear_placeholders(pTHX_ HV *hv, U32 items)
     } while (--i >= 0);
     /* You can't get here, hence assertion should always fail.  */
     assert (items == 0);
-    assert (0);
+    NOT_REACHED; /* NOTREACHED */
 }
 
 STATIC void
@@ -1641,19 +1866,23 @@ Perl_hfree_next_entry(pTHX_ HV *hv, STRLEN *indexp)
 
     PERL_ARGS_ASSERT_HFREE_NEXT_ENTRY;
 
-    if (SvOOK(hv) && ((iter = HvAUX(hv)))
-       && ((entry = iter->xhv_eiter)) )
-    {
-       /* the iterator may get resurrected after each
-        * destructor call, so check each time */
-       if (entry && HvLAZYDEL(hv)) {   /* was deleted earlier? */
-           HvLAZYDEL_off(hv);
-           hv_free_ent(hv, entry);
-           /* warning: at this point HvARRAY may have been
-            * re-allocated, HvMAX changed etc */
-       }
-       iter->xhv_riter = -1;   /* HvRITER(hv) = -1 */
-       iter->xhv_eiter = NULL; /* HvEITER(hv) = NULL */
+    if (SvOOK(hv) && ((iter = HvAUX(hv)))) {
+       if ((entry = iter->xhv_eiter)) {
+            /* the iterator may get resurrected after each
+             * destructor call, so check each time */
+            if (entry && HvLAZYDEL(hv)) {      /* was deleted earlier? */
+                HvLAZYDEL_off(hv);
+                hv_free_ent(hv, entry);
+                /* warning: at this point HvARRAY may have been
+                 * re-allocated, HvMAX changed etc */
+            }
+            iter = HvAUX(hv); /* may have been realloced */
+            iter->xhv_riter = -1;      /* HvRITER(hv) = -1 */
+            iter->xhv_eiter = NULL;    /* HvEITER(hv) = NULL */
+#ifdef PERL_HASH_RANDOMIZE_KEYS
+            iter->xhv_last_rand = iter->xhv_rand;
+#endif
+        }
     }
 
     if (!((XPVHV*)SvANY(hv))->xhv_keys)
@@ -1692,13 +1921,11 @@ Perl_hfree_next_entry(pTHX_ HV *hv, STRLEN *indexp)
 
 Undefines the hash.  The XS equivalent of C<undef(%hash)>.
 
-As well as freeing all the elements of the hash (like hv_clear()), this
+As well as freeing all the elements of the hash (like C<hv_clear()>), this
 also frees any auxiliary data and storage associated with the hash.
 
-If any destructors are triggered as a result, the hv itself may
-be freed.
-
-See also L</hv_clear>.
+See L</av_clear> for a note about the hash possibly being invalid on
+return.
 
 =cut
 */
@@ -1706,13 +1933,13 @@ See also L</hv_clear>.
 void
 Perl_hv_undef_flags(pTHX_ HV *hv, U32 flags)
 {
-    dVAR;
     XPVHV* xhv;
-    const char *name;
-    const bool save = !!SvREFCNT(hv);
+    bool save;
+    SSize_t orig_ix;
 
     if (!hv)
        return;
+    save = cBOOL(SvREFCNT(hv));
     DEBUG_A(Perl_hv_assert(aTHX_ hv));
     xhv = (XPVHV*)SvANY(hv);
 
@@ -1726,75 +1953,68 @@ Perl_hv_undef_flags(pTHX_ HV *hv, U32 flags)
        if they will be freed anyway. */
     /* note that the code following prior to hfreeentries is duplicated
      * in sv_clear(), and changes here should be done there too */
-    if (PL_phase != PERL_PHASE_DESTRUCT && (name = HvNAME(hv))) {
+    if (PL_phase != PERL_PHASE_DESTRUCT && HvNAME(hv)) {
         if (PL_stashcache) {
             DEBUG_o(Perl_deb(aTHX_ "hv_undef_flags clearing PL_stashcache for '%"
-                             HEKf"'\n", HvNAME_HEK(hv)));
-           (void)hv_delete(PL_stashcache, name,
-                            HEK_UTF8(HvNAME_HEK(hv)) ? -HvNAMELEN_get(hv) : HvNAMELEN_get(hv),
-                            G_DISCARD
-                           );
+                             HEKf"'\n", HEKfARG(HvNAME_HEK(hv))));
+           (void)hv_deletehek(PL_stashcache, HvNAME_HEK(hv), G_DISCARD);
         }
        hv_name_set(hv, NULL, 0, 0);
     }
     if (save) {
-       ENTER;
-       SAVEFREESV(SvREFCNT_inc_simple_NN(hv));
+        /* avoid hv being freed when calling destructors below */
+        EXTEND_MORTAL(1);
+        PL_tmps_stack[++PL_tmps_ix] = SvREFCNT_inc_simple_NN(hv);
+        orig_ix = PL_tmps_ix;
     }
     hfreeentries(hv);
     if (SvOOK(hv)) {
-      struct xpvhv_aux * const aux = HvAUX(hv);
       struct mro_meta *meta;
+      const char *name;
 
-      if ((name = HvENAME_get(hv))) {
+      if (HvENAME_get(hv)) {
        if (PL_phase != PERL_PHASE_DESTRUCT)
            mro_isa_changed_in(hv);
         if (PL_stashcache) {
             DEBUG_o(Perl_deb(aTHX_ "hv_undef_flags clearing PL_stashcache for effective name '%"
-                             HEKf"'\n", HvENAME_HEK(hv)));
-           (void)hv_delete(
-                   PL_stashcache, name,
-                    HEK_UTF8(HvENAME_HEK(hv)) ? -HvENAMELEN_get(hv) : HvENAMELEN_get(hv),
-                    G_DISCARD
-                 );
+                             HEKf"'\n", HEKfARG(HvENAME_HEK(hv))));
+           (void)hv_deletehek(PL_stashcache, HvENAME_HEK(hv), G_DISCARD);
         }
       }
 
       /* If this call originated from sv_clear, then we must check for
        * effective names that need freeing, as well as the usual name. */
       name = HvNAME(hv);
-      if (flags & HV_NAME_SETALL ? !!aux->xhv_name_u.xhvnameu_name : !!name) {
+      if (flags & HV_NAME_SETALL ? !!HvAUX(hv)->xhv_name_u.xhvnameu_name : !!name) {
         if (name && PL_stashcache) {
             DEBUG_o(Perl_deb(aTHX_ "hv_undef_flags clearing PL_stashcache for name '%"
-                             HEKf"'\n", HvNAME_HEK(hv)));
-           (void)hv_delete(PL_stashcache, name, (HEK_UTF8(HvNAME_HEK(hv)) ? -HvNAMELEN_get(hv) : HvNAMELEN_get(hv)), G_DISCARD);
+                             HEKf"'\n", HEKfARG(HvNAME_HEK(hv))));
+           (void)hv_deletehek(PL_stashcache, HvNAME_HEK(hv), G_DISCARD);
         }
        hv_name_set(hv, NULL, 0, flags);
       }
-      if((meta = aux->xhv_mro_meta)) {
+      if((meta = HvAUX(hv)->xhv_mro_meta)) {
        if (meta->mro_linear_all) {
-           SvREFCNT_dec(MUTABLE_SV(meta->mro_linear_all));
-           meta->mro_linear_all = NULL;
-           /* This is just acting as a shortcut pointer.  */
-           meta->mro_linear_current = NULL;
-       } else if (meta->mro_linear_current) {
+           SvREFCNT_dec_NN(meta->mro_linear_all);
+           /* mro_linear_current is just acting as a shortcut pointer,
+              hence the else.  */
+       }
+       else
            /* Only the current MRO is stored, so this owns the data.
             */
            SvREFCNT_dec(meta->mro_linear_current);
-           meta->mro_linear_current = NULL;
-       }
        SvREFCNT_dec(meta->mro_nextmethod);
        SvREFCNT_dec(meta->isa);
+       SvREFCNT_dec(meta->super);
        Safefree(meta);
-       aux->xhv_mro_meta = NULL;
+       HvAUX(hv)->xhv_mro_meta = NULL;
       }
-      SvREFCNT_dec(aux->xhv_super);
-      if (!aux->xhv_name_u.xhvnameu_name && ! aux->xhv_backreferences)
+      if (!HvAUX(hv)->xhv_name_u.xhvnameu_name && ! HvAUX(hv)->xhv_backreferences)
        SvFLAGS(hv) &= ~SVf_OOK;
     }
     if (!SvOOK(hv)) {
        Safefree(HvARRAY(hv));
-       xhv->xhv_max   = 7;     /* HvMAX(hv) = 7 (it's a normal hash) */
+        xhv->xhv_max = PERL_HASH_DEFAULT_HvMAX;        /* HvMAX(hv) = 7 (it's a normal hash) */
        HvARRAY(hv) = 0;
     }
     /* if we're freeing the HV, the SvMAGIC field has been reused for
@@ -1804,30 +2024,52 @@ Perl_hv_undef_flags(pTHX_ HV *hv, U32 flags)
 
     if (SvRMAGICAL(hv))
        mg_clear(MUTABLE_SV(hv));
-    if (save) LEAVE;
+
+    if (save) {
+        /* disarm hv's premature free guard */
+        if (LIKELY(PL_tmps_ix == orig_ix))
+            PL_tmps_ix--;
+        else
+            PL_tmps_stack[orig_ix] = &PL_sv_undef;
+        SvREFCNT_dec_NN(hv);
+    }
 }
 
 /*
 =for apidoc hv_fill
 
-Returns the number of hash buckets that happen to be in use. This function is
-wrapped by the macro C<HvFILL>.
+Returns the number of hash buckets that happen to be in use.
+
+This function is wrapped by the macro C<HvFILL>.
 
-Previously this value was stored in the HV structure, rather than being
-calculated on demand.
+As of perl 5.25 this function is used only for debugging
+purposes, and the number of used hash buckets is not
+in any way cached, thus this function can be costly
+to execute as it must iterate over all the buckets in the
+hash.
 
 =cut
 */
 
 STRLEN
-Perl_hv_fill(pTHX_ HV const *const hv)
+Perl_hv_fill(pTHX_ HV *const hv)
 {
     STRLEN count = 0;
     HE **ents = HvARRAY(hv);
 
     PERL_ARGS_ASSERT_HV_FILL;
 
+    /* No keys implies no buckets used.
+       One key can only possibly mean one bucket used.  */
+    if (HvTOTALKEYS(hv) < 2)
+        return HvTOTALKEYS(hv);
+
     if (ents) {
+        /* I wonder why we count down here...
+         * Is it some micro-optimisation?
+         * I would have thought counting up was better.
+         * - Yves
+         */
        HE *const *const last = ents + HvMAX(hv);
        count = last + 1 - ents;
 
@@ -1839,35 +2081,91 @@ Perl_hv_fill(pTHX_ HV const *const hv)
     return count;
 }
 
-static struct xpvhv_aux*
-S_hv_auxinit(HV *hv) {
-    struct xpvhv_aux *iter;
-    char *array;
-
-    PERL_ARGS_ASSERT_HV_AUXINIT;
+/* hash a pointer to a U32 - Used in the hash traversal randomization
+ * and bucket order randomization code
+ *
+ * this code was derived from Sereal, which was derived from autobox.
+ */
 
-    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);
-    iter = HvAUX(hv);
+PERL_STATIC_INLINE U32 S_ptr_hash(PTRV u) {
+#if PTRSIZE == 8
+    /*
+     * This is one of Thomas Wang's hash functions for 64-bit integers from:
+     * http://www.concentric.net/~Ttwang/tech/inthash.htm
+     */
+    u = (~u) + (u << 18);
+    u = u ^ (u >> 31);
+    u = u * 21;
+    u = u ^ (u >> 11);
+    u = u + (u << 6);
+    u = u ^ (u >> 22);
+#else
+    /*
+     * This is one of Bob Jenkins' hash functions for 32-bit integers
+     * from: http://burtleburtle.net/bob/hash/integer.html
+     */
+    u = (u + 0x7ed55d16) + (u << 12);
+    u = (u ^ 0xc761c23c) ^ (u >> 19);
+    u = (u + 0x165667b1) + (u << 5);
+    u = (u + 0xd3a2646c) ^ (u << 9);
+    u = (u + 0xfd7046c5) + (u << 3);
+    u = (u ^ 0xb55a4f09) ^ (u >> 16);
+#endif
+    return (U32)u;
+}
 
+static struct xpvhv_aux*
+S_hv_auxinit_internal(struct xpvhv_aux *iter) {
+    PERL_ARGS_ASSERT_HV_AUXINIT_INTERNAL;
     iter->xhv_riter = -1;      /* HvRITER(hv) = -1 */
     iter->xhv_eiter = NULL;    /* HvEITER(hv) = NULL */
+#ifdef PERL_HASH_RANDOMIZE_KEYS
+    iter->xhv_last_rand = iter->xhv_rand;
+#endif
     iter->xhv_name_u.xhvnameu_name = 0;
     iter->xhv_name_count = 0;
     iter->xhv_backreferences = 0;
     iter->xhv_mro_meta = NULL;
-    iter->xhv_super = NULL;
+    iter->xhv_aux_flags = 0;
     return iter;
 }
 
+
+static struct xpvhv_aux*
+S_hv_auxinit(pTHX_ HV *hv) {
+    struct xpvhv_aux *iter;
+    char *array;
+
+    PERL_ARGS_ASSERT_HV_AUXINIT;
+
+    if (!SvOOK(hv)) {
+        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);
+        iter = HvAUX(hv);
+#ifdef PERL_HASH_RANDOMIZE_KEYS
+        if (PL_HASH_RAND_BITS_ENABLED) {
+            /* mix in some new state to PL_hash_rand_bits to "randomize" the traversal order*/
+            if (PL_HASH_RAND_BITS_ENABLED == 1)
+                PL_hash_rand_bits += ptr_hash((PTRV)array);
+            PL_hash_rand_bits = ROTL_UV(PL_hash_rand_bits,1);
+        }
+        iter->xhv_rand = (U32)PL_hash_rand_bits;
+#endif
+    } else {
+        iter = HvAUX(hv);
+    }
+
+    return hv_auxinit_internal(iter);
+}
+
 /*
 =for apidoc hv_iterinit
 
@@ -1888,25 +2186,24 @@ Perl_hv_iterinit(pTHX_ HV *hv)
 {
     PERL_ARGS_ASSERT_HV_ITERINIT;
 
-    /* FIXME: Are we not NULL, or do we croak? Place bets now! */
-
-    if (!hv)
-       Perl_croak(aTHX_ "Bad hash");
-
     if (SvOOK(hv)) {
-       struct xpvhv_aux * const iter = HvAUX(hv);
+       struct xpvhv_aux * iter = HvAUX(hv);
        HE * const entry = iter->xhv_eiter; /* HvEITER(hv) */
        if (entry && HvLAZYDEL(hv)) {   /* was deleted earlier? */
            HvLAZYDEL_off(hv);
            hv_free_ent(hv, entry);
        }
+       iter = HvAUX(hv); /* may have been reallocated */
        iter->xhv_riter = -1;   /* HvRITER(hv) = -1 */
        iter->xhv_eiter = NULL; /* HvEITER(hv) = NULL */
+#ifdef PERL_HASH_RANDOMIZE_KEYS
+        iter->xhv_last_rand = iter->xhv_rand;
+#endif
     } else {
        hv_auxinit(hv);
     }
 
-    /* used to be xhv->xhv_fill before 5.004_65 */
+    /* note this includes placeholders! */
     return HvTOTALKEYS(hv);
 }
 
@@ -1916,9 +2213,6 @@ Perl_hv_riter_p(pTHX_ HV *hv) {
 
     PERL_ARGS_ASSERT_HV_RITER_P;
 
-    if (!hv)
-       Perl_croak(aTHX_ "Bad hash");
-
     iter = SvOOK(hv) ? HvAUX(hv) : hv_auxinit(hv);
     return &(iter->xhv_riter);
 }
@@ -1929,9 +2223,6 @@ Perl_hv_eiter_p(pTHX_ HV *hv) {
 
     PERL_ARGS_ASSERT_HV_EITER_P;
 
-    if (!hv)
-       Perl_croak(aTHX_ "Bad hash");
-
     iter = SvOOK(hv) ? HvAUX(hv) : hv_auxinit(hv);
     return &(iter->xhv_eiter);
 }
@@ -1942,9 +2233,6 @@ Perl_hv_riter_set(pTHX_ HV *hv, I32 riter) {
 
     PERL_ARGS_ASSERT_HV_RITER_SET;
 
-    if (!hv)
-       Perl_croak(aTHX_ "Bad hash");
-
     if (SvOOK(hv)) {
        iter = HvAUX(hv);
     } else {
@@ -1957,14 +2245,29 @@ Perl_hv_riter_set(pTHX_ HV *hv, I32 riter) {
 }
 
 void
+Perl_hv_rand_set(pTHX_ HV *hv, U32 new_xhv_rand) {
+    struct xpvhv_aux *iter;
+
+    PERL_ARGS_ASSERT_HV_RAND_SET;
+
+#ifdef PERL_HASH_RANDOMIZE_KEYS
+    if (SvOOK(hv)) {
+        iter = HvAUX(hv);
+    } else {
+        iter = hv_auxinit(hv);
+    }
+    iter->xhv_rand = new_xhv_rand;
+#else
+    Perl_croak(aTHX_ "This Perl has not been built with support for randomized hash key traversal but something called Perl_hv_rand_set().");
+#endif
+}
+
+void
 Perl_hv_eiter_set(pTHX_ HV *hv, HE *eiter) {
     struct xpvhv_aux *iter;
 
     PERL_ARGS_ASSERT_HV_EITER_SET;
 
-    if (!hv)
-       Perl_croak(aTHX_ "Bad hash");
-
     if (SvOOK(hv)) {
        iter = HvAUX(hv);
     } else {
@@ -2007,6 +2310,7 @@ Perl_hv_name_set(pTHX_ HV *hv, const char *name, U32 len, U32 flags)
                /* The first elem may be null. */
                if(*name) unshare_hek_or_pvn(*name, 0, 0, 0);
                Safefree(name);
+                iter = HvAUX(hv); /* may been realloced */
                spot = &iter->xhv_name_u.xhvnameu_name;
                iter->xhv_name_count = 0;
              }
@@ -2028,6 +2332,7 @@ Perl_hv_name_set(pTHX_ HV *hv, const char *name, U32 len, U32 flags)
            }
            else if (flags & HV_NAME_SETALL) {
                unshare_hek_or_pvn(iter->xhv_name_u.xhvnameu_name, 0, 0, 0);
+                iter = HvAUX(hv); /* may been realloced */
                spot = &iter->xhv_name_u.xhvnameu_name;
            }
            else {
@@ -2076,7 +2381,7 @@ hek_eq_pvn_flags(pTHX_ const HEK *hek, const char* pv, const I32 pvlen, const U3
 =for apidoc hv_ename_add
 
 Adds a name to a stash's internal list of effective names.  See
-C<hv_ename_delete>.
+C<L</hv_ename_delete>>.
 
 This is called when a stash is assigned to a new location in the symbol
 table.
@@ -2099,10 +2404,12 @@ Perl_hv_ename_add(pTHX_ HV *hv, const char *name, U32 len, U32 flags)
     PERL_HASH(hash, name, len);
 
     if (aux->xhv_name_count) {
-       HEK ** const xhv_name = aux->xhv_name_u.xhvnameu_names;
        I32 count = aux->xhv_name_count;
-       HEK **hekp = xhv_name + (count < 0 ? -count : count);
+       HEK ** const xhv_name = aux->xhv_name_u.xhvnameu_names + (count<0);
+       HEK **hekp = xhv_name + (count < 0 ? -count - 1 : count);
        while (hekp-- > xhv_name)
+       {
+           assert(*hekp);
            if (
                  (HEK_UTF8(*hekp) || (flags & SVf_UTF8)) 
                     ? hek_eq_pvn_flags(aTHX_ *hekp, name, (I32)len, flags)
@@ -2112,6 +2419,7 @@ Perl_hv_ename_add(pTHX_ HV *hv, const char *name, U32 len, U32 flags)
                    aux->xhv_name_count = -count;
                return;
            }
+       }
        if (count < 0) aux->xhv_name_count--, count = -count;
        else aux->xhv_name_count++;
        Renew(aux->xhv_name_u.xhvnameu_names, count + 1, HEK *);
@@ -2148,7 +2456,6 @@ This is called when a stash is deleted from the symbol table.
 void
 Perl_hv_ename_delete(pTHX_ HV *hv, const char *name, U32 len, U32 flags)
 {
-    dVAR;
     struct xpvhv_aux *aux;
 
     PERL_ARGS_ASSERT_HV_ENAME_DELETE;
@@ -2172,6 +2479,7 @@ Perl_hv_ename_delete(pTHX_ HV *hv, const char *name, U32 len, U32 flags)
                : (HEK_LEN(*victim) == (I32)len && memEQ(HEK_KEY(*victim), name, len))
            ) {
                unshare_hek_or_pvn(*victim, 0, 0, 0);
+                aux = HvAUX(hv); /* may been realloced */
                if (count < 0) ++aux->xhv_name_count;
                else --aux->xhv_name_count;
                if (
@@ -2190,9 +2498,10 @@ Perl_hv_ename_delete(pTHX_ HV *hv, const char *name, U32 len, U32 flags)
                return;
            }
        if (
-           count > 0 && (HEK_UTF8(*namep) || (flags & SVf_UTF8)) 
+           count > 0 && ((HEK_UTF8(*namep) || (flags & SVf_UTF8)) 
                 ? hek_eq_pvn_flags(aTHX_ *namep, name, (I32)len, flags)
                : (HEK_LEN(*namep) == (I32)len && memEQ(HEK_KEY(*namep), name, len))
+            )
        ) {
            aux->xhv_name_count = -count;
        }
@@ -2212,12 +2521,12 @@ Perl_hv_ename_delete(pTHX_ HV *hv, const char *name, U32 len, U32 flags)
 
 AV **
 Perl_hv_backreferences_p(pTHX_ HV *hv) {
-    struct xpvhv_aux * const iter = SvOOK(hv) ? HvAUX(hv) : hv_auxinit(hv);
-
     PERL_ARGS_ASSERT_HV_BACKREFERENCES_P;
-    PERL_UNUSED_CONTEXT;
-
-    return &(iter->xhv_backreferences);
+    /* See also Perl_sv_get_backrefs in sv.c where this logic is unrolled */
+    {
+        struct xpvhv_aux * const iter = SvOOK(hv) ? HvAUX(hv) : hv_auxinit(hv);
+        return &(iter->xhv_backreferences);
+    }
 }
 
 void
@@ -2235,7 +2544,7 @@ Perl_hv_kill_backrefs(pTHX_ HV *hv) {
        HvAUX(hv)->xhv_backreferences = 0;
        Perl_sv_kill_backrefs(aTHX_ MUTABLE_SV(hv), av);
        if (SvTYPE(av) == SVt_PVAV)
-           SvREFCNT_dec(av);
+           SvREFCNT_dec_NN(av);
     }
 }
 
@@ -2244,7 +2553,7 @@ hv_iternext is implemented as a macro in hv.h
 
 =for apidoc hv_iternext
 
-Returns entries from a hash iterator.  See C<hv_iterinit>.
+Returns entries from a hash iterator.  See C<L</hv_iterinit>>.
 
 You may call C<hv_delete> or C<hv_delete_ent> on the hash entry that the
 iterator currently points to, without losing your place or invalidating your
@@ -2256,10 +2565,11 @@ trigger the resource deallocation.
 
 =for apidoc hv_iternext_flags
 
-Returns entries from a hash iterator.  See C<hv_iterinit> and C<hv_iternext>.
-The C<flags> value will normally be zero; if HV_ITERNEXT_WANTPLACEHOLDERS is
+Returns entries from a hash iterator.  See C<L</hv_iterinit>> and
+C<L</hv_iternext>>.
+The C<flags> value will normally be zero; if C<HV_ITERNEXT_WANTPLACEHOLDERS> is
 set the placeholders keys (for restricted hashes) will be returned in addition
-to normal keys. By default placeholders are automatically skipped over.
+to normal keys.  By default placeholders are automatically skipped over.
 Currently a placeholder is implemented with a value that is
 C<&PL_sv_placeholder>.  Note that the implementation of placeholders and
 restricted hashes may change, and the implementation currently is
@@ -2280,9 +2590,6 @@ Perl_hv_iternext_flags(pTHX_ HV *hv, I32 flags)
 
     PERL_ARGS_ASSERT_HV_ITERNEXT_FLAGS;
 
-    if (!hv)
-       Perl_croak(aTHX_ "Bad hash");
-
     xhv = (XPVHV*)SvANY(hv);
 
     if (!SvOOK(hv)) {
@@ -2324,6 +2631,7 @@ Perl_hv_iternext_flags(pTHX_ HV *hv, I32 flags)
             SvREFCNT_dec(HeVAL(entry));
             Safefree(HeKEY_hek(entry));
             del_HE(entry);
+            iter = HvAUX(hv); /* may been realloced */
             iter->xhv_eiter = NULL; /* HvEITER(hv) = NULL */
            HvLAZYDEL_off(hv);
             return NULL;
@@ -2362,6 +2670,19 @@ Perl_hv_iternext_flags(pTHX_ HV *hv, I32 flags)
        }
     }
 
+#ifdef PERL_HASH_RANDOMIZE_KEYS
+    if (iter->xhv_last_rand != iter->xhv_rand) {
+        if (iter->xhv_riter != -1) {
+            Perl_ck_warner_d(aTHX_ packWARN(WARN_INTERNAL),
+                             "Use of each() on hash after insertion without resetting hash iterator results in undefined behavior"
+                             pTHX__FORMAT
+                             pTHX__VALUE);
+        }
+        iter = HvAUX(hv); /* may been realloced */
+        iter->xhv_last_rand = iter->xhv_rand;
+    }
+#endif
+
     /* Skip the entire loop if the hash is empty.   */
     if ((flags & HV_ITERNEXT_WANTPLACEHOLDERS)
        ? HvTOTALKEYS(hv) : HvUSEDKEYS(hv)) {
@@ -2372,9 +2693,12 @@ Perl_hv_iternext_flags(pTHX_ HV *hv, I32 flags)
            if (iter->xhv_riter > (I32)xhv->xhv_max /* HvRITER(hv) > HvMAX(hv) */) {
                /* There is no next one.  End of the hash.  */
                iter->xhv_riter = -1; /* HvRITER(hv) = -1 */
+#ifdef PERL_HASH_RANDOMIZE_KEYS
+                iter->xhv_last_rand = iter->xhv_rand; /* reset xhv_last_rand so we can detect inserts during traversal */
+#endif
                break;
            }
-           entry = (HvARRAY(hv))[iter->xhv_riter];
+            entry = (HvARRAY(hv))[ PERL_HASH_ITER_BUCKET(iter) & xhv->xhv_max ];
 
            if (!(flags & HV_ITERNEXT_WANTPLACEHOLDERS)) {
                /* If we have an entry, but it's a placeholder, don't count it.
@@ -2387,13 +2711,19 @@ Perl_hv_iternext_flags(pTHX_ HV *hv, I32 flags)
               or if we run through it and find only placeholders.  */
        }
     }
-    else iter->xhv_riter = -1;
+    else {
+        iter->xhv_riter = -1;
+#ifdef PERL_HASH_RANDOMIZE_KEYS
+        iter->xhv_last_rand = iter->xhv_rand;
+#endif
+    }
 
     if (oldentry && HvLAZYDEL(hv)) {           /* was deleted earlier? */
        HvLAZYDEL_off(hv);
        hv_free_ent(hv, oldentry);
     }
 
+    iter = HvAUX(hv); /* may been realloced */
     iter->xhv_eiter = entry; /* HvEITER(hv) = entry */
     return entry;
 }
@@ -2402,13 +2732,13 @@ Perl_hv_iternext_flags(pTHX_ HV *hv, I32 flags)
 =for apidoc hv_iterkey
 
 Returns the key from the current position of the hash iterator.  See
-C<hv_iterinit>.
+C<L</hv_iterinit>>.
 
 =cut
 */
 
 char *
-Perl_hv_iterkey(pTHX_ register HE *entry, I32 *retlen)
+Perl_hv_iterkey(pTHX_ HE *entry, I32 *retlen)
 {
     PERL_ARGS_ASSERT_HV_ITERKEY;
 
@@ -2430,13 +2760,13 @@ Perl_hv_iterkey(pTHX_ register HE *entry, I32 *retlen)
 
 Returns the key as an C<SV*> from the current position of the hash
 iterator.  The return value will always be a mortal copy of the key.  Also
-see C<hv_iterinit>.
+see C<L</hv_iterinit>>.
 
 =cut
 */
 
 SV *
-Perl_hv_iterkeysv(pTHX_ register HE *entry)
+Perl_hv_iterkeysv(pTHX_ HE *entry)
 {
     PERL_ARGS_ASSERT_HV_ITERKEYSV;
 
@@ -2447,13 +2777,13 @@ Perl_hv_iterkeysv(pTHX_ register HE *entry)
 =for apidoc hv_iterval
 
 Returns the value from the current position of the hash iterator.  See
-C<hv_iterkey>.
+C<L</hv_iterkey>>.
 
 =cut
 */
 
 SV *
-Perl_hv_iterval(pTHX_ HV *hv, register HE *entry)
+Perl_hv_iterval(pTHX_ HV *hv, HE *entry)
 {
     PERL_ARGS_ASSERT_HV_ITERVAL;
 
@@ -2498,7 +2828,7 @@ Now a macro in hv.h
 
 =for apidoc hv_magic
 
-Adds magic to a hash.  See C<sv_magic>.
+Adds magic to a hash.  See C<L</sv_magic>>.
 
 =cut
 */
@@ -2527,7 +2857,6 @@ Perl_unshare_hek(pTHX_ HEK *hek)
 STATIC void
 S_unshare_hek_or_pvn(pTHX_ const HEK *hek, const char *str, I32 len, U32 hash)
 {
-    dVAR;
     XPVHV* xhv;
     HE *entry;
     HE **oentry;
@@ -2616,7 +2945,7 @@ S_unshare_hek_or_pvn(pTHX_ const HEK *hek, const char *str, I32 len, U32 hash)
  * len and hash must both be valid for str.
  */
 HEK *
-Perl_share_hek(pTHX_ const char *str, I32 len, register U32 hash)
+Perl_share_hek(pTHX_ const char *str, I32 len, U32 hash)
 {
     bool is_utf8 = FALSE;
     int flags = 0;
@@ -2648,9 +2977,8 @@ 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)
+S_share_hek_flags(pTHX_ const char *str, I32 len, U32 hash, int flags)
 {
-    dVAR;
     HE *entry;
     const int flags_masked = flags & HVhek_MASK;
     const U32 hindex = hash & (I32) HvMAX(PL_strtab);
@@ -2718,8 +3046,9 @@ S_share_hek_flags(pTHX_ const char *str, I32 len, register U32 hash, int flags)
 
        xhv->xhv_keys++; /* HvTOTALKEYS(hv)++ */
        if (!next) {                    /* initial entry? */
-       } else if (xhv->xhv_keys > xhv->xhv_max /* HvUSEDKEYS(hv) > HvMAX(hv) */) {
-               hsplit(PL_strtab);
+       } else if ( DO_HSPLIT(xhv) ) {
+            const STRLEN oldsize = xhv->xhv_max + 1;
+            hsplit(PL_strtab, oldsize, oldsize * 2);
        }
     }
 
@@ -2731,10 +3060,9 @@ S_share_hek_flags(pTHX_ const char *str, I32 len, register U32 hash, int flags)
     return HeKEY_hek(entry);
 }
 
-I32 *
+SSize_t *
 Perl_hv_placeholders_p(pTHX_ HV *hv)
 {
-    dVAR;
     MAGIC *mg = mg_find((const SV *)hv, PERL_MAGIC_rhash);
 
     PERL_ARGS_ASSERT_HV_PLACEHOLDERS_P;
@@ -2753,10 +3081,10 @@ Perl_hv_placeholders_p(pTHX_ HV *hv)
 I32
 Perl_hv_placeholders_get(pTHX_ const HV *hv)
 {
-    dVAR;
     MAGIC * const mg = mg_find((const SV *)hv, PERL_MAGIC_rhash);
 
     PERL_ARGS_ASSERT_HV_PLACEHOLDERS_GET;
+    PERL_UNUSED_CONTEXT;
 
     return mg ? mg->mg_len : 0;
 }
@@ -2764,7 +3092,6 @@ Perl_hv_placeholders_get(pTHX_ const HV *hv)
 void
 Perl_hv_placeholders_set(pTHX_ HV *hv, I32 ph)
 {
-    dVAR;
     MAGIC * const mg = mg_find((const SV *)hv, PERL_MAGIC_rhash);
 
     PERL_ARGS_ASSERT_HV_PLACEHOLDERS_SET;
@@ -2825,7 +3152,7 @@ S_refcounted_he_value(pTHX_ const struct refcounted_he *he)
 
 Generates and returns a C<HV *> representing the content of a
 C<refcounted_he> chain.
-I<flags> is currently unused and must be zero.
+C<flags> is currently unused and must be zero.
 
 =cut
 */
@@ -2932,9 +3259,9 @@ Perl_refcounted_he_chain_2hv(pTHX_ const struct refcounted_he *chain, U32 flags)
 =for apidoc m|SV *|refcounted_he_fetch_pvn|const struct refcounted_he *chain|const char *keypv|STRLEN keylen|U32 hash|U32 flags
 
 Search along a C<refcounted_he> chain for an entry with the key specified
-by I<keypv> and I<keylen>.  If I<flags> has the C<REFCOUNTED_HE_KEY_UTF8>
+by C<keypv> and C<keylen>.  If C<flags> has the C<REFCOUNTED_HE_KEY_UTF8>
 bit set, the key octets are interpreted as UTF-8, otherwise they
-are interpreted as Latin-1.  I<hash> is a precomputed hash of the key
+are interpreted as Latin-1.  C<hash> is a precomputed hash of the key
 string, or zero if it has not been precomputed.  Returns a mortal scalar
 representing the value associated with the key, or C<&PL_sv_placeholder>
 if there is no value associated with the key.
@@ -2954,18 +3281,18 @@ Perl_refcounted_he_fetch_pvn(pTHX_ const struct refcounted_he *chain,
        Perl_croak(aTHX_ "panic: refcounted_he_fetch_pvn bad flags %"UVxf,
            (UV)flags);
     if (!chain)
-       return &PL_sv_placeholder;
+       goto ret;
     if (flags & REFCOUNTED_HE_KEY_UTF8) {
        /* For searching purposes, canonicalise to Latin-1 where possible. */
        const char *keyend = keypv + keylen, *p;
        STRLEN nonascii_count = 0;
        for (p = keypv; p != keyend; p++) {
-           U8 c = (U8)*p;
-           if (c & 0x80) {
-               if (!((c & 0xfe) == 0xc2 && ++p != keyend &&
-                           (((U8)*p) & 0xc0) == 0x80))
+           if (! UTF8_IS_INVARIANT(*p)) {
+               if (! UTF8_IS_NEXT_CHAR_DOWNGRADEABLE(p, keyend)) {
                    goto canonicalised_key;
+                }
                nonascii_count++;
+                p++;
            }
        }
        if (nonascii_count) {
@@ -2977,8 +3304,13 @@ Perl_refcounted_he_fetch_pvn(pTHX_ const struct refcounted_he *chain,
            keypv = q;
            for (; p != keyend; p++, q++) {
                U8 c = (U8)*p;
-               *q = (char)
-                   ((c & 0x80) ? ((c & 0x03) << 6) | (((U8)*++p) & 0x3f) : c);
+                if (UTF8_IS_INVARIANT(c)) {
+                    *q = (char) c;
+                }
+                else {
+                    p++;
+                    *q = (char) EIGHT_BIT_UTF8_TO_NATIVE(c, *p);
+                }
            }
        }
        flags &= ~REFCOUNTED_HE_KEY_UTF8;
@@ -3009,6 +3341,7 @@ Perl_refcounted_he_fetch_pvn(pTHX_ const struct refcounted_he *chain,
            return sv_2mortal(refcounted_he_value(chain));
        }
     }
+  ret:
     return flags & REFCOUNTED_HE_EXISTS ? NULL : &PL_sv_placeholder;
 }
 
@@ -3065,25 +3398,25 @@ be empty), and thus forms a longer chain.  When using the longer chain,
 the new key/value pair takes precedence over any entry for the same key
 further along the chain.
 
-The new key is specified by I<keypv> and I<keylen>.  If I<flags> has
+The new key is specified by C<keypv> and C<keylen>.  If C<flags> has
 the C<REFCOUNTED_HE_KEY_UTF8> bit set, the key octets are interpreted
-as UTF-8, otherwise they are interpreted as Latin-1.  I<hash> is
+as UTF-8, otherwise they are interpreted as Latin-1.  C<hash> is
 a precomputed hash of the key string, or zero if it has not been
 precomputed.
 
-I<value> is the scalar value to store for this key.  I<value> is copied
+C<value> is the scalar value to store for this key.  C<value> is copied
 by this function, which thus does not take ownership of any reference
 to it, and later changes to the scalar will not be reflected in the
 value visible in the C<refcounted_he>.  Complex types of scalar will not
 be stored with referential integrity, but will be coerced to strings.
-I<value> may be either null or C<&PL_sv_placeholder> to indicate that no
+C<value> may be either null or C<&PL_sv_placeholder> to indicate that no
 value is to be associated with the key; this, as with any non-null value,
 takes precedence over the existence of a value for the key further along
 the chain.
 
-I<parent> points to the rest of the C<refcounted_he> chain to be
+C<parent> points to the rest of the C<refcounted_he> chain to be
 attached to the new C<refcounted_he>.  This function takes ownership
-of one reference to I<parent>, and returns one reference to the new
+of one reference to C<parent>, and returns one reference to the new
 C<refcounted_he>.
 
 =cut
@@ -3130,12 +3463,12 @@ Perl_refcounted_he_new_pvn(pTHX_ struct refcounted_he *parent,
        const char *keyend = keypv + keylen, *p;
        STRLEN nonascii_count = 0;
        for (p = keypv; p != keyend; p++) {
-           U8 c = (U8)*p;
-           if (c & 0x80) {
-               if (!((c & 0xfe) == 0xc2 && ++p != keyend &&
-                           (((U8)*p) & 0xc0) == 0x80))
+           if (! UTF8_IS_INVARIANT(*p)) {
+               if (! UTF8_IS_NEXT_CHAR_DOWNGRADEABLE(p, keyend)) {
                    goto canonicalised_key;
+                }
                nonascii_count++;
+                p++;
            }
        }
        if (nonascii_count) {
@@ -3147,8 +3480,13 @@ Perl_refcounted_he_new_pvn(pTHX_ struct refcounted_he *parent,
            keypv = q;
            for (; p != keyend; p++, q++) {
                U8 c = (U8)*p;
-               *q = (char)
-                   ((c & 0x80) ? ((c & 0x03) << 6) | (((U8)*++p) & 0x3f) : c);
+                if (UTF8_IS_INVARIANT(c)) {
+                    *q = (char) c;
+                }
+                else {
+                    p++;
+                    *q = (char) EIGHT_BIT_UTF8_TO_NATIVE(c, *p);
+                }
            }
        }
        flags &= ~REFCOUNTED_HE_KEY_UTF8;
@@ -3253,7 +3591,9 @@ no action occurs in this case.
 
 void
 Perl_refcounted_he_free(pTHX_ struct refcounted_he *he) {
+#ifdef USE_ITHREADS
     dVAR;
+#endif
     PERL_UNUSED_CONTEXT;
 
     while (he) {
@@ -3290,7 +3630,10 @@ to this function: no action occurs and a null pointer is returned.
 struct refcounted_he *
 Perl_refcounted_he_inc(pTHX_ struct refcounted_he *he)
 {
+#ifdef USE_ITHREADS
     dVAR;
+#endif
+    PERL_UNUSED_CONTEXT;
     if (he) {
        HINTS_REFCNT_LOCK;
        he->refcounted_he_refcnt++;
@@ -3315,6 +3658,7 @@ Perl_cop_fetch_label(pTHX_ COP *const cop, STRLEN *len, U32 *flags) {
     struct refcounted_he *const chain = cop->cop_hints_hash;
 
     PERL_ARGS_ASSERT_COP_FETCH_LABEL;
+    PERL_UNUSED_CONTEXT;
 
     if (!chain)
        return NULL;
@@ -3347,8 +3691,9 @@ Perl_cop_fetch_label(pTHX_ COP *const cop, STRLEN *len, U32 *flags) {
 /*
 =for apidoc cop_store_label
 
-Save a label into a C<cop_hints_hash>. You need to set flags to C<SVf_UTF8>
-for a utf-8 label.
+Save a label into a C<cop_hints_hash>.
+You need to set flags to C<SVf_UTF8>
+for a UTF-8 label.
 
 =cut
 */
@@ -3446,11 +3791,5 @@ Perl_hv_assert(pTHX_ HV *hv)
 #endif
 
 /*
- * Local variables:
- * c-indentation-style: bsd
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- *
  * ex: set ts=8 sts=4 sw=4 et:
  */