change #22071 (taint bug in $^0) introduced a potential double
[p5sagit/p5-mst-13.2.git] / hv.c
diff --git a/hv.c b/hv.c
index 3606911..b7415ec 100644 (file)
--- a/hv.c
+++ b/hv.c
@@ -80,6 +80,7 @@ S_more_he(pTHX)
 STATIC HEK *
 S_save_hek_flags(pTHX_ const char *str, I32 len, U32 hash, int flags)
 {
+    int flags_masked = flags & HVhek_MASK;
     char *k;
     register HEK *hek;
 
@@ -89,7 +90,10 @@ S_save_hek_flags(pTHX_ const char *str, I32 len, U32 hash, int flags)
     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;
 }
 
@@ -168,6 +172,126 @@ S_hv_notallowed(pTHX_ int flags, const char *key, I32 klen,
 /* (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, 0);
+    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_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,11 +306,6 @@ information on how to use this function on tied hashes.
 =cut
 */
 
-#define HV_FETCH_ISSTORE   0x01
-#define HV_FETCH_ISEXISTS  0x02
-#define HV_FETCH_LVALUE    0x04
-#define HV_FETCH_JUST_SV   0x08
-
 SV**
 Perl_hv_fetch(pTHX_ HV *hv, const char *key, I32 klen_i32, I32 lval)
 {
@@ -202,10 +321,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 */
 /*
@@ -229,15 +366,17 @@ 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);
+                          (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;
+    XPVHV* xhv;
+    U32 n_links;
+    HE *entry;
+    HE **oentry;
     SV *sv;
     bool is_utf8;
     int masked_flags;
@@ -246,6 +385,8 @@ S_hv_fetch_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
        return 0;
 
     if (keysv) {
+       if (flags & HVhek_FREEKEY)
+           Safefree(key);
        key = SvPV(keysv, klen);
        flags = 0;
        is_utf8 = (SvUTF8(keysv) != 0);
@@ -253,6 +394,7 @@ S_hv_fetch_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
        is_utf8 = ((flags & HVhek_UTF8) ? TRUE : FALSE);
     }
 
+    xhv = (XPVHV*)SvANY(hv);
     if (SvMAGICAL(hv)) {
        if (SvRMAGICAL(hv) && !(action & (HV_FETCH_ISSTORE|HV_FETCH_ISEXISTS)))
          {
@@ -301,16 +443,27 @@ S_hv_fetch_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
                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? */
-                       if (flags & HVhek_FREEKEY)
-                           Safefree(key);
-
+                       /* Would be nice if we had a routine to do the
+                          copy and upercase in a single pass through.  */
+                       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;
                    }
            }
@@ -319,6 +472,10 @@ S_hv_fetch_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
        else if (SvRMAGICAL(hv) && (action & HV_FETCH_ISEXISTS)) {
            if (mg_find((SV*)hv, PERL_MAGIC_tied) || SvGMAGICAL((SV*)hv)) {
                SV* svret;
+               /* I don't understand why hv_exists_ent has svret and sv,
+                  whereas hv_exists only had one.  */
+               svret = sv_newmortal();
+               sv = sv_newmortal();
 
                if (keysv || is_utf8) {
                    if (!keysv) {
@@ -327,15 +484,12 @@ S_hv_fetch_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
                    } else {
                        keysv = newSVsv(keysv);
                    }
-                   key = (char *)sv_2mortal(keysv);
-                   klen = HEf_SVKEY;
+                   mg_copy((SV*)hv, sv, (char *)sv_2mortal(keysv), HEf_SVKEY);
+               } else {
+                   mg_copy((SV*)hv, sv, key, klen);
                }
-
-               /* 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);
+               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.
@@ -345,18 +499,67 @@ S_hv_fetch_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
 #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));
-               key = strupr(SvPVX(keysv));
+               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;
+
+               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) {
+               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)) {
+                   /* 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;
+
+                   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 ((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
@@ -395,7 +598,9 @@ S_hv_fetch_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
 
     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;
@@ -408,14 +613,17 @@ S_hv_fetch_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
     }
 
     masked_flags = (flags & HVhek_MASK);
+    n_links = 0;
 
 #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)) {
+    {
+       /* entry = (HvARRAY(hv))[hash & (I32) HvMAX(hv)]; */
+       entry = ((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max];
+    }
+    for (; entry; ++n_links, entry = HeNEXT(entry)) {
        if (HeHASH(entry) != hash)              /* strings can't be equal */
            continue;
        if (HeKLEN(entry) != (I32)klen)
@@ -424,46 +632,74 @@ 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)
+
+        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
+                   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);
+                   xhv->xhv_placeholders--;
+               } else {
+                   /* store */
+                   if (val != &PL_sv_placeholder)
+                       xhv->xhv_placeholders--;
+               }
+               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 (SvRMAGICAL((SV*)hv) && mg_find((SV*)hv, PERL_MAGIC_env)) {
+    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);
        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
