X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=hv.c;h=a5884e52e2550ebdb6e96da9f0a37cb0d152a6a6;hb=f052c252aae9b0ee4c2e0248b24590df50414a11;hp=d3bb914653dfb9c610b4f17ef6487406327a03cb;hpb=a8fc9800e47fd3c23e88282f4505c051278ccc9b;p=p5sagit%2Fp5-mst-13.2.git diff --git a/hv.c b/hv.c index d3bb914..a5884e5 100644 --- a/hv.c +++ b/hv.c @@ -21,7 +21,7 @@ S_new_he(pTHX) HE* he; LOCK_SV_MUTEX; if (!PL_he_root) - more_he(); + more_he(); he = PL_he_root; PL_he_root = HeNEXT(he); UNLOCK_SV_MUTEX; @@ -51,8 +51,8 @@ S_more_he(pTHX) heend = &he[1008 / sizeof(HE) - 1]; PL_he_root = ++he; while (he < heend) { - HeNEXT(he) = (HE*)(he + 1); - he++; + HeNEXT(he) = (HE*)(he + 1); + he++; } HeNEXT(he) = 0; } @@ -126,6 +126,25 @@ Perl_he_dup(pTHX_ HE *e, bool shared, CLONE_PARAMS* param) } #endif /* USE_ITHREADS */ +static void +Perl_hv_notallowed(pTHX_ bool is_utf8, const char *key, I32 klen, + const char *keysave) +{ + SV *sv = sv_newmortal(); + if (key == keysave) { + sv_setpvn(sv, key, klen); + } + else { + /* Need to free saved eventually assign to mortal SV */ + SV *sv = sv_newmortal(); + sv_usepvn(sv, (char *) key, klen); + } + if (is_utf8) { + SvUTF8_on(sv); + } + Perl_croak(aTHX_ "Attempt to access to key '%_' in fixed hash",sv); +} + /* (klen == HEf_SVKEY) is special for MAGICAL hv entries, meaning key slot * contains an SV* */ @@ -135,7 +154,7 @@ Perl_he_dup(pTHX_ HE *e, bool shared, CLONE_PARAMS* param) 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 a C. +dereferencing it to an C. See L for more information on how to use this function on tied hashes. @@ -189,9 +208,9 @@ Perl_hv_fetch(pTHX_ HV *hv, const char *key, I32 klen, I32 lval) 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)) + || (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); @@ -222,7 +241,11 @@ Perl_hv_fetch(pTHX_ HV *hv, const char *key, I32 klen, I32 lval) continue; if (key != keysave) Safefree(key); + /* if we find a placeholder, we pretend we haven't found anything */ + if (HeVAL(entry) == &PL_sv_undef) + break; return &HeVAL(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)) { @@ -237,6 +260,9 @@ Perl_hv_fetch(pTHX_ HV *hv, const char *key, I32 klen, I32 lval) } } #endif + if (!entry && SvREADONLY(hv)) { + Perl_hv_notallowed(aTHX_ is_utf8, key, klen, keysave); + } if (lval) { /* gonna assign to this, so it better be there */ sv = NEWSV(61,0); if (key != keysave) { /* must be is_utf8 == 0 */ @@ -252,7 +278,7 @@ Perl_hv_fetch(pTHX_ HV *hv, const char *key, I32 klen, I32 lval) return 0; } -/* returns a HE * structure with the all fields set */ +/* returns an HE * structure with the all fields set */ /* note that hent_val will be a mortal sv for MAGICAL hashes */ /* =for apidoc hv_fetch_ent @@ -320,9 +346,9 @@ Perl_hv_fetch_ent(pTHX_ HV *hv, SV *keysv, I32 lval, register U32 hash) 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)) + || (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); @@ -352,6 +378,9 @@ Perl_hv_fetch_ent(pTHX_ HV *hv, SV *keysv, I32 lval, register U32 hash) continue; if (key != keysave) Safefree(key); + /* if we find a placeholder, we pretend we haven't found anything */ + if (HeVAL(entry) == &PL_sv_undef) + break; return entry; } #ifdef DYNAMIC_ENV_FETCH /* %ENV lookup? If so, try to fetch the value now */ @@ -365,6 +394,9 @@ Perl_hv_fetch_ent(pTHX_ HV *hv, SV *keysv, I32 lval, register U32 hash) } } #endif + if (!entry && SvREADONLY(hv)) { + Perl_hv_notallowed(aTHX_ is_utf8, key, klen, keysave); + } if (key != keysave) Safefree(key); if (lval) { /* gonna assign to this, so it better be there */ @@ -440,7 +472,7 @@ Perl_hv_store(pTHX_ HV *hv, const char *key, I32 klen, SV *val, register U32 has return 0; #ifdef ENV_IS_CASELESS else if (mg_find((SV*)hv, PERL_MAGIC_env)) { - key = savepvn(key,klen); + key = savepvn(key,klen); key = (const char*)strupr((char*)key); hash = 0; } @@ -475,13 +507,20 @@ Perl_hv_store(pTHX_ HV *hv, const char *key, I32 klen, SV *val, register U32 has continue; if (HeKUTF8(entry) != (char)is_utf8) continue; - SvREFCNT_dec(HeVAL(entry)); + if (HeVAL(entry) == &PL_sv_undef) + xhv->xhv_placeholders--; /* yes, can store into placeholder slot */ + else + SvREFCNT_dec(HeVAL(entry)); HeVAL(entry) = val; if (key != keysave) Safefree(key); return &HeVAL(entry); } + if (SvREADONLY(hv)) { + Perl_hv_notallowed(aTHX_ is_utf8, key, klen, keysave); + } + entry = new_HE(); if (HvSHAREKEYS(hv)) HeKEY_hek(entry) = share_hek(key, is_utf8?-klen:klen, hash); @@ -539,18 +578,18 @@ Perl_hv_store_ent(pTHX_ HV *hv, SV *keysv, SV *val, register U32 hash) 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; + 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); @@ -589,13 +628,20 @@ Perl_hv_store_ent(pTHX_ HV *hv, SV *keysv, SV *val, register U32 hash) continue; if (HeKUTF8(entry) != (char)is_utf8) continue; - SvREFCNT_dec(HeVAL(entry)); + if (HeVAL(entry) == &PL_sv_undef) + xhv->xhv_placeholders--; /* yes, can store into placeholder slot */ + else + SvREFCNT_dec(HeVAL(entry)); HeVAL(entry) = val; if (key != keysave) Safefree(key); return entry; } + if (SvREADONLY(hv)) { + Perl_hv_notallowed(aTHX_ is_utf8, key, klen, keysave); + } + entry = new_HE(); if (HvSHAREKEYS(hv)) HeKEY_hek(entry) = share_hek(key, is_utf8?-(I32)klen:klen, hash); @@ -669,7 +715,7 @@ Perl_hv_delete(pTHX_ HV *hv, const char *key, I32 klen, I32 flags) key = strupr(SvPVX(sv)); } #endif - } + } } xhv = (XPVHV*)SvANY(hv); if (!xhv->xhv_array /* !HvARRAY(hv) */) @@ -699,22 +745,63 @@ Perl_hv_delete(pTHX_ HV *hv, const char *key, I32 klen, I32 flags) continue; if (key != keysave) Safefree(key); - *oentry = HeNEXT(entry); - if (i && !*oentry) - xhv->xhv_fill--; /* HvFILL(hv)-- */ + /* if placeholder is here, it's already been deleted.... */ + if (HeVAL(entry) == &PL_sv_undef) + { + 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)-- */ + xhv->xhv_placeholders--; + return Nullsv; + } + } + else if (SvREADONLY(hv) && HeVAL(entry) && SvREADONLY(HeVAL(entry))) { + Perl_hv_notallowed(aTHX_ is_utf8, key, klen, keysave); + } + if (flags & G_DISCARD) sv = Nullsv; else { sv = sv_2mortal(HeVAL(entry)); HeVAL(entry) = &PL_sv_undef; } - if (entry == xhv->xhv_eiter /* HvEITER(hv) */) - HvLAZYDEL_on(hv); - else - hv_free_ent(hv, entry); - xhv->xhv_keys--; /* HvKEYS(hv)-- */ + + /* + * 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_undef; + /* 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)-- */ + } return sv; } + if (SvREADONLY(hv)) { + Perl_hv_notallowed(aTHX_ is_utf8, key, klen, keysave); + } + if (key != keysave) Safefree(key); return Nullsv; @@ -800,22 +887,63 @@ Perl_hv_delete_ent(pTHX_ HV *hv, SV *keysv, I32 flags, U32 hash) continue; if (key != keysave) Safefree(key); - *oentry = HeNEXT(entry); - if (i && !*oentry) - xhv->xhv_fill--; /* HvFILL(hv)-- */ + + /* if placeholder is here, it's already been deleted.... */ + if (HeVAL(entry) == &PL_sv_undef) + { + 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)-- */ + xhv->xhv_placeholders--; + return Nullsv; + } + else if (SvREADONLY(hv) && HeVAL(entry) && SvREADONLY(HeVAL(entry))) { + Perl_hv_notallowed(aTHX_ is_utf8, key, klen, keysave); + } + if (flags & G_DISCARD) sv = Nullsv; else { sv = sv_2mortal(HeVAL(entry)); HeVAL(entry) = &PL_sv_undef; } - if (entry == xhv->xhv_eiter /* HvEITER(hv) */) - HvLAZYDEL_on(hv); - else - hv_free_ent(hv, entry); - xhv->xhv_keys--; /* HvKEYS(hv)-- */ + + /* + * 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_undef; + /* 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)-- */ + } return sv; } + if (SvREADONLY(hv)) { + Perl_hv_notallowed(aTHX_ is_utf8, key, klen, keysave); + } + if (key != keysave) Safefree(key); return Nullsv; @@ -895,6 +1023,10 @@ Perl_hv_exists(pTHX_ HV *hv, const char *key, I32 klen) continue; if (key != keysave) Safefree(key); + /* If we find the key, but the value is a placeholder, return false. */ + if (HeVAL(entry) == &PL_sv_undef) + return FALSE; + return TRUE; } #ifdef DYNAMIC_ENV_FETCH /* is it out there? */ @@ -941,12 +1073,12 @@ Perl_hv_exists_ent(pTHX_ HV *hv, SV *keysv, U32 hash) if (SvRMAGICAL(hv)) { if (mg_find((SV*)hv, PERL_MAGIC_tied) || SvGMAGICAL((SV*)hv)) { - SV* svret = sv_newmortal(); + 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 SvTRUE(svret); + magic_existspack(svret, mg_find(sv, PERL_MAGIC_tiedelem)); + return SvTRUE(svret); } #ifdef ENV_IS_CASELESS else if (mg_find((SV*)hv, PERL_MAGIC_env)) { @@ -988,6 +1120,9 @@ Perl_hv_exists_ent(pTHX_ HV *hv, SV *keysv, U32 hash) continue; if (key != keysave) Safefree(key); + /* If we find the key, but the value is a placeholder, return false. */ + if (HeVAL(entry) == &PL_sv_undef) + return FALSE; return TRUE; } #ifdef DYNAMIC_ENV_FETCH /* is it out there? */ @@ -1098,13 +1233,13 @@ Perl_hv_ksplit(pTHX_ HV *hv, IV newmax) PL_nomemok = TRUE; #if defined(STRANGE_MALLOC) || defined(MYMALLOC) Renew(a, PERL_HV_ARRAY_ALLOC_BYTES(newsize), char); - if (!a) { + if (!a) { PL_nomemok = FALSE; return; } #else New(2, a, PERL_HV_ARRAY_ALLOC_BYTES(newsize), char); - if (!a) { + if (!a) { PL_nomemok = FALSE; return; } @@ -1225,7 +1360,7 @@ Perl_newHVhv(pTHX_ HV *ohv) HvMAX(hv) = hv_max; HvFILL(hv) = hv_fill; - HvKEYS(hv) = HvKEYS(ohv); + HvTOTALKEYS(hv) = HvTOTALKEYS(ohv); HvARRAY(hv) = ents; } else { @@ -1264,7 +1399,7 @@ Perl_hv_free_ent(pTHX_ HV *hv, register HE *entry) SvREFCNT_dec(val); if (HeKLEN(entry) == HEf_SVKEY) { SvREFCNT_dec(HeKEY_sv(entry)); - Safefree(HeKEY_hek(entry)); + Safefree(HeKEY_hek(entry)); } else if (HvSHAREKEYS(hv)) unshare_hek(HeKEY_hek(entry)); @@ -1310,6 +1445,7 @@ Perl_hv_clear(pTHX_ HV *hv) hfreeentries(hv); xhv->xhv_fill = 0; /* HvFILL(hv) = 0 */ xhv->xhv_keys = 0; /* HvKEYS(hv) = 0 */ + 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*)); @@ -1376,6 +1512,7 @@ Perl_hv_undef(pTHX_ HV *hv) xhv->xhv_array = 0; /* HvARRAY(hv) = 0 */ xhv->xhv_fill = 0; /* HvFILL(hv) = 0 */ xhv->xhv_keys = 0; /* HvKEYS(hv) = 0 */ + xhv->xhv_placeholders = 0; /* HvPLACEHOLDERS(hv) = 0 */ if (SvRMAGICAL(hv)) mg_clear((SV*)hv); @@ -1412,7 +1549,7 @@ Perl_hv_iterinit(pTHX_ HV *hv) xhv->xhv_riter = -1; /* HvRITER(hv) = -1 */ xhv->xhv_eiter = Null(HE*); /* HvEITER(hv) = Null(HE*) */ /* used to be xhv->xhv_fill before 5.004_65 */ - return xhv->xhv_keys; /* HvKEYS(hv) */ + return XHvTOTALKEYS(xhv); } /* @@ -1455,11 +1592,11 @@ Perl_hv_iternext(pTHX_ HV *hv) HeKLEN(entry) = HEf_SVKEY; } magic_nextpack((SV*) hv,mg,key); - if (SvOK(key)) { + if (SvOK(key)) { /* force key to stay around until next time */ HeSVKEY_set(entry, SvREFCNT_inc(key)); return entry; /* beware, hent_val is not set */ - } + } if (HeVAL(entry)) SvREFCNT_dec(HeVAL(entry)); Safefree(HeKEY_hek(entry)); @@ -1477,7 +1614,16 @@ Perl_hv_iternext(pTHX_ HV *hv) PERL_HV_ARRAY_ALLOC_BYTES(xhv->xhv_max+1 /* HvMAX(hv)+1 */), char); if (entry) + { entry = HeNEXT(entry); + /* + * Skip past any placeholders -- don't want to include them in + * any iteration. + */ + while (entry && HeVAL(entry) == &PL_sv_undef) { + entry = HeNEXT(entry); + } + } while (!entry) { xhv->xhv_riter++; /* HvRITER(hv)++ */ if (xhv->xhv_riter > xhv->xhv_max /* HvRITER(hv) > HvMAX(hv) */) { @@ -1486,6 +1632,11 @@ Perl_hv_iternext(pTHX_ HV *hv) } /* entry = (HvARRAY(hv))[HvRITER(hv)]; */ entry = ((HE**)xhv->xhv_array)[xhv->xhv_riter]; + + /* if we have an entry, but it's a placeholder, don't count it */ + if (entry && HeVAL(entry) == &PL_sv_undef) + entry = 0; + } if (oldentry && HvLAZYDEL(hv)) { /* was deleted earlier? */ @@ -1599,12 +1750,16 @@ 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. */ @@ -1690,7 +1845,7 @@ Perl_share_hek(pTHX_ const char *str, I32 len, register U32 hash) /* 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, Nullsv, hash); */ xhv = (XPVHV*)SvANY(PL_strtab); /* assert(xhv_array != 0) */