X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=hv.c;h=d1835b2bbf9a28857d4e0cfe9ecceace605315dc;hb=1de063289cf096bd67e3d9d1b4a6dca2498966fa;hp=485e2206e422ea0ad919d806b430894895a62bc4;hpb=bc92a800b506a714f5af60e9261c914eb78da89b;p=p5sagit%2Fp5-mst-13.2.git diff --git a/hv.c b/hv.c index 485e220..d1835b2 100644 --- a/hv.c +++ b/hv.c @@ -1,7 +1,7 @@ /* hv.c * * Copyright (C) 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, - * 2000, 2001, 2002, 2003, by Larry Wall and others + * 2000, 2001, 2002, 2003, 2004, 2005, 2006, by Larry Wall and others * * You may distribute under the terms of either the GNU General Public * License or the Artistic License, as specified in the README file. @@ -14,49 +14,39 @@ /* =head1 Hash Manipulation Functions + +A HV structure represents a Perl hash. It consists mainly of an array +of pointers, each of which points to a linked list of HE structures. The +array is indexed by the hash function of the key, so each linked list +represents all the hash entries with the same hash value. Each HE contains +a pointer to the actual value, plus a pointer to a HEK structure which +holds the key and hash value. + +=cut + */ #include "EXTERN.h" #define PERL_IN_HV_C +#define PERL_HASH_INTERNAL_ACCESS #include "perl.h" -#define HV_MAX_LENGTH_BEFORE_SPLIT 4 - -STATIC HE* -S_new_he(pTHX) -{ - HE* he; - LOCK_SV_MUTEX; - if (!PL_he_root) - more_he(); - he = PL_he_root; - PL_he_root = HeNEXT(he); - UNLOCK_SV_MUTEX; - return he; -} +#define HV_MAX_LENGTH_BEFORE_SPLIT 14 -STATIC void -S_del_he(pTHX_ HE *p) -{ - LOCK_SV_MUTEX; - HeNEXT(p) = (HE*)PL_he_root; - PL_he_root = p; - UNLOCK_SV_MUTEX; -} +static const char S_strtab_error[] + = "Cannot modify shared string table in hv_%s"; STATIC void S_more_he(pTHX) { - register HE* he; - register HE* heend; - XPV *ptr; - New(54, ptr, 1008/sizeof(XPV), XPV); - ptr->xpv_pv = (char*)PL_he_arenaroot; - PL_he_arenaroot = ptr; - - he = (HE*)ptr; - heend = &he[1008 / sizeof(HE) - 1]; - PL_he_root = ++he; + dVAR; + HE* he; + HE* heend; + + he = (HE*) Perl_get_arena(aTHX_ PERL_ARENA_SIZE); + + heend = &he[PERL_ARENA_SIZE / sizeof(HE) - 1]; + PL_body_roots[HE_SVSLOT] = he; while (he < heend) { HeNEXT(he) = (HE*)(he + 1); he++; @@ -71,52 +61,101 @@ S_more_he(pTHX) #else +STATIC HE* +S_new_he(pTHX) +{ + dVAR; + HE* he; + void ** const root = &PL_body_roots[HE_SVSLOT]; + + LOCK_SV_MUTEX; + if (!*root) + S_more_he(aTHX); + he = (HE*) *root; + assert(he); + *root = HeNEXT(he); + UNLOCK_SV_MUTEX; + return he; +} + #define new_HE() new_he() -#define del_HE(p) del_he(p) +#define del_HE(p) \ + STMT_START { \ + LOCK_SV_MUTEX; \ + HeNEXT(p) = (HE*)(PL_body_roots[HE_SVSLOT]); \ + PL_body_roots[HE_SVSLOT] = p; \ + UNLOCK_SV_MUTEX; \ + } STMT_END + + #endif STATIC HEK * -S_save_hek_flags(pTHX_ const char *str, I32 len, U32 hash, int flags) +S_save_hek_flags(const char *str, I32 len, U32 hash, int flags) { + const int flags_masked = flags & HVhek_MASK; char *k; register HEK *hek; - New(54, k, HEK_BASESIZE + len + 2, char); + Newx(k, HEK_BASESIZE + len + 2, char); hek = (HEK*)k; Copy(str, HEK_KEY(hek), len, char); HEK_KEY(hek)[len] = 0; HEK_LEN(hek) = len; HEK_HASH(hek) = hash; - HEK_FLAGS(hek) = (unsigned char)flags; + HEK_FLAGS(hek) = (unsigned char)flags_masked | HVhek_UNSHARED; + + if (flags & HVhek_FREEKEY) + Safefree(str); return hek; } -/* free the pool of temporary HE/HEK pairs retunrned by hv_fetch_ent +/* free the pool of temporary HE/HEK pairs returned by hv_fetch_ent * for tied hashes */ void Perl_free_tied_hv_pool(pTHX) { - HE *ohe; + dVAR; HE *he = PL_hv_fetch_ent_mh; while (he) { + HE * const ohe = he; Safefree(HeKEY_hek(he)); - ohe = he; he = HeNEXT(he); del_HE(ohe); } - PL_hv_fetch_ent_mh = Nullhe; + PL_hv_fetch_ent_mh = NULL; } #if defined(USE_ITHREADS) +HEK * +Perl_hek_dup(pTHX_ HEK *source, CLONE_PARAMS* param) +{ + HEK *shared = (HEK*)ptr_table_fetch(PL_ptr_table, source); + + PERL_UNUSED_ARG(param); + + if (shared) { + /* We already shared this hash key. */ + (void)share_hek_hek(shared); + } + else { + shared + = share_hek_flags(HEK_KEY(source), HEK_LEN(source), + HEK_HASH(source), HEK_FLAGS(source)); + ptr_table_store(PL_ptr_table, source, shared); + } + return shared; +} + HE * -Perl_he_dup(pTHX_ HE *e, bool shared, CLONE_PARAMS* param) +Perl_he_dup(pTHX_ const HE *e, bool shared, CLONE_PARAMS* param) { HE *ret; if (!e) - return Nullhe; + return NULL; /* look for it in the table first */ ret = (HE*)ptr_table_fetch(PL_ptr_table, e); if (ret) @@ -129,13 +168,28 @@ Perl_he_dup(pTHX_ HE *e, bool shared, CLONE_PARAMS* param) HeNEXT(ret) = he_dup(HeNEXT(e),shared, param); if (HeKLEN(e) == HEf_SVKEY) { char *k; - New(54, k, HEK_BASESIZE + sizeof(SV*), char); + Newx(k, HEK_BASESIZE + sizeof(SV*), char); HeKEY_hek(ret) = (HEK*)k; HeKEY_sv(ret) = SvREFCNT_inc(sv_dup(HeKEY_sv(e), param)); } - else if (shared) - HeKEY_hek(ret) = share_hek_flags(HeKEY(e), HeKLEN(e), HeHASH(e), - HeKFLAGS(e)); + else if (shared) { + /* This is hek_dup inlined, which seems to be important for speed + reasons. */ + HEK * const source = HeKEY_hek(e); + HEK *shared = (HEK*)ptr_table_fetch(PL_ptr_table, source); + + if (shared) { + /* We already shared this hash key. */ + (void)share_hek_hek(shared); + } + else { + shared + = share_hek_flags(HEK_KEY(source), HEK_LEN(source), + HEK_HASH(source), HEK_FLAGS(source)); + ptr_table_store(PL_ptr_table, source, shared); + } + HeKEY_hek(ret) = shared; + } else HeKEY_hek(ret) = save_hek_flags(HeKEY(e), HeKLEN(e), HeHASH(e), HeKFLAGS(e)); @@ -148,7 +202,7 @@ static void S_hv_notallowed(pTHX_ int flags, const char *key, I32 klen, const char *msg) { - SV *sv = sv_newmortal(), *esv = sv_newmortal(); + SV * const sv = sv_newmortal(); if (!(flags & HVhek_FREEKEY)) { sv_setpvn(sv, key, klen); } @@ -160,20 +214,36 @@ S_hv_notallowed(pTHX_ int flags, const char *key, I32 klen, if (flags & HVhek_UTF8) { SvUTF8_on(sv); } - Perl_sv_setpvf(aTHX_ esv, "Attempt to %s a restricted hash", msg); - Perl_croak(aTHX_ SvPVX(esv), sv); + Perl_croak(aTHX_ msg, sv); } /* (klen == HEf_SVKEY) is special for MAGICAL hv entries, meaning key slot * contains an SV* */ +#define HV_FETCH_ISSTORE 0x01 +#define HV_FETCH_ISEXISTS 0x02 +#define HV_FETCH_LVALUE 0x04 +#define HV_FETCH_JUST_SV 0x08 + /* -=for apidoc hv_fetch +=for apidoc hv_store -Returns the SV which corresponds to the specified key in the hash. The -C is the length of the key. If C is set then the fetch will be -part of a store. Check that the return value is non-null before -dereferencing it to an C. +Stores an SV in a hash. The hash key is specified as C and C is +the length of the key. The C 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. Note that the caller is +responsible for suitably incrementing the reference count of C before +the call, and decrementing it if the function returned NULL. Effectively +a successful hv_store takes ownership of one reference to C. 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 for more information on how to use this function on tied hashes. @@ -181,176 +251,147 @@ information on how to use this function on tied hashes. =cut */ - SV** -Perl_hv_fetch(pTHX_ HV *hv, const char *key, I32 klen, I32 lval) +Perl_hv_store(pTHX_ HV *hv, const char *key, I32 klen_i32, SV *val, U32 hash) { - bool is_utf8 = FALSE; - const char *keysave = key; - int flags = 0; - - if (klen < 0) { - klen = -klen; - is_utf8 = TRUE; - } + HE *hek; + STRLEN klen; + int flags; - if (is_utf8) { - STRLEN tmplen = klen; - /* Just casting the &klen to (STRLEN) won't work well - * if STRLEN and I32 are of different widths. --jhi */ - key = (char*)bytes_from_utf8((U8*)key, &tmplen, &is_utf8); - klen = tmplen; - /* If we were able to downgrade here, then than means that we were - passed in a key which only had chars 0-255, but was utf8 encoded. */ - if (is_utf8) - flags = HVhek_UTF8; - /* If we found we were able to downgrade the string to bytes, then - we should flag that it needs upgrading on keys or each. */ - if (key != keysave) - flags |= HVhek_WASUTF8 | HVhek_FREEKEY; + if (klen_i32 < 0) { + klen = -klen_i32; + flags = HVhek_UTF8; + } else { + klen = klen_i32; + flags = 0; } + hek = hv_fetch_common (hv, NULL, key, klen, flags, + (HV_FETCH_ISSTORE|HV_FETCH_JUST_SV), val, hash); + return hek ? &HeVAL(hek) : NULL; +} - return hv_fetch_flags (hv, key, klen, lval, flags); +/* XXX This looks like an ideal candidate to inline */ +SV** +Perl_hv_store_flags(pTHX_ HV *hv, const char *key, I32 klen, SV *val, + register U32 hash, int flags) +{ + HE * const hek = hv_fetch_common (hv, NULL, key, klen, flags, + (HV_FETCH_ISSTORE|HV_FETCH_JUST_SV), val, hash); + return hek ? &HeVAL(hek) : NULL; } -STATIC SV** -S_hv_fetch_flags(pTHX_ HV *hv, const char *key, I32 klen, I32 lval, int flags) +/* +=for apidoc hv_store_ent + +Stores C in a hash. The hash key is specified as C. The C +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 macros +described here. Note that the caller is responsible for suitably +incrementing the reference count of C before the call, and +decrementing it if the function returned NULL. Effectively a successful +hv_store_ent takes ownership of one reference to C. 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; +unlike C it does not take ownership of it, so maintaining the correct +reference count on C 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 for more +information on how to use this function on tied hashes. + +=cut +*/ + +/* XXX This looks like an ideal candidate to inline */ +HE * +Perl_hv_store_ent(pTHX_ HV *hv, SV *keysv, SV *val, U32 hash) { - register XPVHV* xhv; - register U32 hash; - register HE *entry; - SV *sv; + return hv_fetch_common(hv, keysv, NULL, 0, 0, HV_FETCH_ISSTORE, val, hash); +} - if (!hv) - return 0; +/* +=for apidoc hv_exists - if (SvRMAGICAL(hv)) { - /* All this clause seems to be utf8 unaware. - By moving the utf8 stuff out to hv_fetch_flags I need to ensure - key doesn't leak. I've not tried solving the utf8-ness. - NWC. - */ - if (mg_find((SV*)hv, PERL_MAGIC_tied) || SvGMAGICAL((SV*)hv)) { - sv = sv_newmortal(); - sv_upgrade(sv, SVt_PVLV); - mg_copy((SV*)hv, sv, key, klen); - if (flags & HVhek_FREEKEY) - Safefree(key); - LvTYPE(sv) = 't'; - LvTARG(sv) = sv; /* fake (SV**) */ - return &(LvTARG(sv)); - } -#ifdef ENV_IS_CASELESS - else if (mg_find((SV*)hv, PERL_MAGIC_env)) { - I32 i; - for (i = 0; i < klen; ++i) - if (isLOWER(key[i])) { - char *nkey = strupr(SvPVX(sv_2mortal(newSVpvn(key,klen)))); - SV **ret = hv_fetch(hv, nkey, klen, 0); - if (!ret && lval) { - ret = hv_store_flags(hv, key, klen, NEWSV(61,0), 0, - flags); - } else if (flags & HVhek_FREEKEY) - Safefree(key); - return ret; - } - } -#endif - } +Returns a boolean indicating whether the specified hash key exists. The +C is the length of the key. - /* We use xhv->xhv_foo fields directly instead of HvFOO(hv) to - avoid unnecessary pointer dereferencing. */ - xhv = (XPVHV*)SvANY(hv); - if (!xhv->xhv_array /* !HvARRAY(hv) */) { - if (lval -#ifdef DYNAMIC_ENV_FETCH /* if it's an %ENV lookup, we may get it on the fly */ - || (SvRMAGICAL((SV*)hv) && mg_find((SV*)hv, PERL_MAGIC_env)) -#endif - ) - Newz(503, xhv->xhv_array /* HvARRAY(hv) */, - PERL_HV_ARRAY_ALLOC_BYTES(xhv->xhv_max+1 /* HvMAX(hv)+1 */), - char); - else { - if (flags & HVhek_FREEKEY) - Safefree(key); - return 0; - } - } +=cut +*/ - if (HvREHASH(hv)) { - PERL_HASH_INTERNAL(hash, key, klen); +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 { - PERL_HASH(hash, key, klen); + klen = klen_i32; + flags = 0; } + return hv_fetch_common(hv, NULL, key, klen, flags, HV_FETCH_ISEXISTS, 0, 0) + ? TRUE : FALSE; +} - /* entry = (HvARRAY(hv))[hash & (I32) HvMAX(hv)]; */ - entry = ((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max]; - for (; entry; entry = HeNEXT(entry)) { - if (!HeKEY_hek(entry)) - continue; - 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; - /* flags is 0 if not utf8. need HeKFLAGS(entry) also 0. - flags is 1 if utf8. need HeKFLAGS(entry) also 1. - xor is true if bits differ, in which case this isn't a match. */ - if ((HeKFLAGS(entry) ^ flags) & HVhek_UTF8) - continue; - if (lval && HeKFLAGS(entry) != flags) { - /* We match if HVhek_UTF8 bit in our flags and hash key's match. - But if entry was set previously with HVhek_WASUTF8 and key now - doesn't (or vice versa) then we should change the key's flag, - as this is assignment. */ - if (HvSHAREKEYS(hv)) { - /* Need to swap the key we have for a key with the flags we - need. As keys are shared we can't just write to the flag, - so we share the new one, unshare the old one. */ - int flags_nofree = flags & ~HVhek_FREEKEY; - HEK *new_hek = share_hek_flags(key, klen, hash, flags_nofree); - unshare_hek (HeKEY_hek(entry)); - HeKEY_hek(entry) = new_hek; - } - else - HeKFLAGS(entry) = flags; - if (flags & HVhek_ENABLEHVKFLAGS) - HvHASKFLAGS_on(hv); - } - if (flags & HVhek_FREEKEY) - Safefree(key); - /* if we find a placeholder, we pretend we haven't found anything */ - if (HeVAL(entry) == &PL_sv_placeholder) - break; - return &HeVAL(entry); +/* +=for apidoc hv_fetch +Returns the SV which corresponds to the specified key in the hash. The +C is the length of the key. If C is set then the fetch will be +part of a store. Check that the return value is non-null before +dereferencing it to an C. + +See L for more +information on how to use this function on tied hashes. + +=cut +*/ + +SV** +Perl_hv_fetch(pTHX_ HV *hv, const char *key, I32 klen_i32, I32 lval) +{ + HE *hek; + STRLEN klen; + int flags; + + if (klen_i32 < 0) { + klen = -klen_i32; + flags = HVhek_UTF8; + } else { + klen = klen_i32; + flags = 0; } -#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)) { - unsigned long len; - char *env = PerlEnv_ENVgetenv_len(key,&len); - if (env) { - sv = newSVpvn(env,len); - SvTAINTED_on(sv); - if (flags & HVhek_FREEKEY) - Safefree(key); - return hv_store(hv,key,klen,sv,hash); - } - } -#endif - if (!entry && SvREADONLY(hv)) { - S_hv_notallowed(aTHX_ flags, key, klen, - "access disallowed key '%"SVf"' in" - ); - } - if (lval) { /* gonna assign to this, so it better be there */ - sv = NEWSV(61,0); - return hv_store_flags(hv,key,klen,sv,hash,flags); - } - if (flags & HVhek_FREEKEY) - Safefree(key); - return 0; + hek = hv_fetch_common (hv, NULL, key, klen, flags, + lval ? (HV_FETCH_JUST_SV | HV_FETCH_LVALUE) : HV_FETCH_JUST_SV, + NULL, 0); + return hek ? &HeVAL(hek) : NULL; +} + +/* +=for apidoc hv_exists_ent + +Returns a boolean indicating whether the specified hash key exists. C +can be a valid precomputed hash value, or 0 to ask for it to be +computed. + +=cut +*/ + +/* XXX This looks like an ideal candidate to inline */ +bool +Perl_hv_exists_ent(pTHX_ HV *hv, SV *keysv, U32 hash) +{ + return hv_fetch_common(hv, keysv, NULL, 0, 0, HV_FETCH_ISEXISTS, 0, hash) + ? TRUE : FALSE; } /* returns an HE * structure with the all fields set */ @@ -375,95 +416,267 @@ information on how to use this function on tied hashes. HE * Perl_hv_fetch_ent(pTHX_ HV *hv, SV *keysv, I32 lval, register U32 hash) { - register XPVHV* xhv; - register char *key; - STRLEN klen; - register HE *entry; + return hv_fetch_common(hv, keysv, NULL, 0, 0, + (lval ? HV_FETCH_LVALUE : 0), NULL, hash); +} + +STATIC HE * +S_hv_fetch_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen, + int flags, int action, SV *val, register U32 hash) +{ + dVAR; + XPVHV* xhv; + HE *entry; + HE **oentry; SV *sv; bool is_utf8; - int flags = 0; - char *keysave; + int masked_flags; if (!hv) - return 0; + return NULL; - if (SvRMAGICAL(hv)) { - if (mg_find((SV*)hv, PERL_MAGIC_tied) || SvGMAGICAL((SV*)hv)) { - sv = sv_newmortal(); - keysv = newSVsv(keysv); - mg_copy((SV*)hv, sv, (char*)keysv, HEf_SVKEY); - /* grab a fake HE/HEK pair from the pool or make a new one */ - entry = PL_hv_fetch_ent_mh; - if (entry) - PL_hv_fetch_ent_mh = HeNEXT(entry); - else { - char *k; - entry = new_HE(); - New(54, k, HEK_BASESIZE + sizeof(SV*), char); - HeKEY_hek(entry) = (HEK*)k; - } - HeNEXT(entry) = Nullhe; - HeSVKEY_set(entry, keysv); - HeVAL(entry) = sv; - sv_upgrade(sv, SVt_PVLV); - LvTYPE(sv) = 'T'; - LvTARG(sv) = (SV*)entry; /* so we can free entry when freeing sv */ - return entry; - } -#ifdef ENV_IS_CASELESS - else if (mg_find((SV*)hv, PERL_MAGIC_env)) { - U32 i; - key = SvPV(keysv, klen); - for (i = 0; i < klen; ++i) - if (isLOWER(key[i])) { - SV *nkeysv = sv_2mortal(newSVpvn(key,klen)); - (void)strupr(SvPVX(nkeysv)); - entry = hv_fetch_ent(hv, nkeysv, 0, 0); - if (!entry && lval) - entry = hv_store_ent(hv, keysv, NEWSV(61,0), hash); - return entry; - } - } -#endif + if (keysv) { + if (SvSMAGICAL(hv) && SvGMAGICAL(hv)) + keysv = hv_magic_uvar_xkey(hv, keysv, action); + if (flags & HVhek_FREEKEY) + Safefree(key); + key = SvPV_const(keysv, klen); + flags = 0; + is_utf8 = (SvUTF8(keysv) != 0); + } else { + is_utf8 = ((flags & HVhek_UTF8) ? TRUE : FALSE); } - keysave = key = SvPV(keysv, klen); xhv = (XPVHV*)SvANY(hv); - if (!xhv->xhv_array /* !HvARRAY(hv) */) { - if (lval -#ifdef DYNAMIC_ENV_FETCH /* if it's an %ENV lookup, we may get it on the fly */ - || (SvRMAGICAL((SV*)hv) && mg_find((SV*)hv, PERL_MAGIC_env)) -#endif - ) - Newz(503, xhv->xhv_array /* HvARRAY(hv) */, - PERL_HV_ARRAY_ALLOC_BYTES(xhv->xhv_max+1 /* HvMAX(hv)+1 */), - char); - else - return 0; - } + if (SvMAGICAL(hv)) { + if (SvRMAGICAL(hv) && !(action & (HV_FETCH_ISSTORE|HV_FETCH_ISEXISTS))) { + if (mg_find((SV*)hv, PERL_MAGIC_tied) || SvGMAGICAL((SV*)hv)) { + sv = sv_newmortal(); - is_utf8 = (SvUTF8(keysv)!=0); + /* XXX should be able to skimp on the HE/HEK here when + HV_FETCH_JUST_SV is true. */ - if (is_utf8) { - key = (char*)bytes_from_utf8((U8*)key, &klen, &is_utf8); - if (is_utf8) - flags = HVhek_UTF8; - if (key != keysave) - flags |= HVhek_WASUTF8 | HVhek_FREEKEY; - } + if (!keysv) { + keysv = newSVpvn(key, klen); + if (is_utf8) { + SvUTF8_on(keysv); + } + } else { + keysv = newSVsv(keysv); + } + mg_copy((SV*)hv, sv, (char *)keysv, HEf_SVKEY); + + /* grab a fake HE/HEK pair from the pool or make a new one */ + entry = PL_hv_fetch_ent_mh; + if (entry) + PL_hv_fetch_ent_mh = HeNEXT(entry); + else { + char *k; + entry = new_HE(); + Newx(k, HEK_BASESIZE + sizeof(SV*), char); + HeKEY_hek(entry) = (HEK*)k; + } + HeNEXT(entry) = NULL; + HeSVKEY_set(entry, keysv); + HeVAL(entry) = sv; + sv_upgrade(sv, SVt_PVLV); + LvTYPE(sv) = 'T'; + /* so we can free entry when freeing sv */ + LvTARG(sv) = (SV*)entry; + + /* XXX remove at some point? */ + if (flags & HVhek_FREEKEY) + Safefree(key); + + return entry; + } +#ifdef ENV_IS_CASELESS + else if (mg_find((SV*)hv, PERL_MAGIC_env)) { + U32 i; + for (i = 0; i < klen; ++i) + if (isLOWER(key[i])) { + /* Would be nice if we had a routine to do the + copy and upercase in a single pass through. */ + const char * const nkey = strupr(savepvn(key,klen)); + /* Note that this fetch is for nkey (the uppercased + key) whereas the store is for key (the original) */ + entry = hv_fetch_common(hv, NULL, nkey, klen, + HVhek_FREEKEY, /* free nkey */ + 0 /* non-LVAL fetch */, + NULL /* no value */, + 0 /* compute hash */); + if (!entry && (action & HV_FETCH_LVALUE)) { + /* This call will free key if necessary. + Do it this way to encourage compiler to tail + call optimise. */ + entry = hv_fetch_common(hv, keysv, key, klen, + flags, HV_FETCH_ISSTORE, + newSV(0), hash); + } else { + if (flags & HVhek_FREEKEY) + Safefree(key); + } + return entry; + } + } +#endif + } /* ISFETCH */ + else if (SvRMAGICAL(hv) && (action & HV_FETCH_ISEXISTS)) { + if (mg_find((SV*)hv, PERL_MAGIC_tied) || SvGMAGICAL((SV*)hv)) { + /* I don't understand why hv_exists_ent has svret and sv, + whereas hv_exists only had one. */ + SV * const svret = sv_newmortal(); + sv = sv_newmortal(); + + if (keysv || is_utf8) { + if (!keysv) { + keysv = newSVpvn(key, klen); + SvUTF8_on(keysv); + } else { + keysv = newSVsv(keysv); + } + mg_copy((SV*)hv, sv, (char *)sv_2mortal(keysv), HEf_SVKEY); + } else { + mg_copy((SV*)hv, sv, key, klen); + } + if (flags & HVhek_FREEKEY) + Safefree(key); + magic_existspack(svret, mg_find(sv, PERL_MAGIC_tiedelem)); + /* This cast somewhat evil, but I'm merely using NULL/ + not NULL to return the boolean exists. + And I know hv is not NULL. */ + return SvTRUE(svret) ? (HE *)hv : NULL; + } +#ifdef ENV_IS_CASELESS + else if (mg_find((SV*)hv, PERL_MAGIC_env)) { + /* XXX This code isn't UTF8 clean. */ + char * const keysave = (char * const)key; + /* Will need to free this, so set FREEKEY flag. */ + key = savepvn(key,klen); + key = (const char*)strupr((char*)key); + is_utf8 = FALSE; + hash = 0; + keysv = 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) { + const bool save_taint = PL_tainted; + if (keysv || is_utf8) { + if (!keysv) { + keysv = newSVpvn(key, klen); + SvUTF8_on(keysv); + } + if (PL_tainting) + PL_tainted = SvTAINTED(keysv); + keysv = sv_2mortal(newSVsv(keysv)); + mg_copy((SV*)hv, val, (char*)keysv, HEf_SVKEY); + } else { + mg_copy((SV*)hv, val, key, klen); + } + + TAINT_IF(save_taint); + if (!needs_store) { + if (flags & HVhek_FREEKEY) + Safefree(key); + return NULL; + } +#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 = FALSE; + hash = 0; + keysv = 0; + + if (flags & HVhek_FREEKEY) { + Safefree(keysave); + } + flags |= HVhek_FREEKEY; + } +#endif + } + } /* ISSTORE */ + } /* SvMAGICAL */ + + if (!HvARRAY(hv)) { + if ((action & (HV_FETCH_LVALUE | HV_FETCH_ISSTORE)) +#ifdef DYNAMIC_ENV_FETCH /* if it's an %ENV lookup, we may get it on the fly */ + || (SvRMAGICAL((SV*)hv) && mg_find((SV*)hv, PERL_MAGIC_env)) +#endif + ) { + char *array; + Newxz(array, + PERL_HV_ARRAY_ALLOC_BYTES(xhv->xhv_max+1 /* HvMAX(hv)+1 */), + char); + HvARRAY(hv) = (HE**)array; + } +#ifdef DYNAMIC_ENV_FETCH + else if (action & HV_FETCH_ISEXISTS) { + /* for an %ENV exists, if we do an insert it's by a recursive + store call, so avoid creating HvARRAY(hv) right now. */ + } +#endif + else { + /* XXX remove at some point? */ + if (flags & HVhek_FREEKEY) + Safefree(key); + + return 0; + } + } + + if (is_utf8) { + char * const keysave = (char *)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) + Safefree(keysave); + flags |= HVhek_WASUTF8 | HVhek_FREEKEY; + } + } if (HvREHASH(hv)) { PERL_HASH_INTERNAL(hash, key, klen); + /* We don't have a pointer to the hv, so we have to replicate the + flag into every HEK, so that hv_iterkeysv can see it. */ + /* And yes, you do need this even though you are not "storing" because + you can flip the flags below if doing an lval lookup. (And that + was put in to give the semantics Andreas was expecting.) */ + flags |= HVhek_REHASH; } else if (!hash) { - if SvIsCOW_shared_hash(keysv) { - hash = SvUVX(keysv); + if (keysv && (SvIsCOW_shared_hash(keysv))) { + hash = SvSHARED_HASH(keysv); } else { PERL_HASH(hash, key, klen); } } - /* entry = (HvARRAY(hv))[hash & (I32) HvMAX(hv)]; */ - entry = ((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max]; + masked_flags = (flags & HVhek_MASK); + +#ifdef DYNAMIC_ENV_FETCH + if (!HvARRAY(hv)) entry = NULL; + else +#endif + { + entry = (HvARRAY(hv))[hash & (I32) HvMAX(hv)]; + } for (; entry; entry = HeNEXT(entry)) { if (HeHASH(entry) != hash) /* strings can't be equal */ continue; @@ -471,72 +684,193 @@ Perl_hv_fetch_ent(pTHX_ HV *hv, SV *keysv, I32 lval, register U32 hash) continue; if (HeKEY(entry) != key && memNE(HeKEY(entry),key,klen)) /* is this it? */ continue; - if ((HeKFLAGS(entry) ^ flags) & HVhek_UTF8) + if ((HeKFLAGS(entry) ^ masked_flags) & HVhek_UTF8) continue; - if (lval && HeKFLAGS(entry) != flags) { - /* We match if HVhek_UTF8 bit in our flags and hash key's match. - But if entry was set previously with HVhek_WASUTF8 and key now - doesn't (or vice versa) then we should change the key's flag, - as this is assignment. */ - if (HvSHAREKEYS(hv)) { - /* Need to swap the key we have for a key with the flags we - need. As keys are shared we can't just write to the flag, - so we share the new one, unshare the old one. */ - int flags_nofree = flags & ~HVhek_FREEKEY; - HEK *new_hek = share_hek_flags(key, klen, hash, flags_nofree); - unshare_hek (HeKEY_hek(entry)); - HeKEY_hek(entry) = new_hek; - } - else - HeKFLAGS(entry) = flags; - if (flags & HVhek_ENABLEHVKFLAGS) - HvHASKFLAGS_on(hv); - } - if (key != keysave) - Safefree(key); - /* 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 * const new_hek = share_hek_flags(key, klen, hash, + masked_flags); + unshare_hek (HeKEY_hek(entry)); + HeKEY_hek(entry) = new_hek; + } + else if (hv == PL_strtab) { + /* PL_strtab is usually the only hash without HvSHAREKEYS, + so putting this test here is cheap */ + if (flags & HVhek_FREEKEY) + Safefree(key); + Perl_croak(aTHX_ S_strtab_error, + action & HV_FETCH_LVALUE ? "fetch" : "store"); + } + else + HeKFLAGS(entry) = masked_flags; + if (masked_flags & HVhek_ENABLEHVKFLAGS) + HvHASKFLAGS_on(hv); + } + if (HeVAL(entry) == &PL_sv_placeholder) { + /* yes, can store into placeholder slot */ + if (action & HV_FETCH_LVALUE) { + if (SvMAGICAL(hv)) { + /* This preserves behaviour with the old hv_fetch + implementation which at this point would bail out + with a break; (at "if we find a placeholder, we + pretend we haven't found anything") + + That break mean that if a placeholder were found, it + caused a call into hv_store, which in turn would + check magic, and if there is no magic end up pretty + much back at this point (in hv_store's code). */ + break; + } + /* LVAL fetch which actaully needs a store. */ + val = newSV(0); + HvPLACEHOLDERS(hv)--; + } else { + /* store */ + if (val != &PL_sv_placeholder) + HvPLACEHOLDERS(hv)--; + } + HeVAL(entry) = val; + } else if (action & HV_FETCH_ISSTORE) { + SvREFCNT_dec(HeVAL(entry)); + HeVAL(entry) = val; + } + } else if (HeVAL(entry) == &PL_sv_placeholder) { + /* if we find a placeholder, we pretend we haven't found + anything */ break; + } + if (flags & HVhek_FREEKEY) + Safefree(key); return entry; } #ifdef DYNAMIC_ENV_FETCH /* %ENV lookup? If so, try to fetch the value now */ - if (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); + const char * const env = PerlEnv_ENVgetenv_len(key,&len); if (env) { sv = newSVpvn(env,len); SvTAINTED_on(sv); - return hv_store_ent(hv,keysv,sv,hash); + return hv_fetch_common(hv,keysv,key,klen,flags,HV_FETCH_ISSTORE,sv, + hash); } } #endif - if (!entry && SvREADONLY(hv)) { - S_hv_notallowed(aTHX_ flags, key, klen, - "access disallowed key '%"SVf"' in" - ); + + if (!entry && SvREADONLY(hv) && !(action & HV_FETCH_ISEXISTS)) { + hv_notallowed(flags, key, klen, + "Attempt to access disallowed key '%"SVf"' in" + " a restricted hash"); } - if (flags & HVhek_FREEKEY) - Safefree(key); - if (lval) { /* gonna assign to this, so it better be there */ - sv = NEWSV(61,0); - return hv_store_ent(hv,keysv,sv,hash); + if (!(action & (HV_FETCH_LVALUE|HV_FETCH_ISSTORE))) { + /* Not doing some form of store, so return failure. */ + if (flags & HVhek_FREEKEY) + Safefree(key); + return 0; + } + if (action & HV_FETCH_LVALUE) { + val = newSV(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. */ + } + } + + /* Welcome to hv_store... */ + + if (!HvARRAY(hv)) { + /* Not sure if we can get here. I think the only case of oentry being + NULL is for %ENV with dynamic env fetch. But that should disappear + with magic in the previous code. */ + char *array; + Newxz(array, + PERL_HV_ARRAY_ALLOC_BYTES(xhv->xhv_max+1 /* HvMAX(hv)+1 */), + char); + HvARRAY(hv) = (HE**)array; + } + + oentry = &(HvARRAY(hv))[hash & (I32) xhv->xhv_max]; + + entry = new_HE(); + /* share_hek_flags will do the free for us. This might be considered + bad API design. */ + if (HvSHAREKEYS(hv)) + HeKEY_hek(entry) = share_hek_flags(key, klen, hash, flags); + else if (hv == PL_strtab) { + /* PL_strtab is usually the only hash without HvSHAREKEYS, so putting + this test here is cheap */ + if (flags & HVhek_FREEKEY) + Safefree(key); + Perl_croak(aTHX_ S_strtab_error, + action & HV_FETCH_LVALUE ? "fetch" : "store"); + } + else /* gotta do the real thing */ + HeKEY_hek(entry) = save_hek_flags(key, klen, hash, flags); + HeVAL(entry) = val; + HeNEXT(entry) = *oentry; + *oentry = entry; + + if (val == &PL_sv_placeholder) + HvPLACEHOLDERS(hv)++; + if (masked_flags & HVhek_ENABLEHVKFLAGS) + HvHASKFLAGS_on(hv); + + { + const HE *counter = HeNEXT(entry); + + xhv->xhv_keys++; /* HvTOTALKEYS(hv)++ */ + if (!counter) { /* initial entry? */ + xhv->xhv_fill++; /* HvFILL(hv)++ */ + } else if (xhv->xhv_keys > (IV)xhv->xhv_max) { + hsplit(hv); + } else if(!HvREHASH(hv)) { + U32 n_links = 1; + + while ((counter = HeNEXT(counter))) + n_links++; + + if (n_links > HV_MAX_LENGTH_BEFORE_SPLIT) { + /* Use only the old HvKEYS(hv) > HvMAX(hv) condition to limit + bucket splits on a rehashed hash, as we're not going to + split it again, and if someone is lucky (evil) enough to + get all the keys in one list they could exhaust our memory + as we repeatedly double the number of buckets on every + entry. Linear search feels a less worse thing to do. */ + hsplit(hv); + } + } } - return 0; + + return entry; } STATIC void -S_hv_magic_check(pTHX_ HV *hv, bool *needs_copy, bool *needs_store) +S_hv_magic_check(HV *hv, bool *needs_copy, bool *needs_store) { - MAGIC *mg = SvMAGIC(hv); + const MAGIC *mg = SvMAGIC(hv); *needs_copy = FALSE; *needs_store = TRUE; while (mg) { if (isUPPER(mg->mg_type)) { *needs_copy = TRUE; - switch (mg->mg_type) { - case PERL_MAGIC_tied: - case PERL_MAGIC_sig: + if (mg->mg_type == PERL_MAGIC_tied) { *needs_store = FALSE; + return; /* We've set all there is to set. */ } } mg = mg->mg_moremagic; @@ -544,959 +878,325 @@ S_hv_magic_check(pTHX_ HV *hv, bool *needs_copy, bool *needs_store) } /* -=for apidoc hv_store - -Stores an SV in a hash. The hash key is specified as C and C is -the length of the key. The C 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. Note that the caller is -responsible for suitably incrementing the reference count of C before -the call, and decrementing it if the function returned NULL. Effectively -a successful hv_store takes ownership of one reference to C. 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. +=for apidoc hv_scalar -See L for more -information on how to use this function on tied hashes. +Evaluates the hash in scalar context and returns the result. Handles magic when the hash is tied. =cut */ -SV** -Perl_hv_store(pTHX_ HV *hv, const char *key, I32 klen, SV *val, U32 hash) +SV * +Perl_hv_scalar(pTHX_ HV *hv) { - bool is_utf8 = FALSE; - const char *keysave = key; - int flags = 0; - - if (klen < 0) { - klen = -klen; - is_utf8 = TRUE; - } + SV *sv; - if (is_utf8) { - STRLEN tmplen = klen; - /* Just casting the &klen to (STRLEN) won't work well - * if STRLEN and I32 are of different widths. --jhi */ - key = (char*)bytes_from_utf8((U8*)key, &tmplen, &is_utf8); - klen = tmplen; - /* If we were able to downgrade here, then than means that we were - passed in a key which only had chars 0-255, but was utf8 encoded. */ - if (is_utf8) - flags = HVhek_UTF8; - /* If we found we were able to downgrade the string to bytes, then - we should flag that it needs upgrading on keys or each. */ - if (key != keysave) - flags |= HVhek_WASUTF8 | HVhek_FREEKEY; + if (SvRMAGICAL(hv)) { + MAGIC * const mg = mg_find((SV*)hv, PERL_MAGIC_tied); + if (mg) + return magic_scalarpack(hv, mg); } - return hv_store_flags (hv, key, klen, val, hash, flags); + 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; } -SV** -Perl_hv_store_flags(pTHX_ HV *hv, const char *key, I32 klen, SV *val, - register U32 hash, int flags) -{ - register XPVHV* xhv; - register U32 n_links; - register HE *entry; - register HE **oentry; - - if (!hv) - return 0; - - xhv = (XPVHV*)SvANY(hv); - if (SvMAGICAL(hv)) { - bool needs_copy; - bool needs_store; - hv_magic_check (hv, &needs_copy, &needs_store); - if (needs_copy) { - mg_copy((SV*)hv, val, key, klen); - if (!xhv->xhv_array /* !HvARRAY */ && !needs_store) { - if (flags & HVhek_FREEKEY) - Safefree(key); - return 0; - } -#ifdef ENV_IS_CASELESS - else if (mg_find((SV*)hv, PERL_MAGIC_env)) { - key = savepvn(key,klen); - key = (const char*)strupr((char*)key); - hash = 0; - } -#endif - } - } - - if (flags) - 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) - PERL_HASH(hash, key, klen); - - if (!xhv->xhv_array /* !HvARRAY(hv) */) - Newz(505, xhv->xhv_array /* HvARRAY(hv) */, - PERL_HV_ARRAY_ALLOC_BYTES(xhv->xhv_max+1 /* HvMAX(hv)+1 */), - char); +/* +=for apidoc hv_delete - /* oentry = &(HvARRAY(hv))[hash & (I32) HvMAX(hv)]; */ - oentry = &((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max]; +Deletes a key/value pair in the hash. The value SV is removed from the +hash and returned to the caller. The C is the length of the key. +The C value will normally be zero; if set to G_DISCARD then NULL +will be returned. - n_links = 0; +=cut +*/ - for (entry = *oentry; entry; ++n_links, entry = HeNEXT(entry)) { - if (HeHASH(entry) != hash) /* strings can't be equal */ - continue; - if (HeKLEN(entry) != (I32)klen) - continue; - if (HeKEY(entry) != key && memNE(HeKEY(entry),key,klen)) /* is this it? */ - continue; - if ((HeKFLAGS(entry) ^ flags) & HVhek_UTF8) - continue; - if (HeVAL(entry) == &PL_sv_placeholder) - xhv->xhv_placeholders--; /* yes, can store into placeholder slot */ - else - SvREFCNT_dec(HeVAL(entry)); - if (flags & HVhek_PLACEHOLD) { - /* We have been requested to insert a placeholder. Currently - only Storable is allowed to do this. */ - xhv->xhv_placeholders++; - HeVAL(entry) = &PL_sv_placeholder; - } else - HeVAL(entry) = val; - - if (HeKFLAGS(entry) != flags) { - /* We match if HVhek_UTF8 bit in our flags and hash key's match. - But if entry was set previously with HVhek_WASUTF8 and key now - doesn't (or vice versa) then we should change the key's flag, - as this is assignment. */ - if (HvSHAREKEYS(hv)) { - /* Need to swap the key we have for a key with the flags we - need. As keys are shared we can't just write to the flag, - so we share the new one, unshare the old one. */ - int flags_nofree = flags & ~HVhek_FREEKEY; - HEK *new_hek = share_hek_flags(key, klen, hash, flags_nofree); - unshare_hek (HeKEY_hek(entry)); - HeKEY_hek(entry) = new_hek; - } - else - HeKFLAGS(entry) = flags; - } - if (flags & HVhek_FREEKEY) - Safefree(key); - return &HeVAL(entry); - } +SV * +Perl_hv_delete(pTHX_ HV *hv, const char *key, I32 klen_i32, I32 flags) +{ + STRLEN klen; + int k_flags; - if (SvREADONLY(hv)) { - S_hv_notallowed(aTHX_ flags, key, klen, - "access disallowed key '%"SVf"' to" - ); + if (klen_i32 < 0) { + klen = -klen_i32; + k_flags = HVhek_UTF8; + } else { + klen = klen_i32; + k_flags = 0; } - - entry = new_HE(); - /* share_hek_flags will do the free for us. This might be considered - bad API design. */ - if (HvSHAREKEYS(hv)) - HeKEY_hek(entry) = share_hek_flags(key, klen, hash, flags); - else /* gotta do the real thing */ - HeKEY_hek(entry) = save_hek_flags(key, klen, hash, flags); - if (flags & HVhek_PLACEHOLD) { - /* We have been requested to insert a placeholder. Currently - only Storable is allowed to do this. */ - xhv->xhv_placeholders++; - HeVAL(entry) = &PL_sv_placeholder; - } else - HeVAL(entry) = val; - HeNEXT(entry) = *oentry; - *oentry = entry; - - xhv->xhv_keys++; /* HvKEYS(hv)++ */ - if (!n_links) { /* initial entry? */ - xhv->xhv_fill++; /* HvFILL(hv)++ */ - } else if ((n_links > HV_MAX_LENGTH_BEFORE_SPLIT) - && (!HvREHASH(hv) - || (xhv->xhv_keys > (IV)xhv->xhv_max))) { - /* Use the old HvKEYS(hv) > HvMAX(hv) condition to limit bucket - splits on a rehashed hash, as we're not going to split it again, - and if someone is lucky (evil) enough to get all the keys in one - list they could exhaust our memory as we repeatedly double the - number of buckets on every entry. Linear search feels a less worse - thing to do. */ - hsplit(hv); - } - - return &HeVAL(entry); + return hv_delete_common(hv, NULL, key, klen, k_flags, flags, 0); } /* -=for apidoc hv_store_ent - -Stores C in a hash. The hash key is specified as C. The C -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 macros -described here. Note that the caller is responsible for suitably -incrementing the reference count of C before the call, and -decrementing it if the function returned NULL. Effectively a successful -hv_store_ent takes ownership of one reference to C. 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; -unlike C it does not take ownership of it, so maintaining the correct -reference count on C is entirely the caller's responsibility. hv_store -is not implemented as a call to hv_store_ent, and does not create a temporary -SV for the key, so if your key data is not already in SV form then use -hv_store in preference to hv_store_ent. +=for apidoc hv_delete_ent -See L for more -information on how to use this function on tied hashes. +Deletes a key/value pair in the hash. The value SV is removed from the +hash and returned to the caller. The C value will normally be zero; +if set to G_DISCARD then NULL will be returned. C can be a valid +precomputed hash value, or 0 to ask for it to be computed. =cut */ -HE * -Perl_hv_store_ent(pTHX_ HV *hv, SV *keysv, SV *val, U32 hash) +/* XXX This looks like an ideal candidate to inline */ +SV * +Perl_hv_delete_ent(pTHX_ HV *hv, SV *keysv, I32 flags, U32 hash) { - XPVHV* xhv; - char *key; - STRLEN klen; - U32 n_links; - HE *entry; - HE **oentry; - bool is_utf8; - int flags = 0; - char *keysave; - - if (!hv) - return 0; - - 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 (PL_tainting) - PL_tainted = SvTAINTED(keysv); - keysv = sv_2mortal(newSVsv(keysv)); - mg_copy((SV*)hv, val, (char*)keysv, HEf_SVKEY); - TAINT_IF(save_taint); - if (!xhv->xhv_array /* !HvARRAY(hv) */ && !needs_store) - return Nullhe; -#ifdef ENV_IS_CASELESS - else if (mg_find((SV*)hv, PERL_MAGIC_env)) { - key = SvPV(keysv, klen); - keysv = sv_2mortal(newSVpvn(key,klen)); - (void)strupr(SvPVX(keysv)); - hash = 0; - } -#endif - } - } - - keysave = key = SvPV(keysv, klen); - is_utf8 = (SvUTF8(keysv) != 0); - - if (is_utf8) { - key = (char*)bytes_from_utf8((U8*)key, &klen, &is_utf8); - if (is_utf8) - flags = HVhek_UTF8; - if (key != keysave) - flags |= HVhek_WASUTF8 | HVhek_FREEKEY; - HvHASKFLAGS_on((SV*)hv); - } - - if (HvREHASH(hv)) { - /* We don't have a pointer to the hv, so we have to replicate the - flag into every HEK, so that hv_iterkeysv can see it. */ - flags |= HVhek_REHASH; - PERL_HASH_INTERNAL(hash, key, klen); - } else if (!hash) { - if SvIsCOW_shared_hash(keysv) { - hash = SvUVX(keysv); - } else { - PERL_HASH(hash, key, klen); - } - } - - if (!xhv->xhv_array /* !HvARRAY(hv) */) - Newz(505, xhv->xhv_array /* HvARRAY(hv) */, - PERL_HV_ARRAY_ALLOC_BYTES(xhv->xhv_max+1 /* HvMAX(hv)+1 */), - char); - - /* oentry = &(HvARRAY(hv))[hash & (I32) HvMAX(hv)]; */ - oentry = &((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max]; - n_links = 0; - entry = *oentry; - for (; entry; ++n_links, entry = HeNEXT(entry)) { - if (HeHASH(entry) != hash) /* strings can't be equal */ - continue; - if (HeKLEN(entry) != (I32)klen) - continue; - if (HeKEY(entry) != key && memNE(HeKEY(entry),key,klen)) /* is this it? */ - continue; - if ((HeKFLAGS(entry) ^ flags) & HVhek_UTF8) - continue; - if (HeVAL(entry) == &PL_sv_placeholder) - xhv->xhv_placeholders--; /* yes, can store into placeholder slot */ - else - SvREFCNT_dec(HeVAL(entry)); - HeVAL(entry) = val; - if (HeKFLAGS(entry) != flags) { - /* We match if HVhek_UTF8 bit in our flags and hash key's match. - But if entry was set previously with HVhek_WASUTF8 and key now - doesn't (or vice versa) then we should change the key's flag, - as this is assignment. */ - if (HvSHAREKEYS(hv)) { - /* Need to swap the key we have for a key with the flags we - need. As keys are shared we can't just write to the flag, - so we share the new one, unshare the old one. */ - int flags_nofree = flags & ~HVhek_FREEKEY; - HEK *new_hek = share_hek_flags(key, klen, hash, flags_nofree); - unshare_hek (HeKEY_hek(entry)); - HeKEY_hek(entry) = new_hek; - } - else - HeKFLAGS(entry) = flags; - } - if (flags & HVhek_FREEKEY) - Safefree(key); - return entry; - } - - if (SvREADONLY(hv)) { - S_hv_notallowed(aTHX_ flags, key, klen, - "access disallowed key '%"SVf"' to" - ); - } - - entry = new_HE(); - /* share_hek_flags will do the free for us. This might be considered - bad API design. */ - if (HvSHAREKEYS(hv)) - HeKEY_hek(entry) = share_hek_flags(key, klen, hash, flags); - else /* gotta do the real thing */ - HeKEY_hek(entry) = save_hek_flags(key, klen, hash, flags); - HeVAL(entry) = val; - HeNEXT(entry) = *oentry; - *oentry = entry; - - xhv->xhv_keys++; /* HvKEYS(hv)++ */ - if (!n_links) { /* initial entry? */ - xhv->xhv_fill++; /* HvFILL(hv)++ */ - } else if ((xhv->xhv_keys > (IV)xhv->xhv_max) - || ((n_links > HV_MAX_LENGTH_BEFORE_SPLIT) && !HvREHASH(hv))) { - /* Use only the old HvKEYS(hv) > HvMAX(hv) condition to limit bucket - splits on a rehashed hash, as we're not going to split it again, - and if someone is lucky (evil) enough to get all the keys in one - list they could exhaust our memory as we repeatedly double the - number of buckets on every entry. Linear search feels a less worse - thing to do. */ - hsplit(hv); - } - - return entry; -} - -/* -=for apidoc hv_delete - -Deletes a key/value pair in the hash. The value SV is removed from the -hash and returned to the caller. The C is the length of the key. -The C value will normally be zero; if set to G_DISCARD then NULL -will be returned. - -=cut -*/ - -SV * -Perl_hv_delete(pTHX_ HV *hv, const char *key, I32 klen, I32 flags) -{ - register XPVHV* xhv; - register I32 i; - register U32 hash; - register HE *entry; - register HE **oentry; - SV **svp; - SV *sv; - bool is_utf8 = FALSE; - int k_flags = 0; - const char *keysave = key; - - if (!hv) - return Nullsv; - if (klen < 0) { - klen = -klen; - is_utf8 = TRUE; - } - if (SvRMAGICAL(hv)) { - bool needs_copy; - bool needs_store; - hv_magic_check (hv, &needs_copy, &needs_store); - - if (needs_copy && (svp = hv_fetch(hv, key, klen, TRUE))) { - sv = *svp; - if (SvMAGICAL(sv)) { - mg_clear(sv); - } - if (!needs_store) { - if (mg_find(sv, PERL_MAGIC_tiedelem)) { - /* No longer an element */ - sv_unmagic(sv, PERL_MAGIC_tiedelem); - return sv; - } - return Nullsv; /* element cannot be deleted */ - } -#ifdef ENV_IS_CASELESS - else if (mg_find((SV*)hv, PERL_MAGIC_env)) { - sv = sv_2mortal(newSVpvn(key,klen)); - key = strupr(SvPVX(sv)); - } -#endif - } - } - xhv = (XPVHV*)SvANY(hv); - if (!xhv->xhv_array /* !HvARRAY(hv) */) - return Nullsv; - - if (is_utf8) { - STRLEN tmplen = klen; - /* See the note in hv_fetch(). --jhi */ - key = (char*)bytes_from_utf8((U8*)key, &tmplen, &is_utf8); - klen = tmplen; - if (is_utf8) - k_flags = HVhek_UTF8; - if (key != keysave) - k_flags |= HVhek_FREEKEY; - } - - if (HvREHASH(hv)) { - PERL_HASH_INTERNAL(hash, key, klen); - } else { - PERL_HASH(hash, key, klen); - } - - /* oentry = &(HvARRAY(hv))[hash & (I32) HvMAX(hv)]; */ - oentry = &((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max]; - entry = *oentry; - i = 1; - for (; entry; i=0, oentry = &HeNEXT(entry), entry = *oentry) { - if (HeHASH(entry) != hash) /* strings can't be equal */ - continue; - if (HeKLEN(entry) != (I32)klen) - continue; - if (HeKEY(entry) != key && memNE(HeKEY(entry),key,klen)) /* is this it? */ - continue; - if ((HeKFLAGS(entry) ^ k_flags) & HVhek_UTF8) - continue; - if (k_flags & HVhek_FREEKEY) - Safefree(key); - /* if placeholder is here, it's already been deleted.... */ - if (HeVAL(entry) == &PL_sv_placeholder) - { - if (SvREADONLY(hv)) - return Nullsv; /* if still SvREADONLY, leave it deleted. */ - else { - /* okay, really delete the placeholder... */ - *oentry = HeNEXT(entry); - if (i && !*oentry) - xhv->xhv_fill--; /* HvFILL(hv)-- */ - if (entry == xhv->xhv_eiter /* HvEITER(hv) */) - HvLAZYDEL_on(hv); - else - hv_free_ent(hv, entry); - xhv->xhv_keys--; /* HvKEYS(hv)-- */ - if (xhv->xhv_keys == 0) - HvHASKFLAGS_off(hv); - xhv->xhv_placeholders--; - return Nullsv; - } - } - else if (SvREADONLY(hv) && HeVAL(entry) && SvREADONLY(HeVAL(entry))) { - S_hv_notallowed(aTHX_ k_flags, key, klen, - "delete readonly key '%"SVf"' from" - ); - } - - if (flags & G_DISCARD) - sv = Nullsv; - else { - sv = sv_2mortal(HeVAL(entry)); - HeVAL(entry) = &PL_sv_placeholder; - } - - /* - * If a restricted hash, rather than really deleting the entry, put - * a placeholder there. This marks the key as being "approved", so - * we can still access via not-really-existing key without raising - * an error. - */ - if (SvREADONLY(hv)) { - HeVAL(entry) = &PL_sv_placeholder; - /* We'll be saving this slot, so the number of allocated keys - * doesn't go down, but the number placeholders goes up */ - xhv->xhv_placeholders++; /* HvPLACEHOLDERS(hv)++ */ - } else { - *oentry = HeNEXT(entry); - if (i && !*oentry) - xhv->xhv_fill--; /* HvFILL(hv)-- */ - if (entry == xhv->xhv_eiter /* HvEITER(hv) */) - HvLAZYDEL_on(hv); - else - hv_free_ent(hv, entry); - xhv->xhv_keys--; /* HvKEYS(hv)-- */ - if (xhv->xhv_keys == 0) - HvHASKFLAGS_off(hv); - } - return sv; - } - if (SvREADONLY(hv)) { - S_hv_notallowed(aTHX_ k_flags, key, klen, - "access disallowed key '%"SVf"' from" - ); - } - - if (k_flags & HVhek_FREEKEY) - Safefree(key); - return Nullsv; + return hv_delete_common(hv, keysv, NULL, 0, 0, flags, hash); } -/* -=for apidoc hv_delete_ent - -Deletes a key/value pair in the hash. The value SV is removed from the -hash and returned to the caller. The C value will normally be zero; -if set to G_DISCARD then NULL will be returned. C can be a valid -precomputed hash value, or 0 to ask for it to be computed. - -=cut -*/ - -SV * -Perl_hv_delete_ent(pTHX_ HV *hv, SV *keysv, I32 flags, U32 hash) +STATIC SV * +S_hv_delete_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen, + int k_flags, I32 d_flags, U32 hash) { + dVAR; register XPVHV* xhv; - register I32 i; - register char *key; - STRLEN klen; register HE *entry; register HE **oentry; - SV *sv; + HE *const *first_entry; bool is_utf8; - int k_flags = 0; - char *keysave; - - if (!hv) - return Nullsv; - if (SvRMAGICAL(hv)) { - bool needs_copy; - bool needs_store; - hv_magic_check (hv, &needs_copy, &needs_store); - - if (needs_copy && (entry = hv_fetch_ent(hv, keysv, TRUE, hash))) { - sv = HeVAL(entry); - if (SvMAGICAL(sv)) { - mg_clear(sv); - } - if (!needs_store) { - if (mg_find(sv, PERL_MAGIC_tiedelem)) { - /* No longer an element */ - sv_unmagic(sv, PERL_MAGIC_tiedelem); - return sv; - } - return Nullsv; /* element cannot be deleted */ - } -#ifdef ENV_IS_CASELESS - else if (mg_find((SV*)hv, PERL_MAGIC_env)) { - key = SvPV(keysv, klen); - keysv = sv_2mortal(newSVpvn(key,klen)); - (void)strupr(SvPVX(keysv)); - hash = 0; - } -#endif - } - } - xhv = (XPVHV*)SvANY(hv); - if (!xhv->xhv_array /* !HvARRAY(hv) */) - return Nullsv; - - keysave = key = SvPV(keysv, klen); - is_utf8 = (SvUTF8(keysv) != 0); - - if (is_utf8) { - key = (char*)bytes_from_utf8((U8*)key, &klen, &is_utf8); - if (is_utf8) - k_flags = HVhek_UTF8; - if (key != keysave) - k_flags |= HVhek_FREEKEY; - } - - if (HvREHASH(hv)) { - PERL_HASH_INTERNAL(hash, key, klen); - } else if (!hash) { - PERL_HASH(hash, key, klen); - } - - /* oentry = &(HvARRAY(hv))[hash & (I32) HvMAX(hv)]; */ - oentry = &((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max]; - entry = *oentry; - i = 1; - for (; entry; i=0, oentry = &HeNEXT(entry), entry = *oentry) { - if (HeHASH(entry) != hash) /* strings can't be equal */ - continue; - if (HeKLEN(entry) != (I32)klen) - continue; - if (HeKEY(entry) != key && memNE(HeKEY(entry),key,klen)) /* is this it? */ - continue; - if ((HeKFLAGS(entry) ^ k_flags) & HVhek_UTF8) - continue; - if (k_flags & HVhek_FREEKEY) - Safefree(key); - - /* if placeholder is here, it's already been deleted.... */ - if (HeVAL(entry) == &PL_sv_placeholder) - { - if (SvREADONLY(hv)) - return Nullsv; /* if still SvREADONLY, leave it deleted. */ - - /* okay, really delete the placeholder. */ - *oentry = HeNEXT(entry); - if (i && !*oentry) - xhv->xhv_fill--; /* HvFILL(hv)-- */ - if (entry == xhv->xhv_eiter /* HvEITER(hv) */) - HvLAZYDEL_on(hv); - else - hv_free_ent(hv, entry); - xhv->xhv_keys--; /* HvKEYS(hv)-- */ - if (xhv->xhv_keys == 0) - HvHASKFLAGS_off(hv); - xhv->xhv_placeholders--; - return Nullsv; - } - else if (SvREADONLY(hv) && HeVAL(entry) && SvREADONLY(HeVAL(entry))) { - S_hv_notallowed(aTHX_ k_flags, key, klen, - "delete readonly key '%"SVf"' from" - ); - } - - if (flags & G_DISCARD) - sv = Nullsv; - else { - sv = sv_2mortal(HeVAL(entry)); - HeVAL(entry) = &PL_sv_placeholder; - } - - /* - * If a restricted hash, rather than really deleting the entry, put - * a placeholder there. This marks the key as being "approved", so - * we can still access via not-really-existing key without raising - * an error. - */ - if (SvREADONLY(hv)) { - HeVAL(entry) = &PL_sv_placeholder; - /* We'll be saving this slot, so the number of allocated keys - * doesn't go down, but the number placeholders goes up */ - xhv->xhv_placeholders++; /* HvPLACEHOLDERS(hv)++ */ - } else { - *oentry = HeNEXT(entry); - if (i && !*oentry) - xhv->xhv_fill--; /* HvFILL(hv)-- */ - if (entry == xhv->xhv_eiter /* HvEITER(hv) */) - HvLAZYDEL_on(hv); - else - hv_free_ent(hv, entry); - xhv->xhv_keys--; /* HvKEYS(hv)-- */ - if (xhv->xhv_keys == 0) - HvHASKFLAGS_off(hv); - } - return sv; - } - if (SvREADONLY(hv)) { - S_hv_notallowed(aTHX_ k_flags, key, klen, - "delete disallowed key '%"SVf"' from" - ); - } - - if (k_flags & HVhek_FREEKEY) - Safefree(key); - return Nullsv; -} - -/* -=for apidoc hv_exists - -Returns a boolean indicating whether the specified hash key exists. The -C is the length of the key. - -=cut -*/ - -bool -Perl_hv_exists(pTHX_ HV *hv, const char *key, I32 klen) -{ - register XPVHV* xhv; - register U32 hash; - register HE *entry; - SV *sv; - bool is_utf8 = FALSE; - const char *keysave = key; - int k_flags = 0; + int masked_flags; if (!hv) - return 0; - - if (klen < 0) { - klen = -klen; - is_utf8 = TRUE; - } - - if (SvRMAGICAL(hv)) { - if (mg_find((SV*)hv, PERL_MAGIC_tied) || SvGMAGICAL((SV*)hv)) { - sv = sv_newmortal(); - mg_copy((SV*)hv, sv, key, klen); - magic_existspack(sv, mg_find(sv, PERL_MAGIC_tiedelem)); - return (bool)SvTRUE(sv); - } -#ifdef ENV_IS_CASELESS - else if (mg_find((SV*)hv, PERL_MAGIC_env)) { - sv = sv_2mortal(newSVpvn(key,klen)); - key = strupr(SvPVX(sv)); - } -#endif - } - - xhv = (XPVHV*)SvANY(hv); -#ifndef DYNAMIC_ENV_FETCH - if (!xhv->xhv_array /* !HvARRAY(hv) */) - return 0; -#endif - - if (is_utf8) { - STRLEN tmplen = klen; - /* See the note in hv_fetch(). --jhi */ - key = (char*)bytes_from_utf8((U8*)key, &tmplen, &is_utf8); - klen = tmplen; - if (is_utf8) - k_flags = HVhek_UTF8; - if (key != keysave) - k_flags |= HVhek_FREEKEY; - } - - if (HvREHASH(hv)) { - PERL_HASH_INTERNAL(hash, key, klen); - } else { - PERL_HASH(hash, key, klen); - } + return NULL; -#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)) { - if (HeHASH(entry) != hash) /* strings can't be equal */ - continue; - if (HeKLEN(entry) != klen) - continue; - if (HeKEY(entry) != key && memNE(HeKEY(entry),key,klen)) /* is this it? */ - continue; - if ((HeKFLAGS(entry) ^ k_flags) & HVhek_UTF8) - continue; + if (keysv) { + if (SvSMAGICAL(hv) && SvGMAGICAL(hv)) + keysv = hv_magic_uvar_xkey(hv, keysv, -1); if (k_flags & HVhek_FREEKEY) Safefree(key); - /* If we find the key, but the value is a placeholder, return false. */ - if (HeVAL(entry) == &PL_sv_placeholder) - return FALSE; - - return TRUE; - } -#ifdef DYNAMIC_ENV_FETCH /* is it out there? */ - if (SvRMAGICAL((SV*)hv) && mg_find((SV*)hv, PERL_MAGIC_env)) { - unsigned long len; - char *env = PerlEnv_ENVgetenv_len(key,&len); - if (env) { - sv = newSVpvn(env,len); - SvTAINTED_on(sv); - (void)hv_store(hv,key,klen,sv,hash); - if (k_flags & HVhek_FREEKEY) - Safefree(key); - return TRUE; - } + key = SvPV_const(keysv, klen); + k_flags = 0; + is_utf8 = (SvUTF8(keysv) != 0); + } else { + is_utf8 = ((k_flags & HVhek_UTF8) ? TRUE : FALSE); } -#endif - if (k_flags & HVhek_FREEKEY) - Safefree(key); - return FALSE; -} - - -/* -=for apidoc hv_exists_ent - -Returns a boolean indicating whether the specified hash key exists. C -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) -{ - register XPVHV* xhv; - register char *key; - STRLEN klen; - register HE *entry; - SV *sv; - bool is_utf8; - char *keysave; - int k_flags = 0; - - if (!hv) - return 0; if (SvRMAGICAL(hv)) { - if (mg_find((SV*)hv, PERL_MAGIC_tied) || SvGMAGICAL((SV*)hv)) { - SV* svret = sv_newmortal(); - sv = sv_newmortal(); - keysv = sv_2mortal(newSVsv(keysv)); - mg_copy((SV*)hv, sv, (char*)keysv, HEf_SVKEY); - magic_existspack(svret, mg_find(sv, PERL_MAGIC_tiedelem)); - return (bool)SvTRUE(svret); - } + bool needs_copy; + bool needs_store; + hv_magic_check (hv, &needs_copy, &needs_store); + + if (needs_copy) { + SV *sv; + entry = hv_fetch_common(hv, keysv, key, klen, + k_flags & ~HVhek_FREEKEY, HV_FETCH_LVALUE, + NULL, hash); + sv = entry ? HeVAL(entry) : NULL; + if (sv) { + if (SvMAGICAL(sv)) { + mg_clear(sv); + } + if (!needs_store) { + if (mg_find(sv, PERL_MAGIC_tiedelem)) { + /* No longer an element */ + sv_unmagic(sv, PERL_MAGIC_tiedelem); + return sv; + } + return NULL; /* element cannot be deleted */ + } #ifdef ENV_IS_CASELESS - else if (mg_find((SV*)hv, PERL_MAGIC_env)) { - key = SvPV(keysv, klen); - keysv = sv_2mortal(newSVpvn(key,klen)); - (void)strupr(SvPVX(keysv)); - hash = 0; - } + else if (mg_find((SV*)hv, PERL_MAGIC_env)) { + /* XXX This code isn't UTF8 clean. */ + keysv = sv_2mortal(newSVpvn(key,klen)); + if (k_flags & HVhek_FREEKEY) { + Safefree(key); + } + key = strupr(SvPVX(keysv)); + is_utf8 = 0; + k_flags = 0; + hash = 0; + } #endif + } + } } - xhv = (XPVHV*)SvANY(hv); -#ifndef DYNAMIC_ENV_FETCH - if (!xhv->xhv_array /* !HvARRAY(hv) */) - return 0; -#endif + if (!HvARRAY(hv)) + return NULL; - keysave = key = SvPV(keysv, klen); - is_utf8 = (SvUTF8(keysv) != 0); if (is_utf8) { + const char * const keysave = key; key = (char*)bytes_from_utf8((U8*)key, &klen, &is_utf8); + if (is_utf8) - k_flags = HVhek_UTF8; - if (key != keysave) - k_flags |= HVhek_FREEKEY; + k_flags |= HVhek_UTF8; + else + k_flags &= ~HVhek_UTF8; + if (key != keysave) { + if (k_flags & HVhek_FREEKEY) { + /* This shouldn't happen if our caller does what we expect, + but strictly the API allows it. */ + Safefree(keysave); + } + k_flags |= HVhek_WASUTF8 | HVhek_FREEKEY; + } + HvHASKFLAGS_on((SV*)hv); } + if (HvREHASH(hv)) { PERL_HASH_INTERNAL(hash, key, klen); - } else if (!hash) - PERL_HASH(hash, key, klen); + } else if (!hash) { + if (keysv && (SvIsCOW_shared_hash(keysv))) { + hash = SvSHARED_HASH(keysv); + } else { + PERL_HASH(hash, key, klen); + } + } -#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)) { + masked_flags = (k_flags & HVhek_MASK); + + first_entry = oentry = &(HvARRAY(hv))[hash & (I32) HvMAX(hv)]; + entry = *oentry; + for (; entry; oentry = &HeNEXT(entry), entry = *oentry) { + SV *sv; 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) ^ k_flags) & HVhek_UTF8) + if ((HeKFLAGS(entry) ^ masked_flags) & HVhek_UTF8) continue; - if (k_flags & HVhek_FREEKEY) - Safefree(key); - /* If we find the key, but the value is a placeholder, return false. */ - if (HeVAL(entry) == &PL_sv_placeholder) - return FALSE; - return TRUE; - } -#ifdef DYNAMIC_ENV_FETCH /* is it out there? */ - if (SvRMAGICAL((SV*)hv) && mg_find((SV*)hv, PERL_MAGIC_env)) { - unsigned long len; - char *env = PerlEnv_ENVgetenv_len(key,&len); - if (env) { - sv = newSVpvn(env,len); - SvTAINTED_on(sv); - (void)hv_store_ent(hv,keysv,sv,hash); - if (k_flags & HVhek_FREEKEY) - Safefree(key); - return TRUE; + + if (hv == PL_strtab) { + if (k_flags & HVhek_FREEKEY) + Safefree(key); + Perl_croak(aTHX_ S_strtab_error, "delete"); + } + + /* if placeholder is here, it's already been deleted.... */ + if (HeVAL(entry) == &PL_sv_placeholder) { + if (k_flags & HVhek_FREEKEY) + Safefree(key); + return NULL; + } + if (SvREADONLY(hv) && HeVAL(entry) && SvREADONLY(HeVAL(entry))) { + hv_notallowed(k_flags, key, klen, + "Attempt to delete readonly key '%"SVf"' from" + " a restricted hash"); + } + if (k_flags & HVhek_FREEKEY) + Safefree(key); + + if (d_flags & G_DISCARD) + sv = NULL; + else { + sv = sv_2mortal(HeVAL(entry)); + HeVAL(entry) = &PL_sv_placeholder; + } + + /* + * If a restricted hash, rather than really deleting the entry, put + * a placeholder there. This marks the key as being "approved", so + * we can still access via not-really-existing key without raising + * an error. + */ + if (SvREADONLY(hv)) { + SvREFCNT_dec(HeVAL(entry)); + HeVAL(entry) = &PL_sv_placeholder; + /* We'll be saving this slot, so the number of allocated keys + * doesn't go down, but the number placeholders goes up */ + HvPLACEHOLDERS(hv)++; + } else { + *oentry = HeNEXT(entry); + if(!*first_entry) { + xhv->xhv_fill--; /* HvFILL(hv)-- */ + } + if (SvOOK(hv) && entry == HvAUX(hv)->xhv_eiter /* HvEITER(hv) */) + HvLAZYDEL_on(hv); + else + hv_free_ent(hv, entry); + xhv->xhv_keys--; /* HvTOTALKEYS(hv)-- */ + if (xhv->xhv_keys == 0) + HvHASKFLAGS_off(hv); } + return sv; } -#endif + if (SvREADONLY(hv)) { + hv_notallowed(k_flags, key, klen, + "Attempt to delete disallowed key '%"SVf"' from" + " a restricted hash"); + } + if (k_flags & HVhek_FREEKEY) - Safefree(key); - return FALSE; + Safefree(key); + return NULL; } STATIC void S_hsplit(pTHX_ HV *hv) { + dVAR; register XPVHV* xhv = (XPVHV*)SvANY(hv); - I32 oldsize = (I32) xhv->xhv_max+1; /* HvMAX(hv)+1 (sick) */ + const I32 oldsize = (I32) xhv->xhv_max+1; /* HvMAX(hv)+1 (sick) */ register I32 newsize = oldsize * 2; register I32 i; - register char *a = xhv->xhv_array; /* HvARRAY(hv) */ + char *a = (char*) HvARRAY(hv); register HE **aep; - register HE **bep; - register HE *entry; register HE **oentry; int longest_chain = 0; int was_shared; + /*PerlIO_printf(PerlIO_stderr(), "hsplit called for %p which had %d\n", + hv, (int) oldsize);*/ + + if (HvPLACEHOLDERS_get(hv) && !SvREADONLY(hv)) { + /* Can make this clear any placeholders first for non-restricted hashes, + even though Storable rebuilds restricted hashes by putting in all the + placeholders (first) before turning on the readonly flag, because + Storable always pre-splits the hash. */ + hv_clear_placeholders(hv); + } + PL_nomemok = TRUE; #if defined(STRANGE_MALLOC) || defined(MYMALLOC) - Renew(a, PERL_HV_ARRAY_ALLOC_BYTES(newsize), char); + Renew(a, PERL_HV_ARRAY_ALLOC_BYTES(newsize) + + (SvOOK(hv) ? sizeof(struct xpvhv_aux) : 0), char); if (!a) { PL_nomemok = FALSE; return; } + if (SvOOK(hv)) { + Copy(&a[oldsize * sizeof(HE*)], &a[newsize * sizeof(HE*)], 1, struct xpvhv_aux); + } #else - New(2, a, PERL_HV_ARRAY_ALLOC_BYTES(newsize), char); + Newx(a, PERL_HV_ARRAY_ALLOC_BYTES(newsize) + + (SvOOK(hv) ? sizeof(struct xpvhv_aux) : 0), char); if (!a) { PL_nomemok = FALSE; return; } - Copy(xhv->xhv_array /* HvARRAY(hv) */, a, oldsize * sizeof(HE*), char); + Copy(HvARRAY(hv), a, oldsize * sizeof(HE*), char); + if (SvOOK(hv)) { + Copy(HvAUX(hv), &a[newsize * sizeof(HE*)], 1, struct xpvhv_aux); + } if (oldsize >= 64) { - offer_nice_chunk(xhv->xhv_array /* HvARRAY(hv) */, - PERL_HV_ARRAY_ALLOC_BYTES(oldsize)); + offer_nice_chunk(HvARRAY(hv), + PERL_HV_ARRAY_ALLOC_BYTES(oldsize) + + (SvOOK(hv) ? sizeof(struct xpvhv_aux) : 0)); } else - Safefree(xhv->xhv_array /* HvARRAY(hv) */); + Safefree(HvARRAY(hv)); #endif PL_nomemok = FALSE; Zero(&a[oldsize * sizeof(HE*)], (newsize-oldsize) * sizeof(HE*), char); /* zero 2nd half*/ xhv->xhv_max = --newsize; /* HvMAX(hv) = --newsize */ - xhv->xhv_array = a; /* HvARRAY(hv) = a */ + HvARRAY(hv) = (HE**) a; aep = (HE**)a; for (i=0; ixhv_fill = 0; HvSHAREKEYS_off(hv); HvREHASH_on(hv); - aep = (HE **) xhv->xhv_array; + aep = HvARRAY(hv); for (i=0; ixhv_array); - xhv->xhv_array = a; /* HvARRAY(hv) = a */ + Safefree (HvARRAY(hv)); + HvARRAY(hv) = (HE **)a; } void Perl_hv_ksplit(pTHX_ HV *hv, IV newmax) { + dVAR; register XPVHV* xhv = (XPVHV*)SvANY(hv); - I32 oldsize = (I32) xhv->xhv_max+1; /* HvMAX(hv)+1 (sick) */ + const I32 oldsize = (I32) xhv->xhv_max+1; /* HvMAX(hv)+1 (sick) */ register I32 newsize; register I32 i; - register I32 j; register char *a; register HE **aep; register HE *entry; @@ -1618,37 +1324,46 @@ Perl_hv_ksplit(pTHX_ HV *hv, IV newmax) if (newsize < newmax) return; /* overflow detection */ - a = xhv->xhv_array; /* HvARRAY(hv) */ + a = (char *) HvARRAY(hv); if (a) { PL_nomemok = TRUE; #if defined(STRANGE_MALLOC) || defined(MYMALLOC) - Renew(a, PERL_HV_ARRAY_ALLOC_BYTES(newsize), char); + Renew(a, PERL_HV_ARRAY_ALLOC_BYTES(newsize) + + (SvOOK(hv) ? sizeof(struct xpvhv_aux) : 0), char); if (!a) { PL_nomemok = FALSE; return; } + if (SvOOK(hv)) { + Copy(&a[oldsize * sizeof(HE*)], &a[newsize * sizeof(HE*)], 1, struct xpvhv_aux); + } #else - New(2, a, PERL_HV_ARRAY_ALLOC_BYTES(newsize), char); + Newx(a, PERL_HV_ARRAY_ALLOC_BYTES(newsize) + + (SvOOK(hv) ? sizeof(struct xpvhv_aux) : 0), char); if (!a) { PL_nomemok = FALSE; return; } - Copy(xhv->xhv_array /* HvARRAY(hv) */, a, oldsize * sizeof(HE*), char); + Copy(HvARRAY(hv), a, oldsize * sizeof(HE*), char); + if (SvOOK(hv)) { + Copy(HvAUX(hv), &a[newsize * sizeof(HE*)], 1, struct xpvhv_aux); + } if (oldsize >= 64) { - offer_nice_chunk(xhv->xhv_array /* HvARRAY(hv) */, - PERL_HV_ARRAY_ALLOC_BYTES(oldsize)); + offer_nice_chunk(HvARRAY(hv), + PERL_HV_ARRAY_ALLOC_BYTES(oldsize) + + (SvOOK(hv) ? sizeof(struct xpvhv_aux) : 0)); } else - Safefree(xhv->xhv_array /* HvARRAY(hv) */); + Safefree(HvARRAY(hv)); #endif PL_nomemok = FALSE; Zero(&a[oldsize * sizeof(HE*)], (newsize-oldsize) * sizeof(HE*), char); /* zero 2nd half*/ } else { - Newz(0, a, PERL_HV_ARRAY_ALLOC_BYTES(newsize), char); + Newxz(a, PERL_HV_ARRAY_ALLOC_BYTES(newsize), char); } xhv->xhv_max = --newsize; /* HvMAX(hv) = --newsize */ - xhv->xhv_array = a; /* HvARRAY(hv) = a */ + HvARRAY(hv) = (HE **) a; if (!xhv->xhv_fill /* !HvFILL(hv) */) /* skip rest if no entries */ return; @@ -1657,7 +1372,9 @@ Perl_hv_ksplit(pTHX_ HV *hv, IV newmax) if (!*aep) /* non-existent */ continue; for (oentry = aep, entry = *aep; entry; entry = *oentry) { - if ((j = (HeHASH(entry) & newsize)) != i) { + register I32 j = (HeHASH(entry) & newsize); + + if (j != i) { j -= i; *oentry = HeNEXT(entry); if (!(HeNEXT(entry) = aep[j])) @@ -1684,10 +1401,9 @@ Creates a new HV. The reference count is set to 1. HV * Perl_newHV(pTHX) { - register HV *hv; register XPVHV* xhv; + HV * const hv = (HV*)newSV(0); - hv = (HV*)NEWSV(502,0); sv_upgrade((SV *)hv, SVt_PVHV); xhv = (XPVHV*)SvANY(hv); SvPOK_off(hv); @@ -1698,15 +1414,13 @@ Perl_newHV(pTHX) xhv->xhv_max = 7; /* HvMAX(hv) = 7 (start with 8 buckets) */ xhv->xhv_fill = 0; /* HvFILL(hv) = 0 */ - xhv->xhv_pmroot = 0; /* HvPMROOT(hv) = 0 */ - (void)hv_iterinit(hv); /* so each() will start off right */ return hv; } HV * Perl_newHVhv(pTHX_ HV *ohv) { - HV *hv = newHV(); + HV * const hv = newHV(); STRLEN hv_max, hv_fill; if (!ohv || (hv_fill = HvFILL(ohv)) == 0) @@ -1716,15 +1430,16 @@ Perl_newHVhv(pTHX_ HV *ohv) if (!SvMAGICAL((SV *)ohv)) { /* It's an ordinary hash, so copy it fast. AMS 20010804 */ STRLEN i; - bool shared = !!HvSHAREKEYS(ohv); - HE **ents, **oents = (HE **)HvARRAY(ohv); + const bool shared = !!HvSHAREKEYS(ohv); + HE **ents, ** const oents = (HE **)HvARRAY(ohv); char *a; - New(0, a, PERL_HV_ARRAY_ALLOC_BYTES(hv_max+1), char); + Newx(a, PERL_HV_ARRAY_ALLOC_BYTES(hv_max+1), char); ents = (HE**)a; /* In each bucket... */ for (i = 0; i <= hv_max; i++) { - HE *prev = NULL, *ent = NULL, *oent = oents[i]; + HE *prev = NULL; + HE *oent = oents[i]; if (!oent) { ents[i] = NULL; @@ -1732,13 +1447,13 @@ Perl_newHVhv(pTHX_ HV *ohv) } /* Copy the linked list of entries. */ - for (oent = oents[i]; oent; oent = HeNEXT(oent)) { - U32 hash = HeHASH(oent); - char *key = HeKEY(oent); - STRLEN len = HeKLEN(oent); - int flags = HeKFLAGS(oent); + for (; oent; oent = HeNEXT(oent)) { + const U32 hash = HeHASH(oent); + const char * const key = HeKEY(oent); + const STRLEN len = HeKLEN(oent); + const int flags = HeKFLAGS(oent); + HE * const ent = new_HE(); - ent = new_HE(); HeVAL(ent) = newSVsv(HeVAL(oent)); HeKEY_hek(ent) = shared ? share_hek_flags(key, len, hash, flags) @@ -1756,12 +1471,12 @@ Perl_newHVhv(pTHX_ HV *ohv) HvFILL(hv) = hv_fill; HvTOTALKEYS(hv) = HvTOTALKEYS(ohv); HvARRAY(hv) = ents; - } + } /* not magical */ else { /* Iterate over ohv, copying keys and values one at a time. */ HE *entry; - I32 riter = HvRITER(ohv); - HE *eiter = HvEITER(ohv); + const I32 riter = HvRITER_get(ohv); + HE * const eiter = HvEITER_get(ohv); /* Can we use fewer buckets? (hv_max is always 2^n-1) */ while (hv_max && hv_max + 1 >= hv_fill * 2) @@ -1774,22 +1489,56 @@ Perl_newHVhv(pTHX_ HV *ohv) newSVsv(HeVAL(entry)), HeHASH(entry), HeKFLAGS(entry)); } - HvRITER(ohv) = riter; - HvEITER(ohv) = eiter; + HvRITER_set(ohv, riter); + HvEITER_set(ohv, eiter); } return hv; } +/* A rather specialised version of newHVhv for copying %^H, ensuring all the + magic stays on it. */ +HV * +Perl_hv_copy_hints_hv(pTHX_ HV *const ohv) +{ + HV * const hv = newHV(); + STRLEN hv_fill; + + if (ohv && (hv_fill = HvFILL(ohv))) { + STRLEN hv_max = HvMAX(ohv); + HE *entry; + const I32 riter = HvRITER_get(ohv); + HE * const eiter = HvEITER_get(ohv); + + while (hv_max && hv_max + 1 >= hv_fill * 2) + hv_max = hv_max / 2; + HvMAX(hv) = hv_max; + + hv_iterinit(ohv); + while ((entry = hv_iternext_flags(ohv, 0))) { + SV *const sv = newSVsv(HeVAL(entry)); + sv_magic(sv, NULL, PERL_MAGIC_hintselem, + (char *)newSVhek (HeKEY_hek(entry)), HEf_SVKEY); + hv_store_flags(hv, HeKEY(entry), HeKLEN(entry), + sv, HeHASH(entry), HeKFLAGS(entry)); + } + HvRITER_set(ohv, riter); + HvEITER_set(ohv, eiter); + } + hv_magic(hv, NULL, PERL_MAGIC_hints); + return hv; +} + void Perl_hv_free_ent(pTHX_ HV *hv, register HE *entry) { + dVAR; SV *val; if (!entry) return; val = HeVAL(entry); - if (val && isGV(val) && GvCVu(val) && HvNAME(hv)) + if (val && isGV(val) && GvCVu(val) && HvNAME_get(hv)) PL_sub_generation++; /* may be deletion of method from stash */ SvREFCNT_dec(val); if (HeKLEN(entry) == HEf_SVKEY) { @@ -1806,20 +1555,15 @@ Perl_hv_free_ent(pTHX_ HV *hv, register HE *entry) void Perl_hv_delayfree_ent(pTHX_ HV *hv, register HE *entry) { + dVAR; if (!entry) return; - if (isGV(HeVAL(entry)) && GvCVu(HeVAL(entry)) && HvNAME(hv)) - PL_sub_generation++; /* may be deletion of method from stash */ - sv_2mortal(HeVAL(entry)); /* free between statements */ + /* SvREFCNT_inc to counter the SvREFCNT_dec in hv_free_ent */ + sv_2mortal(SvREFCNT_inc(HeVAL(entry))); /* free between statements */ if (HeKLEN(entry) == HEf_SVKEY) { - sv_2mortal(HeKEY_sv(entry)); - Safefree(HeKEY_hek(entry)); + sv_2mortal(SvREFCNT_inc(HeKEY_sv(entry))); } - else if (HvSHAREKEYS(hv)) - unshare_hek(HeKEY_hek(entry)); - else - Safefree(HeKEY_hek(entry)); - del_HE(entry); + hv_free_ent(hv, entry); } /* @@ -1833,87 +1577,257 @@ Clears a hash, making it empty. void Perl_hv_clear(pTHX_ HV *hv) { + dVAR; register XPVHV* xhv; if (!hv) return; + DEBUG_A(Perl_hv_assert(aTHX_ hv)); + xhv = (XPVHV*)SvANY(hv); - if (SvREADONLY(hv)) { + if (SvREADONLY(hv) && HvARRAY(hv) != NULL) { /* restricted hash: convert all keys to placeholders */ - I32 i; - HE* entry; - for (i = 0; i <= (I32) xhv->xhv_max; i++) { - entry = ((HE**)xhv->xhv_array)[i]; + STRLEN i; + for (i = 0; i <= xhv->xhv_max; i++) { + HE *entry = (HvARRAY(hv))[i]; for (; entry; entry = HeNEXT(entry)) { /* not already placeholder */ if (HeVAL(entry) != &PL_sv_placeholder) { if (HeVAL(entry) && SvREADONLY(HeVAL(entry))) { - SV* keysv = hv_iterkeysv(entry); + SV* const keysv = hv_iterkeysv(entry); Perl_croak(aTHX_ - "Attempt to delete readonly key '%"SVf"' from a restricted hash", - keysv); + "Attempt to delete readonly key '%"SVf"' from a restricted hash", + (void*)keysv); } SvREFCNT_dec(HeVAL(entry)); HeVAL(entry) = &PL_sv_placeholder; - xhv->xhv_placeholders++; /* HvPLACEHOLDERS(hv)++ */ + HvPLACEHOLDERS(hv)++; } } } - return; + goto reset; } hfreeentries(hv); - xhv->xhv_placeholders = 0; /* HvPLACEHOLDERS(hv) = 0 */ - if (xhv->xhv_array /* HvARRAY(hv) */) - (void)memzero(xhv->xhv_array /* HvARRAY(hv) */, - (xhv->xhv_max+1 /* HvMAX(hv)+1 */) * sizeof(HE*)); + HvPLACEHOLDERS_set(hv, 0); + if (HvARRAY(hv)) + Zero(HvARRAY(hv), xhv->xhv_max+1 /* HvMAX(hv)+1 */, HE*); if (SvRMAGICAL(hv)) mg_clear((SV*)hv); HvHASKFLAGS_off(hv); HvREHASH_off(hv); + reset: + if (SvOOK(hv)) { + HvEITER_set(hv, NULL); + } +} + +/* +=for apidoc hv_clear_placeholders + +Clears any placeholders from a hash. If a restricted hash has any of its keys +marked as readonly and the key is subsequently deleted, the key is not actually +deleted but is marked by assigning it a value of &PL_sv_placeholder. This tags +it so it will be ignored by future operations such as iterating over the hash, +but will still allow the hash to have a value 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. + +=cut +*/ + +void +Perl_hv_clear_placeholders(pTHX_ HV *hv) +{ + dVAR; + const U32 items = (U32)HvPLACEHOLDERS_get(hv); + + if (items) + clear_placeholders(hv, items); +} + +static void +S_clear_placeholders(pTHX_ HV *hv, U32 items) +{ + dVAR; + I32 i; + + if (items == 0) + return; + + i = HvMAX(hv); + do { + /* Loop down the linked list heads */ + bool first = TRUE; + HE **oentry = &(HvARRAY(hv))[i]; + HE *entry; + + while ((entry = *oentry)) { + if (HeVAL(entry) == &PL_sv_placeholder) { + *oentry = HeNEXT(entry); + if (first && !*oentry) + HvFILL(hv)--; /* This linked list is now empty. */ + if (entry == HvEITER_get(hv)) + HvLAZYDEL_on(hv); + else + hv_free_ent(hv, entry); + + if (--items == 0) { + /* Finished. */ + HvTOTALKEYS(hv) -= (IV)HvPLACEHOLDERS_get(hv); + if (HvKEYS(hv) == 0) + HvHASKFLAGS_off(hv); + HvPLACEHOLDERS_set(hv, 0); + return; + } + } else { + oentry = &HeNEXT(entry); + first = FALSE; + } + } + } while (--i >= 0); + /* You can't get here, hence assertion should always fail. */ + assert (items == 0); + assert (0); } STATIC void S_hfreeentries(pTHX_ HV *hv) { - register HE **array; - register HE *entry; - register HE *oentry = Null(HE*); - I32 riter; - I32 max; + /* This is the array that we're going to restore */ + HE **orig_array; + HEK *name; + int attempts = 100; - if (!hv) - return; if (!HvARRAY(hv)) return; - riter = 0; - max = HvMAX(hv); - array = HvARRAY(hv); - /* make everyone else think the array is empty, so that the destructors - * called for freed entries can't recusively mess with us */ - HvARRAY(hv) = Null(HE**); - HvFILL(hv) = 0; - ((XPVHV*) SvANY(hv))->xhv_keys = 0; - - entry = array[0]; - for (;;) { - if (entry) { - oentry = entry; - entry = HeNEXT(entry); - hv_free_ent(hv, oentry); + if (SvOOK(hv)) { + /* If the hash is actually a symbol table with a name, look after the + name. */ + struct xpvhv_aux *iter = HvAUX(hv); + + name = iter->xhv_name; + iter->xhv_name = NULL; + } else { + name = NULL; + } + + orig_array = HvARRAY(hv); + /* orig_array remains unchanged throughout the loop. If after freeing all + the entries it turns out that one of the little blighters has triggered + an action that has caused HvARRAY to be re-allocated, then we set + array to the new HvARRAY, and try again. */ + + while (1) { + /* This is the one we're going to try to empty. First time round + it's the original array. (Hopefully there will only be 1 time + round) */ + HE ** const array = HvARRAY(hv); + I32 i = HvMAX(hv); + + /* Because we have taken xhv_name out, the only allocated pointer + in the aux structure that might exist is the backreference array. + */ + + if (SvOOK(hv)) { + HE *entry; + struct xpvhv_aux *iter = HvAUX(hv); + /* If there are weak references to this HV, we need to avoid + freeing them up here. In particular we need to keep the AV + visible as what we're deleting might well have weak references + back to this HV, so the for loop below may well trigger + the removal of backreferences from this array. */ + + if (iter->xhv_backreferences) { + /* So donate them to regular backref magic to keep them safe. + The sv_magic will increase the reference count of the AV, + so we need to drop it first. */ + SvREFCNT_dec(iter->xhv_backreferences); + if (AvFILLp(iter->xhv_backreferences) == -1) { + /* Turns out that the array is empty. Just free it. */ + SvREFCNT_dec(iter->xhv_backreferences); + + } else { + sv_magic((SV*)hv, (SV*)iter->xhv_backreferences, + PERL_MAGIC_backref, NULL, 0); + } + iter->xhv_backreferences = NULL; + } + + entry = iter->xhv_eiter; /* HvEITER(hv) */ + if (entry && HvLAZYDEL(hv)) { /* was deleted earlier? */ + HvLAZYDEL_off(hv); + hv_free_ent(hv, entry); + } + iter->xhv_riter = -1; /* HvRITER(hv) = -1 */ + iter->xhv_eiter = NULL; /* HvEITER(hv) = NULL */ + + /* There are now no allocated pointers in the aux structure. */ + + SvFLAGS(hv) &= ~SVf_OOK; /* Goodbye, aux structure. */ + /* What aux structure? */ + } + + /* make everyone else think the array is empty, so that the destructors + * called for freed entries can't recusively mess with us */ + HvARRAY(hv) = NULL; + HvFILL(hv) = 0; + ((XPVHV*) SvANY(hv))->xhv_keys = 0; + + + do { + /* Loop down the linked list heads */ + HE *entry = array[i]; + + while (entry) { + register HE * const oentry = entry; + entry = HeNEXT(entry); + hv_free_ent(hv, oentry); + } + } while (--i >= 0); + + /* As there are no allocated pointers in the aux structure, it's now + safe to free the array we just cleaned up, if it's not the one we're + going to put back. */ + if (array != orig_array) { + Safefree(array); + } + + if (!HvARRAY(hv)) { + /* Good. No-one added anything this time round. */ + break; + } + + if (SvOOK(hv)) { + /* Someone attempted to iterate or set the hash name while we had + the array set to 0. We'll catch backferences on the next time + round the while loop. */ + assert(HvARRAY(hv)); + + if (HvAUX(hv)->xhv_name) { + unshare_hek_or_pvn(HvAUX(hv)->xhv_name, 0, 0, 0); + } } - if (!entry) { - if (++riter > max) - break; - entry = array[riter]; + + if (--attempts == 0) { + Perl_die(aTHX_ "panic: hfreeentries failed to free hash - something is repeatedly re-creating entries"); } } - HvARRAY(hv) = array; - (void)hv_iterinit(hv); + + HvARRAY(hv) = orig_array; + + /* If the hash was actually a symbol table, put the name back. */ + if (name) { + /* We have restored the original array. If name is non-NULL, then + the original array had an aux structure at the end. So this is + valid: */ + SvFLAGS(hv) |= SVf_OOK; + HvAUX(hv)->xhv_name = name; + } } /* @@ -1927,26 +1841,55 @@ Undefines the hash. void Perl_hv_undef(pTHX_ HV *hv) { + dVAR; register XPVHV* xhv; + const char *name; + if (!hv) return; + DEBUG_A(Perl_hv_assert(aTHX_ hv)); xhv = (XPVHV*)SvANY(hv); hfreeentries(hv); - Safefree(xhv->xhv_array /* HvARRAY(hv) */); - if (HvNAME(hv)) { + if ((name = HvNAME_get(hv))) { if(PL_stashcache) - hv_delete(PL_stashcache, HvNAME(hv), strlen(HvNAME(hv)), G_DISCARD); - Safefree(HvNAME(hv)); - HvNAME(hv) = 0; + hv_delete(PL_stashcache, name, HvNAMELEN_get(hv), G_DISCARD); + hv_name_set(hv, NULL, 0, 0); } + SvFLAGS(hv) &= ~SVf_OOK; + Safefree(HvARRAY(hv)); xhv->xhv_max = 7; /* HvMAX(hv) = 7 (it's a normal hash) */ - xhv->xhv_array = 0; /* HvARRAY(hv) = 0 */ - xhv->xhv_placeholders = 0; /* HvPLACEHOLDERS(hv) = 0 */ + HvARRAY(hv) = 0; + HvPLACEHOLDERS_set(hv, 0); if (SvRMAGICAL(hv)) mg_clear((SV*)hv); } +static struct xpvhv_aux* +S_hv_auxinit(HV *hv) { + struct xpvhv_aux *iter; + char *array; + + if (!HvARRAY(hv)) { + Newxz(array, PERL_HV_ARRAY_ALLOC_BYTES(HvMAX(hv) + 1) + + sizeof(struct xpvhv_aux), char); + } else { + array = (char *) HvARRAY(hv); + Renew(array, PERL_HV_ARRAY_ALLOC_BYTES(HvMAX(hv) + 1) + + sizeof(struct xpvhv_aux), char); + } + HvARRAY(hv) = (HE**) array; + /* SvOOK_on(hv) attacks the IV flags. */ + SvFLAGS(hv) |= SVf_OOK; + iter = HvAUX(hv); + + iter->xhv_riter = -1; /* HvRITER(hv) = -1 */ + iter->xhv_eiter = NULL; /* HvEITER(hv) = NULL */ + iter->xhv_name = 0; + iter->xhv_backreferences = 0; + return iter; +} + /* =for apidoc hv_iterinit @@ -1954,34 +1897,149 @@ Prepares a starting point to traverse a hash table. Returns the number of keys in the hash (i.e. the same as C). The return value is currently only meaningful for hashes without tie magic. -NOTE: Before version 5.004_65, C used to return the number of -hash buckets that happen to be in use. If you still need that esoteric -value, you can get it through the macro C. +NOTE: Before version 5.004_65, C used to return the number of +hash buckets that happen to be in use. If you still need that esoteric +value, you can get it through the macro C. + + +=cut +*/ + +I32 +Perl_hv_iterinit(pTHX_ HV *hv) +{ + if (!hv) + Perl_croak(aTHX_ "Bad hash"); + + if (SvOOK(hv)) { + struct xpvhv_aux * const 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->xhv_riter = -1; /* HvRITER(hv) = -1 */ + iter->xhv_eiter = NULL; /* HvEITER(hv) = NULL */ + } else { + hv_auxinit(hv); + } + + /* used to be xhv->xhv_fill before 5.004_65 */ + return HvTOTALKEYS(hv); +} + +I32 * +Perl_hv_riter_p(pTHX_ HV *hv) { + struct xpvhv_aux *iter; + + if (!hv) + Perl_croak(aTHX_ "Bad hash"); + + iter = SvOOK(hv) ? HvAUX(hv) : hv_auxinit(hv); + return &(iter->xhv_riter); +} + +HE ** +Perl_hv_eiter_p(pTHX_ HV *hv) { + struct xpvhv_aux *iter; + + if (!hv) + Perl_croak(aTHX_ "Bad hash"); + + iter = SvOOK(hv) ? HvAUX(hv) : hv_auxinit(hv); + return &(iter->xhv_eiter); +} + +void +Perl_hv_riter_set(pTHX_ HV *hv, I32 riter) { + struct xpvhv_aux *iter; + + if (!hv) + Perl_croak(aTHX_ "Bad hash"); + + if (SvOOK(hv)) { + iter = HvAUX(hv); + } else { + if (riter == -1) + return; + + iter = hv_auxinit(hv); + } + iter->xhv_riter = riter; +} + +void +Perl_hv_eiter_set(pTHX_ HV *hv, HE *eiter) { + struct xpvhv_aux *iter; + + if (!hv) + Perl_croak(aTHX_ "Bad hash"); + + if (SvOOK(hv)) { + iter = HvAUX(hv); + } else { + /* 0 is the default so don't go malloc()ing a new structure just to + hold 0. */ + if (!eiter) + return; + + iter = hv_auxinit(hv); + } + iter->xhv_eiter = eiter; +} + +void +Perl_hv_name_set(pTHX_ HV *hv, const char *name, U32 len, U32 flags) +{ + dVAR; + struct xpvhv_aux *iter; + U32 hash; + + PERL_UNUSED_ARG(flags); + + if (len > I32_MAX) + Perl_croak(aTHX_ "panic: hv name too long (%"UVuf")", (UV) len); + + if (SvOOK(hv)) { + iter = HvAUX(hv); + if (iter->xhv_name) { + unshare_hek_or_pvn(iter->xhv_name, 0, 0, 0); + } + } else { + if (name == 0) + return; + + iter = hv_auxinit(hv); + } + PERL_HASH(hash, name, len); + iter->xhv_name = name ? share_hek(name, len, hash) : 0; +} +AV ** +Perl_hv_backreferences_p(pTHX_ HV *hv) { + struct xpvhv_aux * const iter = SvOOK(hv) ? HvAUX(hv) : hv_auxinit(hv); + PERL_UNUSED_CONTEXT; + return &(iter->xhv_backreferences); +} -=cut -*/ +void +Perl_hv_kill_backrefs(pTHX_ HV *hv) { + AV *av; -I32 -Perl_hv_iterinit(pTHX_ HV *hv) -{ - register XPVHV* xhv; - HE *entry; + if (!SvOOK(hv)) + return; - if (!hv) - Perl_croak(aTHX_ "Bad hash"); - xhv = (XPVHV*)SvANY(hv); - entry = xhv->xhv_eiter; /* HvEITER(hv) */ - if (entry && HvLAZYDEL(hv)) { /* was deleted earlier? */ - HvLAZYDEL_off(hv); - hv_free_ent(hv, entry); + av = HvAUX(hv)->xhv_backreferences; + + if (av) { + HvAUX(hv)->xhv_backreferences = 0; + Perl_sv_kill_backrefs(aTHX_ (SV*) hv, av); } - xhv->xhv_riter = -1; /* HvRITER(hv) = -1 */ - xhv->xhv_eiter = Null(HE*); /* HvEITER(hv) = Null(HE*) */ - /* used to be xhv->xhv_fill before 5.004_65 */ - return XHvTOTALKEYS(xhv); } + /* +hv_iternext is implemented as a macro in hv.h + =for apidoc hv_iternext Returns entries from a hash iterator. See C. @@ -1994,16 +2052,6 @@ to free the entry on the next call to C, so you must not discard your iterator immediately else the entry will leak - call C to trigger the resource deallocation. -=cut -*/ - -HE * -Perl_hv_iternext(pTHX_ HV *hv) -{ - return hv_iternext_flags(hv, 0); -} - -/* =for apidoc hv_iternext_flags Returns entries from a hash iterator. See C and C. @@ -2021,18 +2069,29 @@ insufficiently abstracted for any change to be tidy. HE * Perl_hv_iternext_flags(pTHX_ HV *hv, I32 flags) { + dVAR; register XPVHV* xhv; register HE *entry; HE *oldentry; MAGIC* mg; + struct xpvhv_aux *iter; if (!hv) Perl_croak(aTHX_ "Bad hash"); xhv = (XPVHV*)SvANY(hv); - oldentry = entry = xhv->xhv_eiter; /* HvEITER(hv) */ + + if (!SvOOK(hv)) { + /* Too many things (well, pp_each at least) merrily assume that you can + call iv_iternext without calling hv_iterinit, so we'll have to deal + with it. */ + hv_iterinit(hv); + } + iter = HvAUX(hv); + + oldentry = entry = iter->xhv_eiter; /* HvEITER(hv) */ if ((mg = SvTIED_mg((SV*)hv, PERL_MAGIC_tied))) { - SV *key = sv_newmortal(); + SV * const key = sv_newmortal(); if (entry) { sv_setsv(key, HeSVKEY_force(entry)); SvREFCNT_dec(HeSVKEY(entry)); /* get rid of previous key */ @@ -2042,9 +2101,9 @@ Perl_hv_iternext_flags(pTHX_ HV *hv, I32 flags) HEK *hek; /* one HE per MAGICAL hash */ - xhv->xhv_eiter = entry = new_HE(); /* HvEITER(hv) = new_HE() */ + iter->xhv_eiter = entry = new_HE(); /* HvEITER(hv) = new_HE() */ Zero(entry, 1, HE); - Newz(54, k, HEK_BASESIZE + sizeof(SV*), char); + Newxz(k, HEK_BASESIZE + sizeof(SV*), char); hek = (HEK*)k; HeKEY_hek(entry) = hek; HeKLEN(entry) = HEf_SVKEY; @@ -2052,25 +2111,33 @@ Perl_hv_iternext_flags(pTHX_ HV *hv, I32 flags) magic_nextpack((SV*) hv,mg,key); if (SvOK(key)) { /* force key to stay around until next time */ - HeSVKEY_set(entry, SvREFCNT_inc(key)); + HeSVKEY_set(entry, SvREFCNT_inc_simple_NN(key)); return entry; /* beware, hent_val is not set */ } if (HeVAL(entry)) SvREFCNT_dec(HeVAL(entry)); Safefree(HeKEY_hek(entry)); del_HE(entry); - xhv->xhv_eiter = Null(HE*); /* HvEITER(hv) = Null(HE*) */ - return Null(HE*); + iter->xhv_eiter = NULL; /* HvEITER(hv) = NULL */ + return NULL; } -#ifdef DYNAMIC_ENV_FETCH /* set up %ENV for iteration */ - if (!entry && SvRMAGICAL((SV*)hv) && mg_find((SV*)hv, PERL_MAGIC_env)) +#if defined(DYNAMIC_ENV_FETCH) && !defined(__riscos__) /* set up %ENV for iteration */ + if (!entry && SvRMAGICAL((SV*)hv) && mg_find((SV*)hv, PERL_MAGIC_env)) { prime_env_iter(); +#ifdef VMS + /* The prime_env_iter() on VMS just loaded up new hash values + * so the iteration count needs to be reset back to the beginning + */ + hv_iterinit(hv); + iter = HvAUX(hv); + oldentry = entry = iter->xhv_eiter; /* HvEITER(hv) */ +#endif + } #endif - if (!xhv->xhv_array /* !HvARRAY(hv) */) - Newz(506, xhv->xhv_array /* HvARRAY(hv) */, - PERL_HV_ARRAY_ALLOC_BYTES(xhv->xhv_max+1 /* HvMAX(hv)+1 */), - char); + /* hv_iterint now ensures this. */ + assert (HvARRAY(hv)); + /* At start of hash, entry is NULL. */ if (entry) { @@ -2088,14 +2155,13 @@ Perl_hv_iternext_flags(pTHX_ HV *hv, I32 flags) while (!entry) { /* OK. Come to the end of the current list. Grab the next one. */ - xhv->xhv_riter++; /* HvRITER(hv)++ */ - if (xhv->xhv_riter > (I32)xhv->xhv_max /* HvRITER(hv) > HvMAX(hv) */) { + iter->xhv_riter++; /* HvRITER(hv)++ */ + if (iter->xhv_riter > (I32)xhv->xhv_max /* HvRITER(hv) > HvMAX(hv) */) { /* There is no next one. End of the hash. */ - xhv->xhv_riter = -1; /* HvRITER(hv) = -1 */ + iter->xhv_riter = -1; /* HvRITER(hv) = -1 */ break; } - /* entry = (HvARRAY(hv))[HvRITER(hv)]; */ - entry = ((HE**)xhv->xhv_array)[xhv->xhv_riter]; + entry = (HvARRAY(hv))[iter->xhv_riter]; if (!(flags & HV_ITERNEXT_WANTPLACEHOLDERS)) { /* If we have an entry, but it's a placeholder, don't count it. @@ -2113,7 +2179,10 @@ Perl_hv_iternext_flags(pTHX_ HV *hv, I32 flags) hv_free_ent(hv, oldentry); } - xhv->xhv_eiter = entry; /* HvEITER(hv) = entry */ + /*if (HvREHASH(hv) && entry && !HeKREHASH(entry)) + PerlIO_printf(PerlIO_stderr(), "Awooga %p %p\n", hv, entry);*/ + + iter->xhv_eiter = entry; /* HvEITER(hv) = entry */ return entry; } @@ -2131,7 +2200,7 @@ Perl_hv_iterkey(pTHX_ register HE *entry, I32 *retlen) { if (HeKLEN(entry) == HEf_SVKEY) { STRLEN len; - char *p = SvPV(HeKEY_sv(entry), len); + char * const p = SvPV(HeKEY_sv(entry), len); *retlen = len; return p; } @@ -2155,39 +2224,7 @@ see C. SV * Perl_hv_iterkeysv(pTHX_ register HE *entry) { - if (HeKLEN(entry) != HEf_SVKEY) { - HEK *hek = HeKEY_hek(entry); - int flags = HEK_FLAGS(hek); - SV *sv; - - if (flags & HVhek_WASUTF8) { - /* Trouble :-) - Andreas would like keys he put in as utf8 to come back as utf8 - */ - STRLEN utf8_len = HEK_LEN(hek); - U8 *as_utf8 = bytes_to_utf8 ((U8*)HEK_KEY(hek), &utf8_len); - - sv = newSVpvn ((char*)as_utf8, utf8_len); - SvUTF8_on (sv); - Safefree (as_utf8); /* bytes_to_utf8() allocates a new string */ - } else if (flags & HVhek_REHASH) { - /* We don't have a pointer to the hv, so we have to replicate the - flag into every HEK. This hv is using custom a hasing - algorithm. Hence we can't return a shared string scalar, as - that would contain the (wrong) hash value, and might get passed - into an hv routine with a regular hash */ - - sv = newSVpvn (HEK_KEY(hek), HEK_LEN(hek)); - if (HEK_UTF8(hek)) - SvUTF8_on (sv); - } else { - sv = newSVpvn_share(HEK_KEY(hek), - (HEK_UTF8(hek) ? -HEK_LEN(hek) : HEK_LEN(hek)), - HEK_HASH(hek)); - } - return sv_2mortal(sv); - } - return sv_mortalcopy(HeKEY_sv(entry)); + return sv_2mortal(newSVhek(HeKEY_hek(entry))); } /* @@ -2204,10 +2241,11 @@ Perl_hv_iterval(pTHX_ HV *hv, register HE *entry) { if (SvRMAGICAL(hv)) { if (mg_find((SV*)hv, PERL_MAGIC_tied)) { - SV* sv = sv_newmortal(); + SV* const sv = sv_newmortal(); if (HeKLEN(entry) == HEf_SVKEY) mg_copy((SV*)hv, sv, (char*)HeKEY_sv(entry), HEf_SVKEY); - else mg_copy((SV*)hv, sv, HeKEY(entry), HeKLEN(entry)); + else + mg_copy((SV*)hv, sv, HeKEY(entry), HeKLEN(entry)); return sv; } } @@ -2226,14 +2264,18 @@ operation. SV * Perl_hv_iternextsv(pTHX_ HV *hv, char **key, I32 *retlen) { - HE *he; - if ( (he = hv_iternext_flags(hv, 0)) == NULL) + HE * const he = hv_iternext_flags(hv, 0); + + if (!he) return NULL; *key = hv_iterkey(he, retlen); return hv_iterval(hv, he); } /* + +Now a macro in hv.h + =for apidoc hv_magic Adds magic to a hash. See C. @@ -2241,22 +2283,6 @@ Adds magic to a hash. See C. =cut */ -void -Perl_hv_magic(pTHX_ HV *hv, GV *gv, int how) -{ - sv_magic((SV*)hv, (SV*)gv, how, Nullch, 0); -} - -#if 0 /* use the macro from hv.h instead */ - -char* -Perl_sharepvn(pTHX_ const char *sv, I32 len, U32 hash) -{ - return HEK_KEY(share_hek(sv, len, hash)); -} - -#endif - /* possibly free a shared string if no one has access to it * len and hash must both be valid for str. */ @@ -2278,18 +2304,36 @@ Perl_unshare_hek(pTHX_ HEK *hek) are used. If so, len and hash must both be valid for str. */ STATIC void -S_unshare_hek_or_pvn(pTHX_ HEK *hek, const char *str, I32 len, U32 hash) +S_unshare_hek_or_pvn(pTHX_ const HEK *hek, const char *str, I32 len, U32 hash) { + dVAR; register XPVHV* xhv; - register HE *entry; + HE *entry; register HE **oentry; - register I32 i = 1; - I32 found = 0; + HE **first; bool is_utf8 = FALSE; int k_flags = 0; - const char *save = str; + const char * const save = str; + struct shared_he *he = NULL; if (hek) { + /* Find the shared he which is just before us in memory. */ + he = (struct shared_he *)(((char *)hek) + - STRUCT_OFFSET(struct shared_he, + shared_he_hek)); + + /* Assert that the caller passed us a genuine (or at least consistent) + shared hek */ + assert (he->shared_he_he.hent_hek == hek); + + LOCK_STRTAB_MUTEX; + if (he->shared_he_he.he_valu.hent_refcount - 1) { + --he->shared_he_he.he_valu.hent_refcount; + UNLOCK_STRTAB_MUTEX; + return; + } + UNLOCK_STRTAB_MUTEX; + hash = HEK_HASH(hek); } else if (len < 0) { STRLEN tmplen = -len; @@ -2303,26 +2347,24 @@ S_unshare_hek_or_pvn(pTHX_ HEK *hek, const char *str, I32 len, U32 hash) k_flags |= HVhek_WASUTF8 | HVhek_FREEKEY; } - /* what follows is the moral equivalent of: + /* what follows was the moral equivalent of: if ((Svp = hv_fetch(PL_strtab, tmpsv, FALSE, hash))) { - if (--*Svp == Nullsv) + if (--*Svp == NULL) hv_delete(PL_strtab, str, len, G_DISCARD, hash); } */ xhv = (XPVHV*)SvANY(PL_strtab); /* assert(xhv_array != 0) */ LOCK_STRTAB_MUTEX; - /* oentry = &(HvARRAY(hv))[hash & (I32) HvMAX(hv)]; */ - oentry = &((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max]; - if (hek) { - for (entry = *oentry; entry; i=0, oentry = &HeNEXT(entry), entry = *oentry) { - if (HeKEY_hek(entry) != hek) - continue; - found = 1; - break; + first = oentry = &(HvARRAY(PL_strtab))[hash & (I32) HvMAX(PL_strtab)]; + if (he) { + const HE *const he_he = &(he->shared_he_he); + for (entry = *oentry; entry; oentry = &HeNEXT(entry), entry = *oentry) { + if (entry == he_he) + break; } } else { - int flags_masked = k_flags & HVhek_MASK; - for (entry = *oentry; entry; i=0, oentry = &HeNEXT(entry), entry = *oentry) { + const int flags_masked = k_flags & HVhek_MASK; + for (entry = *oentry; entry; oentry = &HeNEXT(entry), entry = *oentry) { if (HeHASH(entry) != hash) /* strings can't be equal */ continue; if (HeKLEN(entry) != len) @@ -2331,28 +2373,29 @@ S_unshare_hek_or_pvn(pTHX_ HEK *hek, const char *str, I32 len, U32 hash) continue; if (HeKFLAGS(entry) != flags_masked) continue; - found = 1; break; } } - if (found) { - if (--HeVAL(entry) == Nullsv) { + if (entry) { + if (--entry->he_valu.hent_refcount == 0) { *oentry = HeNEXT(entry); - if (i && !*oentry) + if (!*first) { + /* There are now no entries in our slot. */ xhv->xhv_fill--; /* HvFILL(hv)-- */ - Safefree(HeKEY_hek(entry)); - del_HE(entry); - xhv->xhv_keys--; /* HvKEYS(hv)-- */ + } + Safefree(entry); + xhv->xhv_keys--; /* HvTOTALKEYS(hv)-- */ } } UNLOCK_STRTAB_MUTEX; - if (!found && ckWARN_d(WARN_INTERNAL)) + if (!entry && ckWARN_d(WARN_INTERNAL)) Perl_warner(aTHX_ packWARN(WARN_INTERNAL), - "Attempt to free non-existent shared string '%s'%s", + "Attempt to free non-existent shared string '%s'%s" + pTHX__FORMAT, hek ? HEK_KEY(hek) : str, - (k_flags & HVhek_UTF8) ? " (utf8)" : ""); + ((k_flags & HVhek_UTF8) ? " (utf8)" : "") pTHX__VALUE); if (k_flags & HVhek_FREEKEY) Safefree(str); } @@ -2366,7 +2409,7 @@ Perl_share_hek(pTHX_ const char *str, I32 len, register U32 hash) { bool is_utf8 = FALSE; int flags = 0; - const char *save = str; + const char * const save = str; if (len < 0) { STRLEN tmplen = -len; @@ -2391,27 +2434,24 @@ Perl_share_hek(pTHX_ const char *str, I32 len, register U32 hash) STATIC HEK * S_share_hek_flags(pTHX_ const char *str, I32 len, register U32 hash, int flags) { - register XPVHV* xhv; + dVAR; register HE *entry; - register HE **oentry; - register I32 i = 1; - I32 found = 0; - int flags_masked = flags & HVhek_MASK; + const int flags_masked = flags & HVhek_MASK; + const U32 hindex = hash & (I32) HvMAX(PL_strtab); /* what follows is the moral equivalent of: if (!(Svp = hv_fetch(PL_strtab, str, len, FALSE))) - hv_store(PL_strtab, str, len, Nullsv, hash); + hv_store(PL_strtab, str, len, NULL, hash); Can't rehash the shared string table, so not sure if it's worth counting the number of entries in the linked list */ - xhv = (XPVHV*)SvANY(PL_strtab); + register XPVHV * const xhv = (XPVHV*)SvANY(PL_strtab); /* assert(xhv_array != 0) */ LOCK_STRTAB_MUTEX; - /* oentry = &(HvARRAY(hv))[hash & (I32) HvMAX(hv)]; */ - oentry = &((HE**)xhv->xhv_array)[hash & (I32) xhv->xhv_max]; - for (entry = *oentry; entry; i=0, entry = HeNEXT(entry)) { + entry = (HvARRAY(PL_strtab))[hindex]; + for (;entry; entry = HeNEXT(entry)) { if (HeHASH(entry) != hash) /* strings can't be equal */ continue; if (HeKLEN(entry) != len) @@ -2420,24 +2460,53 @@ S_share_hek_flags(pTHX_ const char *str, I32 len, register U32 hash, int flags) continue; if (HeKFLAGS(entry) != flags_masked) continue; - found = 1; break; } - if (!found) { - entry = new_HE(); - HeKEY_hek(entry) = save_hek_flags(str, len, hash, flags); - HeVAL(entry) = Nullsv; - HeNEXT(entry) = *oentry; - *oentry = entry; - xhv->xhv_keys++; /* HvKEYS(hv)++ */ - if (i) { /* initial entry? */ + + if (!entry) { + /* What used to be head of the list. + If this is NULL, then we're the first entry for this slot, which + means we need to increate fill. */ + struct shared_he *new_entry; + HEK *hek; + char *k; + HE **const head = &HvARRAY(PL_strtab)[hindex]; + HE *const next = *head; + + /* We don't actually store a HE from the arena and a regular HEK. + Instead we allocate one chunk of memory big enough for both, + and put the HEK straight after the HE. This way we can find the + HEK directly from the HE. + */ + + Newx(k, STRUCT_OFFSET(struct shared_he, + shared_he_hek.hek_key[0]) + len + 2, char); + new_entry = (struct shared_he *)k; + entry = &(new_entry->shared_he_he); + hek = &(new_entry->shared_he_hek); + + Copy(str, HEK_KEY(hek), len, char); + HEK_KEY(hek)[len] = 0; + HEK_LEN(hek) = len; + HEK_HASH(hek) = hash; + HEK_FLAGS(hek) = (unsigned char)flags_masked; + + /* Still "point" to the HEK, so that other code need not know what + we're up to. */ + HeKEY_hek(entry) = hek; + entry->he_valu.hent_refcount = 0; + HeNEXT(entry) = next; + *head = entry; + + xhv->xhv_keys++; /* HvTOTALKEYS(hv)++ */ + if (!next) { /* initial entry? */ xhv->xhv_fill++; /* HvFILL(hv)++ */ } else if (xhv->xhv_keys > (IV)xhv->xhv_max /* HvKEYS(hv) > HvMAX(hv) */) { hsplit(PL_strtab); } } - ++HeVAL(entry); /* use value slot as REFCNT */ + ++entry->he_valu.hent_refcount; UNLOCK_STRTAB_MUTEX; if (flags & HVhek_FREEKEY) @@ -2445,3 +2514,494 @@ S_share_hek_flags(pTHX_ const char *str, I32 len, register U32 hash, int flags) return HeKEY_hek(entry); } + +STATIC SV * +S_hv_magic_uvar_xkey(pTHX_ HV* hv, SV* keysv, int action) +{ + MAGIC* mg; + if ((mg = mg_find((SV*)hv, PERL_MAGIC_uvar))) { + struct ufuncs * const uf = (struct ufuncs *)mg->mg_ptr; + if (uf->uf_set == NULL) { + SV* obj = mg->mg_obj; + mg->mg_obj = keysv; /* pass key */ + uf->uf_index = action; /* pass action */ + magic_getuvar((SV*)hv, mg); + keysv = mg->mg_obj; /* may have changed */ + mg->mg_obj = obj; + } + } + return keysv; +} + +I32 * +Perl_hv_placeholders_p(pTHX_ HV *hv) +{ + dVAR; + MAGIC *mg = mg_find((SV*)hv, PERL_MAGIC_rhash); + + if (!mg) { + mg = sv_magicext((SV*)hv, 0, PERL_MAGIC_rhash, 0, 0, 0); + + if (!mg) { + Perl_die(aTHX_ "panic: hv_placeholders_p"); + } + } + return &(mg->mg_len); +} + + +I32 +Perl_hv_placeholders_get(pTHX_ HV *hv) +{ + dVAR; + MAGIC * const mg = mg_find((SV*)hv, PERL_MAGIC_rhash); + + return mg ? mg->mg_len : 0; +} + +void +Perl_hv_placeholders_set(pTHX_ HV *hv, I32 ph) +{ + dVAR; + MAGIC * const mg = mg_find((SV*)hv, PERL_MAGIC_rhash); + + if (mg) { + mg->mg_len = ph; + } else if (ph) { + if (!sv_magicext((SV*)hv, 0, PERL_MAGIC_rhash, 0, 0, ph)) + Perl_die(aTHX_ "panic: hv_placeholders_set"); + } + /* else we don't need to add magic to record 0 placeholders. */ +} + +SV * +S_refcounted_he_value(pTHX_ const struct refcounted_he *he) +{ + dVAR; + SV *value; + switch(he->refcounted_he_data[0] & HVrhek_typemask) { + case HVrhek_undef: + value = newSV(0); + break; + case HVrhek_delete: + value = &PL_sv_placeholder; + break; + case HVrhek_IV: + value = (he->refcounted_he_data[0] & HVrhek_UV) + ? newSVuv(he->refcounted_he_val.refcounted_he_u_iv) + : newSViv(he->refcounted_he_val.refcounted_he_u_uv); + break; + case HVrhek_PV: + /* Create a string SV that directly points to the bytes in our + structure. */ + value = newSV(0); + sv_upgrade(value, SVt_PV); + SvPV_set(value, (char *) he->refcounted_he_data + 1); + SvCUR_set(value, he->refcounted_he_val.refcounted_he_u_len); + /* This stops anything trying to free it */ + SvLEN_set(value, 0); + SvPOK_on(value); + SvREADONLY_on(value); + if (he->refcounted_he_data[0] & HVrhek_UTF8) + SvUTF8_on(value); + break; + default: + Perl_croak(aTHX_ "panic: refcounted_he_value bad flags %x", + he->refcounted_he_data[0]); + } + return value; +} + +#ifdef USE_ITHREADS +/* A big expression to find the key offset */ +#define REF_HE_KEY(chain) \ + ((((chain->refcounted_he_data[0] & HVrhek_typemask) == HVrhek_PV) \ + ? chain->refcounted_he_val.refcounted_he_u_len + 1 : 0) \ + + 1 + chain->refcounted_he_data) +#endif + +/* +=for apidoc refcounted_he_chain_2hv + +Generates an returns a C by walking up the tree starting at the passed +in C. + +=cut +*/ +HV * +Perl_refcounted_he_chain_2hv(pTHX_ const struct refcounted_he *chain) +{ + dVAR; + HV *hv = newHV(); + U32 placeholders = 0; + /* We could chase the chain once to get an idea of the number of keys, + and call ksplit. But for now we'll make a potentially inefficient + hash with only 8 entries in its array. */ + const U32 max = HvMAX(hv); + + if (!HvARRAY(hv)) { + char *array; + Newxz(array, PERL_HV_ARRAY_ALLOC_BYTES(max + 1), char); + HvARRAY(hv) = (HE**)array; + } + + while (chain) { +#ifdef USE_ITHREADS + U32 hash = chain->refcounted_he_hash; +#else + U32 hash = HEK_HASH(chain->refcounted_he_hek); +#endif + HE **oentry = &((HvARRAY(hv))[hash & max]); + HE *entry = *oentry; + SV *value; + + for (; entry; entry = HeNEXT(entry)) { + if (HeHASH(entry) == hash) { + /* We might have a duplicate key here. If so, entry is older + than the key we've already put in the hash, so if they are + the same, skip adding entry. */ +#ifdef USE_ITHREADS + const STRLEN klen = HeKLEN(entry); + const char *const key = HeKEY(entry); + if (klen == chain->refcounted_he_keylen + && (!!HeKUTF8(entry) + == !!(chain->refcounted_he_data[0] & HVhek_UTF8)) + && memEQ(key, REF_HE_KEY(chain), klen)) + goto next_please; +#else + if (HeKEY_hek(entry) == chain->refcounted_he_hek) + goto next_please; + if (HeKLEN(entry) == HEK_LEN(chain->refcounted_he_hek) + && HeKUTF8(entry) == HEK_UTF8(chain->refcounted_he_hek) + && memEQ(HeKEY(entry), HEK_KEY(chain->refcounted_he_hek), + HeKLEN(entry))) + goto next_please; +#endif + } + } + assert (!entry); + entry = new_HE(); + +#ifdef USE_ITHREADS + HeKEY_hek(entry) + = share_hek_flags(REF_HE_KEY(chain), + chain->refcounted_he_keylen, + chain->refcounted_he_hash, + (chain->refcounted_he_data[0] + & (HVhek_UTF8|HVhek_WASUTF8))); +#else + HeKEY_hek(entry) = share_hek_hek(chain->refcounted_he_hek); +#endif + value = refcounted_he_value(chain); + if (value == &PL_sv_placeholder) + placeholders++; + HeVAL(entry) = value; + + /* Link it into the chain. */ + HeNEXT(entry) = *oentry; + if (!HeNEXT(entry)) { + /* initial entry. */ + HvFILL(hv)++; + } + *oentry = entry; + + HvTOTALKEYS(hv)++; + + next_please: + chain = chain->refcounted_he_next; + } + + if (placeholders) { + clear_placeholders(hv, placeholders); + HvTOTALKEYS(hv) -= placeholders; + } + + /* We could check in the loop to see if we encounter any keys with key + flags, but it's probably not worth it, as this per-hash flag is only + really meant as an optimisation for things like Storable. */ + HvHASKFLAGS_on(hv); + DEBUG_A(Perl_hv_assert(aTHX_ hv)); + + return hv; +} + +SV * +Perl_refcounted_he_fetch(pTHX_ const struct refcounted_he *chain, SV *keysv, + const char *key, STRLEN klen, int flags, U32 hash) +{ + dVAR; + /* Just to be awkward, if you're using this interface the UTF-8-or-not-ness + of your key has to exactly match that which is stored. */ + SV *value = &PL_sv_placeholder; + bool is_utf8; + + if (keysv) { + if (flags & HVhek_FREEKEY) + Safefree(key); + key = SvPV_const(keysv, klen); + flags = 0; + is_utf8 = (SvUTF8(keysv) != 0); + } else { + is_utf8 = ((flags & HVhek_UTF8) ? TRUE : FALSE); + } + + if (!hash) { + if (keysv && (SvIsCOW_shared_hash(keysv))) { + hash = SvSHARED_HASH(keysv); + } else { + PERL_HASH(hash, key, klen); + } + } + + for (; chain; chain = chain->refcounted_he_next) { +#ifdef USE_ITHREADS + if (hash != chain->refcounted_he_hash) + continue; + if (klen != chain->refcounted_he_keylen) + continue; + if (memNE(REF_HE_KEY(chain),key,klen)) + continue; + if (!!is_utf8 != !!(chain->refcounted_he_data[0] & HVhek_UTF8)) + continue; +#else + if (hash != HEK_HASH(chain->refcounted_he_hek)) + continue; + if (klen != (STRLEN)HEK_LEN(chain->refcounted_he_hek)) + continue; + if (memNE(HEK_KEY(chain->refcounted_he_hek),key,klen)) + continue; + if (!!is_utf8 != !!HEK_UTF8(chain->refcounted_he_hek)) + continue; +#endif + + value = sv_2mortal(refcounted_he_value(chain)); + break; + } + + if (flags & HVhek_FREEKEY) + Safefree(key); + + return value; +} + +/* +=for apidoc refcounted_he_new + +Creates a new C. As S is copied, and value is +stored in a compact form, all references remain the property of the caller. +The C is returned with a reference count of 1. + +=cut +*/ + +struct refcounted_he * +Perl_refcounted_he_new(pTHX_ struct refcounted_he *const parent, + SV *const key, SV *const value) { + dVAR; + struct refcounted_he *he; + STRLEN key_len; + const char *key_p = SvPV_const(key, key_len); + STRLEN value_len = 0; + const char *value_p = NULL; + char value_type; + char flags; + STRLEN key_offset; + U32 hash; + bool is_utf8 = SvUTF8(key) ? TRUE : FALSE; + + if (SvPOK(value)) { + value_type = HVrhek_PV; + } else if (SvIOK(value)) { + value_type = HVrhek_IV; + } else if (value == &PL_sv_placeholder) { + value_type = HVrhek_delete; + } else if (!SvOK(value)) { + value_type = HVrhek_undef; + } else { + value_type = HVrhek_PV; + } + + if (value_type == HVrhek_PV) { + value_p = SvPV_const(value, value_len); + key_offset = value_len + 2; + } else { + value_len = 0; + key_offset = 1; + } + flags = value_type; + +#ifdef USE_ITHREADS + he = (struct refcounted_he*) + PerlMemShared_malloc(sizeof(struct refcounted_he) - 1 + + key_len + + key_offset); +#else + he = (struct refcounted_he*) + PerlMemShared_malloc(sizeof(struct refcounted_he) - 1 + + key_offset); +#endif + + + he->refcounted_he_next = parent; + + if (value_type == HVrhek_PV) { + Copy(value_p, he->refcounted_he_data + 1, value_len + 1, char); + he->refcounted_he_val.refcounted_he_u_len = value_len; + if (SvUTF8(value)) { + flags |= HVrhek_UTF8; + } + } else if (value_type == HVrhek_IV) { + if (SvUOK(value)) { + he->refcounted_he_val.refcounted_he_u_uv = SvUVX(value); + flags |= HVrhek_UV; + } else { + he->refcounted_he_val.refcounted_he_u_iv = SvIVX(value); + } + } + + if (is_utf8) { + /* Hash keys are always stored normalised to (yes) ISO-8859-1. + As we're going to be building hash keys from this value in future, + normalise it now. */ + key_p = (char*)bytes_from_utf8((const U8*)key_p, &key_len, &is_utf8); + flags |= is_utf8 ? HVhek_UTF8 : HVhek_WASUTF8; + } + PERL_HASH(hash, key_p, key_len); + +#ifdef USE_ITHREADS + he->refcounted_he_hash = hash; + he->refcounted_he_keylen = key_len; + Copy(key_p, he->refcounted_he_data + key_offset, key_len, char); +#else + he->refcounted_he_hek = share_hek_flags(key_p, key_len, hash, flags); +#endif + + if (flags & HVhek_WASUTF8) { + /* If it was downgraded from UTF-8, then the pointer returned from + bytes_from_utf8 is an allocated pointer that we must free. */ + Safefree(key_p); + } + + he->refcounted_he_data[0] = flags; + he->refcounted_he_refcnt = 1; + + return he; +} + +/* +=for apidoc refcounted_he_free + +Decrements the reference count of the passed in C +by one. If the reference count reaches zero the structure's memory is freed, +and C iterates onto the parent node. + +=cut +*/ + +void +Perl_refcounted_he_free(pTHX_ struct refcounted_he *he) { + PERL_UNUSED_CONTEXT; + + while (he) { + struct refcounted_he *copy; + U32 new_count; + + HINTS_REFCNT_LOCK; + new_count = --he->refcounted_he_refcnt; + HINTS_REFCNT_UNLOCK; + + if (new_count) { + return; + } + +#ifndef USE_ITHREADS + unshare_hek_or_pvn (he->refcounted_he_hek, 0, 0, 0); +#endif + copy = he; + he = he->refcounted_he_next; + PerlMemShared_free(copy); + } +} + +/* +=for apidoc hv_assert + +Check that a hash is in an internally consistent state. + +=cut +*/ + +#ifdef DEBUGGING + +void +Perl_hv_assert(pTHX_ HV *hv) +{ + dVAR; + HE* entry; + int withflags = 0; + int placeholders = 0; + int real = 0; + int bad = 0; + const I32 riter = HvRITER_get(hv); + HE *eiter = HvEITER_get(hv); + + (void)hv_iterinit(hv); + + while ((entry = hv_iternext_flags(hv, HV_ITERNEXT_WANTPLACEHOLDERS))) { + /* sanity check the values */ + if (HeVAL(entry) == &PL_sv_placeholder) + placeholders++; + else + real++; + /* sanity check the keys */ + if (HeSVKEY(entry)) { + NOOP; /* Don't know what to check on SV keys. */ + } else if (HeKUTF8(entry)) { + withflags++; + if (HeKWASUTF8(entry)) { + PerlIO_printf(Perl_debug_log, + "hash key has both WASUFT8 and UTF8: '%.*s'\n", + (int) HeKLEN(entry), HeKEY(entry)); + bad = 1; + } + } else if (HeKWASUTF8(entry)) + withflags++; + } + if (!SvTIED_mg((SV*)hv, PERL_MAGIC_tied)) { + static const char bad_count[] = "Count %d %s(s), but hash reports %d\n"; + const int nhashkeys = HvUSEDKEYS(hv); + const int nhashplaceholders = HvPLACEHOLDERS_get(hv); + + if (nhashkeys != real) { + PerlIO_printf(Perl_debug_log, bad_count, real, "keys", nhashkeys ); + bad = 1; + } + if (nhashplaceholders != placeholders) { + PerlIO_printf(Perl_debug_log, bad_count, placeholders, "placeholder", nhashplaceholders ); + bad = 1; + } + } + if (withflags && ! HvHASKFLAGS(hv)) { + PerlIO_printf(Perl_debug_log, + "Hash has HASKFLAGS off but I count %d key(s) with flags\n", + withflags); + bad = 1; + } + if (bad) { + sv_dump((SV *)hv); + } + HvRITER_set(hv, riter); /* Restore hash iterator state */ + HvEITER_set(hv, eiter); +} + +#endif + +/* + * Local variables: + * c-indentation-style: bsd + * c-basic-offset: 4 + * indent-tabs-mode: t + * End: + * + * ex: set ts=8 sts=4 sw=4 noet: + */