@@ -473,286 +709,38 @@ S_hv_fetch_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
                        "access disallowed key '%"SVf"' in"
                        );
     }
-    if (action & HV_FETCH_LVALUE) {
-       /* XXX remove once common API complete  */
-       if (!keysv) {
-           keysv = sv_2mortal(newSVpvn(key,klen));
-       }
-    }
-
-    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);
-    }
-    return 0;
-}
-
-STATIC void
-S_hv_magic_check(pTHX_ HV *hv, bool *needs_copy, bool *needs_store)
-{
-    MAGIC *mg = SvMAGIC(hv);
-    *needs_copy = FALSE;
-    *needs_store = TRUE;
-    while (mg) {
-       if (isUPPER(mg->mg_type)) {
-           *needs_copy = TRUE;
-           switch (mg->mg_type) {
-           case PERL_MAGIC_tied:
-           case PERL_MAGIC_sig:
-               *needs_store = FALSE;
-           }
-       }
-       mg = mg->mg_moremagic;
-    }
-}
-
-/*
-=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_store_common (hv, NULL, key, klen, flags, val, 0);
-    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.
-
-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_store_common(hv, keysv, NULL, 0, 0, val, hash);
-}
-
-HE *
-S_hv_store_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
-                 int flags, SV *val, U32 hash)
-{
-    XPVHV* xhv;
-    U32 n_links;
-    HE *entry;
-    HE **oentry;
-    bool is_utf8;
-    int masked_flags;
-
-    if (!hv)
+    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 (keysv) {
-       key = SvPV(keysv, klen);
-       flags = 0;
-       is_utf8 = (SvUTF8(keysv) != 0);
-    } else {
-       is_utf8 = ((flags & HVhek_UTF8) ? TRUE : FALSE);
     }
-
-    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)) {
-               const char *keysave = key;
-               key = savepvn(key,klen);
-               key = (const char*)strupr((char*)key);
-               hash = 0;
-
-                if (flags & HVhek_FREEKEY) {
-                    Safefree(keysave);
-                   flags &= ~HVhek_FREEKEY;
-               }
-           }
-#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) {
-       const char *keysave = 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 (flags & HVhek_FREEKEY) {
-               /* This shouldn't happen if our caller does what we expect,
-                  but strictly the API allows it.  */
-               Safefree(keysave);
-           }
-            flags |= HVhek_WASUTF8 | HVhek_FREEKEY;
+    if (action & HV_FETCH_LVALUE) {
+       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.  */
        }
-        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);
-        }
     }
 
-    masked_flags = (flags & HVhek_MASK);
+    /* Welcome to hv_store...  */
 
-    if (!xhv->xhv_array /* !HvARRAY(hv) */)
-       Newz(505, xhv->xhv_array /* HvARRAY(hv) */,
+    if (!xhv->xhv_array) {
+       /* 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.  */
+       Newz(503, 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) ^ masked_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) != 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 (flags & HVhek_FREEKEY)
-           Safefree(key);
-       return entry;
     }
 
