summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embed.fnc4
-rw-r--r--embed.h2
-rw-r--r--global.sym1
-rw-r--r--hv.c55
-rw-r--r--pod/perlapi.pod15
-rw-r--r--proto.h4
-rw-r--r--universal.c46
7 files changed, 82 insertions, 45 deletions
diff --git a/embed.fnc b/embed.fnc
index 32cb2f8afb..309db2e9e8 100644
--- a/embed.fnc
+++ b/embed.fnc
@@ -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
diff --git a/embed.h b/embed.h
index 7a9889a7ab..d084b53cc4 100644
--- a/embed.h
+++ b/embed.h
@@ -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
diff --git a/hv.c b/hv.c
index eb75a30d8a..41f65a72f8 100644
--- a/hv.c
+++ b/hv.c
@@ -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
diff --git a/proto.h b/proto.h
index 79795d77b1..2e7b80ebac 100644
--- a/proto.h
+++ b/proto.h
@@ -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);
}