diff options
-rw-r--r-- | embed.fnc | 4 | ||||
-rw-r--r-- | embed.h | 2 | ||||
-rw-r--r-- | global.sym | 1 | ||||
-rw-r--r-- | hv.c | 55 | ||||
-rw-r--r-- | pod/perlapi.pod | 15 | ||||
-rw-r--r-- | proto.h | 4 | ||||
-rw-r--r-- | universal.c | 46 |
7 files changed, 82 insertions, 45 deletions
@@ -1398,5 +1398,7 @@ sM |SV* |hv_delete_common|HV* tb|SV* key_sv|const char* key|I32 klen|I32 flags|U sM |bool |hv_exists_common|HV* tb|SV* key_sv|const char* key|I32 klen|U32 hash sM |HE* |hv_fetch_common|HV* tb|SV* key_sv|const char* key|I32 klen|int flags|int action|U32 hash #endif -END_EXTERN_C +Apd |void |hv_clear_placeholders|HV* hb + +END_EXTERN_C @@ -2154,6 +2154,7 @@ #define hv_fetch_common S_hv_fetch_common #endif #endif +#define hv_clear_placeholders Perl_hv_clear_placeholders #define ck_anoncode Perl_ck_anoncode #define ck_bitop Perl_ck_bitop #define ck_concat Perl_ck_concat @@ -4644,6 +4645,7 @@ #define hv_fetch_common(a,b,c,d,e,f,g) S_hv_fetch_common(aTHX_ a,b,c,d,e,f,g) #endif #endif +#define hv_clear_placeholders(a) Perl_hv_clear_placeholders(aTHX_ a) #define ck_anoncode(a) Perl_ck_anoncode(aTHX_ a) #define ck_bitop(a) Perl_ck_bitop(aTHX_ a) #define ck_concat(a) Perl_ck_concat(aTHX_ a) diff --git a/global.sym b/global.sym index a3ce0ede8a..9fd5974336 100644 --- a/global.sym +++ b/global.sym @@ -665,3 +665,4 @@ Perl_PerlIO_stdout Perl_PerlIO_stderr Perl_save_set_svflags Perl_hv_assert +Perl_hv_clear_placeholders @@ -1621,6 +1621,61 @@ Perl_hv_clear(pTHX_ HV *hv) HvREHASH_off(hv); } +/* +=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 reaasigned 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) +{ + I32 items; + items = (I32)HvPLACEHOLDERS(hv); + if (items) { + HE *entry; + I32 riter = HvRITER(hv); + HE *eiter = HvEITER(hv); + hv_iterinit(hv); + /* This may look suboptimal with the items *after* the iternext, but + it's quite deliberate. We only get here with items==0 if we've + just deleted the last placeholder in the hash. If we've just done + that then it means that the hash is in lazy delete mode, and the + HE is now only referenced in our iterator. If we just quit the loop + and discarded our iterator then the HE leaks. So we do the && the + other way to ensure iternext is called just one more time, which + has the side effect of triggering the lazy delete. */ + while ((entry = hv_iternext_flags(hv, HV_ITERNEXT_WANTPLACEHOLDERS)) + && items) { + SV *val = hv_iterval(hv, entry); + + if (val == &PL_sv_placeholder) { + + /* It seems that I have to go back in the front of the hash + API to delete a hash, even though I have a HE structure + pointing to the very entry I want to delete, and could hold + onto the previous HE that points to it. And it's easier to + go in with SVs as I can then specify the precomputed hash, + and don't have fun and games with utf8 keys. */ + SV *key = hv_iterkeysv(entry); + + hv_delete_ent (hv, key, G_DISCARD, HeHASH(entry)); + items--; + } + } + HvRITER(hv) = riter; + HvEITER(hv) = eiter; + } +} + STATIC void S_hfreeentries(pTHX_ HV *hv) { diff --git a/pod/perlapi.pod b/pod/perlapi.pod index 0276fefc0f..5532e63cd2 100644 --- a/pod/perlapi.pod +++ b/pod/perlapi.pod @@ -1085,6 +1085,21 @@ Clears a hash, making it empty. =for hackers Found in file hv.c +=item 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 reaasigned 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. + + void hv_clear_placeholders(HV* hb) + +=for hackers +Found in file hv.c + =item hv_delete Deletes a key/value pair in the hash. The value SV is removed from the @@ -1339,5 +1339,7 @@ STATIC SV* S_hv_delete_common(pTHX_ HV* tb, SV* key_sv, const char* key, I32 kle STATIC bool S_hv_exists_common(pTHX_ HV* tb, SV* key_sv, const char* key, I32 klen, U32 hash); STATIC HE* S_hv_fetch_common(pTHX_ HV* tb, SV* key_sv, const char* key, I32 klen, int flags, int action, U32 hash); #endif -END_EXTERN_C +PERL_CALLCONV void Perl_hv_clear_placeholders(pTHX_ HV* hb); + +END_EXTERN_C diff --git a/universal.c b/universal.c index 83df6c5007..a6c1c41ba7 100644 --- a/universal.c +++ b/universal.c @@ -732,53 +732,13 @@ XS(XS_Internals_SvREFCNT) /* This is dangerous stuff. */ XSRETURN_UNDEF; /* Can't happen. */ } -/* Maybe this should return the number of placeholders found in scalar context, - and a list of them in list context. */ XS(XS_Internals_hv_clear_placehold) { dXSARGS; HV *hv = (HV *) SvRV(ST(0)); - - /* I don't care how many parameters were passed in, but I want to avoid - the unused variable warning. */ - - items = (I32)HvPLACEHOLDERS(hv); - - if (items) { - HE *entry; - I32 riter = HvRITER(hv); - HE *eiter = HvEITER(hv); - hv_iterinit(hv); - /* This may look suboptimal with the items *after* the iternext, but - it's quite deliberate. We only get here with items==0 if we've - just deleted the last placeholder in the hash. If we've just done - that then it means that the hash is in lazy delete mode, and the - HE is now only referenced in our iterator. If we just quit the loop - and discarded our iterator then the HE leaks. So we do the && the - other way to ensure iternext is called just one more time, which - has the side effect of triggering the lazy delete. */ - while ((entry = hv_iternext_flags(hv, HV_ITERNEXT_WANTPLACEHOLDERS)) - && items) { - SV *val = hv_iterval(hv, entry); - - if (val == &PL_sv_placeholder) { - - /* It seems that I have to go back in the front of the hash - API to delete a hash, even though I have a HE structure - pointing to the very entry I want to delete, and could hold - onto the previous HE that points to it. And it's easier to - go in with SVs as I can then specify the precomputed hash, - and don't have fun and games with utf8 keys. */ - SV *key = hv_iterkeysv(entry); - - hv_delete_ent (hv, key, G_DISCARD, HeHASH(entry)); - items--; - } - } - HvRITER(hv) = riter; - HvEITER(hv) = eiter; - } - + if (items != 1) + Perl_croak(aTHX_ "Usage: UNIVERSAL::hv_clear_placeholders(hv)"); + hv_clear_placeholders(hv); XSRETURN(0); } |