-    if (SvREADONLY(hv)) {
-       S_hv_notallowed(aTHX_ flags, key, klen,
-                       "access disallowed key '%"SVf"' to"
-                       );
-    }
+    oentry = &((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max];
 
     entry = new_HE();
     /* share_hek_flags will do the free for us.  This might be considered
@@ -767,6 +755,8 @@ S_hv_store_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
 
     if (val == &PL_sv_placeholder)
        xhv->xhv_placeholders++;
+    if (masked_flags & HVhek_ENABLEHVKFLAGS)
+       HvHASKFLAGS_on(hv);
 
     xhv->xhv_keys++; /* HvKEYS(hv)++ */
     if (!n_links) {                            /* initial entry? */
@@ -785,6 +775,54 @@ S_hv_store_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
     return entry;
 }
 
+STATIC void
+S_hv_magic_check(pTHX_ HV *hv, bool *needs_copy, bool *needs_store)
+{
+    MAGIC *mg = SvMAGIC(hv);
+    *needs_copy = FALSE;
+    *needs_store = TRUE;
+    while (mg) {
+       if (isUPPER(mg->mg_type)) {
+           *needs_copy = TRUE;
+           switch (mg->mg_type) {
+           case PERL_MAGIC_tied:
+           case PERL_MAGIC_sig:
+               *needs_store = FALSE;
+           }
+       }
+       mg = mg->mg_moremagic;
+    }
+}
+
+/*
+=for apidoc hv_scalar
+
+Evaluates the hash in scalar context and returns the result. Handles magic when the hash is tied.
+
+=cut
+*/
+
+SV *
+Perl_hv_scalar(pTHX_ HV *hv)
+{
+    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;
+}
+
 /*
 =for apidoc hv_delete
 
@@ -828,7 +866,7 @@ 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)
 {
@@ -844,6 +882,8 @@ S_hv_delete_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
        return Nullsv;
 
     if (keysv) {
+       if (k_flags & HVhek_FREEKEY)
+           Safefree(key);
        key = SvPV(keysv, klen);
        k_flags = 0;
        is_utf8 = (SvUTF8(keysv) != 0);
@@ -859,7 +899,7 @@ S_hv_delete_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
        if (needs_copy) {
            entry = hv_fetch_common(hv, keysv, key, klen,
                                    k_flags & ~HVhek_FREEKEY, HV_FETCH_LVALUE,
-                                   hash);
+                                   Nullsv, hash);
            sv = entry ? HeVAL(entry) : NULL;
            if (sv) {
                if (SvMAGICAL(sv)) {
@@ -873,22 +913,20 @@ S_hv_delete_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
                    }           
                    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));
-               key = strupr(SvPVX(keysv));
-
-                if (k_flags & HVhek_FREEKEY) {
-                    Safefree(keysave);
+               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;
                }
-
-               is_utf8 = 0;
-               k_flags = 0;
-               hash = 0;
-           }
 #endif
+           }
        }
     }
     xhv = (XPVHV*)SvANY(hv);
@@ -922,7 +960,6 @@ S_hv_delete_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
         } else {
             PERL_HASH(hash, key, klen);
         }
-       PERL_HASH(hash, key, klen);
     }
 
     masked_flags = (k_flags & HVhek_MASK);
@@ -1012,49 +1049,6 @@ S_hv_delete_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
     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_fetch_common(hv, NULL, key, klen, flags, HV_FETCH_ISEXISTS, 0)
-       ? TRUE : FALSE;
-}
-
-/*
-=for apidoc hv_exists_ent
-
-Returns a boolean indicating whether the specified hash key exists. C<hash>
-can be a valid precomputed hash value, or 0 to ask for it to be
-computed.
-
-=cut
-*/
-
-bool
-Perl_hv_exists_ent(pTHX_ HV *hv, SV *keysv, U32 hash)
-{
-    return hv_fetch_common(hv, keysv, NULL, 0, 0, HV_FETCH_ISEXISTS, hash)
-       ? TRUE : FALSE;
-}
-
 STATIC void
 S_hsplit(pTHX_ HV *hv)
 {
@@ -1466,7 +1460,7 @@ Perl_hv_clear(pTHX_ HV *hv)
                }
            }
        }
-       return;
+       goto reset;
     }
 
     hfreeentries(hv);
@@ -1480,6 +1474,8 @@ Perl_hv_clear(pTHX_ HV *hv)
 
     HvHASKFLAGS_off(hv);
     HvREHASH_off(hv);
+    reset:
+    HvEITER(hv) = NULL;
 }
 
 /*
@@ -2090,7 +2086,7 @@ S_share_hek_flags(pTHX_ const char *str, I32 len, register U32 hash, int flags)
     }
     if (!found) {
        entry = new_HE();
-       HeKEY_hek(entry) = save_hek_flags(str, len, hash, flags);
+       HeKEY_hek(entry) = save_hek_flags(str, len, hash, flags_masked);
        HeVAL(entry) = Nullsv;
        HeNEXT(entry) = *oentry;
        *oentry = entry;