promote hv_clear_placeholders to perl API
Alan Burlison [Thu, 20 Nov 2003 10:34:30 +0000 (10:34 +0000)]
Message-ID: <3FBC98B6.6090909@sun.com>

p4raw-id: //depot/perl@21756

embed.fnc
embed.h
global.sym
hv.c
pod/perlapi.pod
proto.h
universal.c

index 32cb2f8..309db2e 100644 (file)
--- 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 7a9889a..d084b53 100644 (file)
--- a/embed.h
+++ b/embed.h
 #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
 #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)
index a3ce0ed..9fd5974 100644 (file)
@@ -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 eb75a30..41f65a7 100644 (file)
--- 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)
 {
index 0276fef..5532e63 100644 (file)
@@ -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 79795d7..2e7b80e 100644 (file)
--- 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
index 83df6c5..a6c1c41 100644 (file)
@@ -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);
 }