From 20439bc77dfeec46d94a15cf108446039e26c995 Mon Sep 17 00:00:00 2001 From: Zefram Date: Tue, 19 Oct 2010 09:31:46 -0700 Subject: [PATCH] full API for cop hint hashes Expose cop hint hashes as a type COPHH, with a cophh_* API which is a macro layer over the refcounted_he_* API. The documentation for cophh_* describes purely API-visible behaviour, whereas the refcounted_he_* documentation describes the functions mainly in terms of the implementation. Revise the cop_hints_* API, using the flags parameter consistently and reimplementing in terms of cophh_*. Use the cophh_* and cop_hints_* functions consistently where appropriate. [Modified by the committer to update two calls to Perl_refcounted_he_fetch recently added to newPMOP.] --- MANIFEST | 1 + cop.h | 310 +++++++++++++++++++++++++++++- embed.fnc | 50 +++-- embed.h | 12 +- ext/B/B.xs | 4 +- ext/XS-APItest/APItest.xs | 140 ++++++++++++-- ext/XS-APItest/t/cophh.t | 18 ++ global.sym | 12 +- gv.c | 3 +- hv.c | 471 ++++++++++++++++++++++++++++------------------ hv.h | 37 +++- mg.c | 24 +-- op.c | 17 +- perl.c | 4 +- perlio.c | 3 +- pp_ctl.c | 20 +- proto.h | 64 +++---- scope.c | 15 +- sv.c | 12 +- 19 files changed, 865 insertions(+), 352 deletions(-) create mode 100644 ext/XS-APItest/t/cophh.t diff --git a/MANIFEST b/MANIFEST index ba2c4b7..b07c12b 100644 --- a/MANIFEST +++ b/MANIFEST @@ -3391,6 +3391,7 @@ ext/XS-APItest/t/Block.pm Helper for ./blockhooks.t ext/XS-APItest/t/call_checker.t test call checker plugin API ext/XS-APItest/t/caller.t XS::APItest: tests for caller_cx ext/XS-APItest/t/call.t XS::APItest extension +ext/XS-APItest/t/cophh.t test COPHH API ext/XS-APItest/t/copyhints.t test hv_copy_hints_hv() API ext/XS-APItest/t/exception.t XS::APItest extension ext/XS-APItest/t/grok.t XS::APItest: tests for grok* functions diff --git a/cop.h b/cop.h index 8e77ae2..970aa8d 100644 --- a/cop.h +++ b/cop.h @@ -152,6 +152,230 @@ typedef struct jmpenv JMPENV; PL_top_env->je_mustcatch = (v); \ } STMT_END +/* +=head1 COP Hint Hashes +*/ + +typedef struct refcounted_he COPHH; + +#define COPHH_KEY_UTF8 REFCOUNTED_HE_KEY_UTF8 + +/* +=for apidoc Am|SV *|cophh_fetch_pvn|const COPHH *cophh|const char *keypv|STRLEN keylen|U32 hash|U32 flags + +Look up the entry in the cop hints hash I with the key specified by +I and I. If I has the C bit set, +the key octets are interpreted as UTF-8, otherwise they are interpreted +as Latin-1. I is a precomputed hash of the key string, or zero if +it has not been precomputed. Returns a mortal scalar copy of the value +associated with the key, or C<&PL_sv_placeholder> if there is no value +associated with the key. + +=cut +*/ + +#define cophh_fetch_pvn(cophh, keypv, keylen, hash, flags) \ + Perl_refcounted_he_fetch_pvn(aTHX_ cophh, keypv, keylen, hash, flags) + +/* +=for apidoc Am|SV *|cophh_fetch_pvs|const COPHH *cophh|const char *key|U32 flags + +Like L, but takes a literal string instead of a +string/length pair, and no precomputed hash. + +=cut +*/ + +#define cophh_fetch_pvs(cophh, key, flags) \ + Perl_refcounted_he_fetch_pvn(aTHX_ cophh, STR_WITH_LEN(key), 0, flags) + +/* +=for apidoc Am|SV *|cophh_fetch_pv|const COPHH *cophh|const char *key|U32 hash|U32 flags + +Like L, but takes a nul-terminated string instead of +a string/length pair. + +=cut +*/ + +#define cophh_fetch_pv(cophh, key, hash, flags) \ + Perl_refcounted_he_fetch_pv(aTHX_ cophh, key, hash, flags) + +/* +=for apidoc Am|SV *|cophh_fetch_sv|const COPHH *cophh|SV *key|U32 hash|U32 flags + +Like L, but takes a Perl scalar instead of a +string/length pair. + +=cut +*/ + +#define cophh_fetch_sv(cophh, key, hash, flags) \ + Perl_refcounted_he_fetch_sv(aTHX_ cophh, key, hash, flags) + +/* +=for apidoc Am|HV *|cophh_2hv|const COPHH *cophh|U32 flags + +Generates and returns a standard Perl hash representing the full set of +key/value pairs in the cop hints hash I. I is currently +unused and must be zero. + +=cut +*/ + +#define cophh_2hv(cophh, flags) \ + Perl_refcounted_he_chain_2hv(aTHX_ cophh, flags) + +/* +=for apidoc Am|COPHH *|cophh_copy|COPHH *cophh + +Make and return a complete copy of the cop hints hash I. + +=cut +*/ + +#define cophh_copy(cophh) Perl_refcounted_he_inc(aTHX_ cophh) + +/* +=for apidoc Am|void|cophh_free|COPHH *cophh + +Discard the cop hints hash I, freeing all resources associated +with it. + +=cut +*/ + +#define cophh_free(cophh) Perl_refcounted_he_free(aTHX_ cophh) + +/* +=for apidoc Am|COPHH *|cophh_new_empty + +Generate and return a fresh cop hints hash containing no entries. + +=cut +*/ + +#define cophh_new_empty() ((COPHH *)NULL) + +/* +=for apidoc Am|COPHH *|cophh_store_pvn|COPHH *cophh|const char *keypv|STRLEN keylen|U32 hash|SV *value|U32 flags + +Stores a value, associated with a key, in the cop hints hash I, +and returns the modified hash. The returned hash pointer is in general +not the same as the hash pointer that was passed in. The input hash is +consumed by the function, and the pointer to it must not be subsequently +used. Use L if you need both hashes. + +The key is specified by I and I. If I has the +C bit set, the key octets are interpreted as UTF-8, +otherwise they are interpreted as Latin-1. I is a precomputed +hash of the key string, or zero if it has not been precomputed. + +I is the scalar value to store for this key. I is copied +by this function, which thus does not take ownership of any reference +to it, and later changes to the scalar will not be reflected in the +value visible in the cop hints hash. Complex types of scalar will not +be stored with referential integrity, but will be coerced to strings. + +=cut +*/ + +#define cophh_store_pvn(cophh, keypv, keylen, hash, value, flags) \ + Perl_refcounted_he_new_pvn(aTHX_ cophh, keypv, keylen, hash, value, flags) + +/* +=for apidoc Am|COPHH *|cophh_store_pvs|const COPHH *cophh|const char *key|SV *value|U32 flags + +Like L, but takes a literal string instead of a +string/length pair, and no precomputed hash. + +=cut +*/ + +#define cophh_store_pvs(cophh, key, value, flags) \ + Perl_refcounted_he_new_pvn(aTHX_ cophh, STR_WITH_LEN(key), 0, value, flags) + +/* +=for apidoc Am|COPHH *|cophh_store_pv|const COPHH *cophh|const char *key|U32 hash|SV *value|U32 flags + +Like L, but takes a nul-terminated string instead of +a string/length pair. + +=cut +*/ + +#define cophh_store_pv(cophh, key, hash, value, flags) \ + Perl_refcounted_he_new_pv(aTHX_ cophh, key, hash, value, flags) + +/* +=for apidoc Am|COPHH *|cophh_store_sv|const COPHH *cophh|SV *key|U32 hash|SV *value|U32 flags + +Like L, but takes a Perl scalar instead of a +string/length pair. + +=cut +*/ + +#define cophh_store_sv(cophh, key, hash, value, flags) \ + Perl_refcounted_he_new_sv(aTHX_ cophh, key, hash, value, flags) + +/* +=for apidoc Am|COPHH *|cophh_delete_pvn|COPHH *cophh|const char *keypv|STRLEN keylen|U32 hash|U32 flags + +Delete a key and its associated value from the cop hints hash I, +and returns the modified hash. The returned hash pointer is in general +not the same as the hash pointer that was passed in. The input hash is +consumed by the function, and the pointer to it must not be subsequently +used. Use L if you need both hashes. + +The key is specified by I and I. If I has the +C bit set, the key octets are interpreted as UTF-8, +otherwise they are interpreted as Latin-1. I is a precomputed +hash of the key string, or zero if it has not been precomputed. + +=cut +*/ + +#define cophh_delete_pvn(cophh, keypv, keylen, hash, flags) \ + Perl_refcounted_he_new_pvn(aTHX_ cophh, keypv, keylen, hash, \ + (SV *)NULL, flags) + +/* +=for apidoc Am|COPHH *|cophh_delete_pvs|const COPHH *cophh|const char *key|U32 flags + +Like L, but takes a literal string instead of a +string/length pair, and no precomputed hash. + +=cut +*/ + +#define cophh_delete_pvs(cophh, key, flags) \ + Perl_refcounted_he_new_pvn(aTHX_ cophh, STR_WITH_LEN(key), 0, \ + (SV *)NULL, flags) + +/* +=for apidoc Am|COPHH *|cophh_delete_pv|const COPHH *cophh|const char *key|U32 hash|U32 flags + +Like L, but takes a nul-terminated string instead of +a string/length pair. + +=cut +*/ + +#define cophh_delete_pv(cophh, key, hash, flags) \ + Perl_refcounted_he_new_pv(aTHX_ cophh, key, hash, (SV *)NULL, flags) + +/* +=for apidoc Am|COPHH *|cophh_delete_sv|const COPHH *cophh|SV *key|U32 hash|U32 flags + +Like L, but takes a Perl scalar instead of a +string/length pair. + +=cut +*/ + +#define cophh_delete_sv(cophh, key, hash, flags) \ + Perl_refcounted_he_new_sv(aTHX_ cophh, key, hash, (SV *)NULL, flags) #include "mydtrace.h" @@ -174,7 +398,7 @@ struct cop { STRLEN * cop_warnings; /* lexical warnings bitmask */ /* compile time state of %^H. See the comment in op.c for how this is used to recreate a hash to return from caller. */ - struct refcounted_he * cop_hints_hash; + COPHH * cop_hints_hash; }; #ifdef USE_ITHREADS @@ -243,6 +467,80 @@ struct cop { # define CopFILE_free(c) (SvREFCNT_dec(CopFILEGV(c)),(CopFILEGV(c) = NULL)) #endif /* USE_ITHREADS */ + +#define CopHINTHASH_get(c) ((COPHH*)((c)->cop_hints_hash)) +#define CopHINTHASH_set(c,h) ((c)->cop_hints_hash = (h)) + +/* +=head1 COP Hint Reading +*/ + +/* +=for apidoc Am|SV *|cop_hints_fetch_pvn|const COP *cop|const char *keypv|STRLEN keylen|U32 hash|U32 flags + +Look up the hint entry in the cop I with the key specified by +I and I. If I has the C bit set, +the key octets are interpreted as UTF-8, otherwise they are interpreted +as Latin-1. I is a precomputed hash of the key string, or zero if +it has not been precomputed. Returns a mortal scalar copy of the value +associated with the key, or C<&PL_sv_placeholder> if there is no value +associated with the key. + +=cut +*/ + +#define cop_hints_fetch_pvn(cop, keypv, keylen, hash, flags) \ + cophh_fetch_pvn(CopHINTHASH_get(cop), keypv, keylen, hash, flags) + +/* +=for apidoc Am|SV *|cop_hints_fetch_pvs|const COP *cop|const char *key|U32 flags + +Like L, but takes a literal string instead of a +string/length pair, and no precomputed hash. + +=cut +*/ + +#define cop_hints_fetch_pvs(cop, key, flags) \ + cophh_fetch_pvs(CopHINTHASH_get(cop), key, flags) + +/* +=for apidoc Am|SV *|cop_hints_fetch_pv|const COP *cop|const char *key|U32 hash|U32 flags + +Like L, but takes a nul-terminated string instead +of a string/length pair. + +=cut +*/ + +#define cop_hints_fetch_pv(cop, key, hash, flags) \ + cophh_fetch_pv(CopHINTHASH_get(cop), key, hash, flags) + +/* +=for apidoc Am|SV *|cop_hints_fetch_sv|const COP *cop|SV *key|U32 hash|U32 flags + +Like L, but takes a Perl scalar instead of a +string/length pair. + +=cut +*/ + +#define cop_hints_fetch_sv(cop, key, hash, flags) \ + cophh_fetch_sv(CopHINTHASH_get(cop), key, hash, flags) + +/* +=for apidoc Am|HV *|cop_hints_2hv|const COP *cop|U32 flags + +Generates and returns a standard Perl hash representing the full set of +hint entries in the cop I. I is currently unused and must +be zero. + +=cut +*/ + +#define cop_hints_2hv(cop, flags) \ + cophh_2hv(CopHINTHASH_get(cop), flags) + #define CopLABEL(c) Perl_fetch_cop_label(aTHX_ (c), NULL, NULL) #define CopLABEL_alloc(pv) ((pv)?savepv(pv):NULL) @@ -261,8 +559,7 @@ struct cop { using $[ is highly discouraged, no sane Perl code will be using it. */ #define CopARYBASE_get(c) \ ((CopHINTS_get(c) & HINT_ARYBASE) \ - ? SvIV(Perl_refcounted_he_fetch(aTHX_ (c)->cop_hints_hash, 0, \ - "$[", 2, 0, 0)) \ + ? SvIV(cop_hints_fetch_pvs((c), "$[", 0)) \ : 0) #define CopARYBASE_set(c, b) STMT_START { \ if (b || ((c)->cop_hints & HINT_ARYBASE)) { \ @@ -273,10 +570,9 @@ struct cop { mg_set(val); \ PL_hints |= HINT_ARYBASE; \ } else { \ - (c)->cop_hints_hash \ - = Perl_refcounted_he_new(aTHX_ (c)->cop_hints_hash, \ - newSVpvs_flags("$[", SVs_TEMP), \ - sv_2mortal(newSViv(b))); \ + CopHINTHASH_set((c), \ + cophh_store_pvs(CopHINTHASH_get((c)), "$[", \ + sv_2mortal(newSViv(b)), 0)); \ } \ } \ } STMT_END diff --git a/embed.fnc b/embed.fnc index 9411a68..1543017 100644 --- a/embed.fnc +++ b/embed.fnc @@ -229,13 +229,6 @@ ApR |I32 |my_chsize |int fd|Off_t length #endif : Used in perly.y pR |OP* |convert |I32 optype|I32 flags|NULLOK OP* o -Apd |HV* |cop_hints_2hv |NN const COP *cop -Apd |SV* |cop_hints_fetchpvn|NN const COP *cop|NN const char *key \ - |STRLEN klen|int flags|U32 hash -Amd |SV* |cop_hints_fetchpvs|NN const COP *cop|NN const char *const key -Amd |SV* |cop_hints_fetchpv|NN const COP *cop|NN const char *const key \ - |int flags|U32 hash -Amd |SV* |cop_hints_fetchsv|NN const COP *cop|NN SV *keysv|U32 hash : Used in op.c and perl.c pM |PERL_CONTEXT* |create_eval_scope|U32 flags Aprd |void |croak_sv |NN SV *baseex @@ -480,26 +473,30 @@ ApMdR |HE* |hv_iternext_flags|NN HV *hv|I32 flags ApdR |SV* |hv_iterval |NN HV *hv|NN HE *entry Ap |void |hv_ksplit |NN HV *hv|IV newmax Apdbm |void |hv_magic |NN HV *hv|NULLOK GV *gv|int how -: Used in B.xs -XEdpoM |HV * |refcounted_he_chain_2hv|NULLOK const struct refcounted_he *c -: Used in APItest.xs -XEpoM |SV * |refcounted_he_fetch|NULLOK const struct refcounted_he *chain \ - |NULLOK SV *keysv|NULLOK const char *key \ - |STRLEN klen, int flags, U32 hash -: Used in various files -dpoM |void |refcounted_he_free|NULLOK struct refcounted_he *he -: Used in various files -XEdpoM |struct refcounted_he *|refcounted_he_new \ - |NULLOK struct refcounted_he *const parent \ - |NULLOK SV *const key|NULLOK SV *const value #if defined(PERL_IN_HV_C) -s |struct refcounted_he * |refcounted_he_new_common \ - |NULLOK struct refcounted_he *const parent \ - |NN const char *const key_p \ - |const STRLEN key_len|const char flags \ - |char value_type|NN const void *value \ - |const STRLEN value_len -#endif +s |SV * |refcounted_he_value |NN const struct refcounted_he *he +#endif +Xpd |HV * |refcounted_he_chain_2hv|NULLOK const struct refcounted_he *c|U32 flags +Xpd |SV * |refcounted_he_fetch_pvn|NULLOK const struct refcounted_he *chain \ + |NN const char *keypv|STRLEN keylen|U32 hash|U32 flags +Xpd |SV * |refcounted_he_fetch_pv|NULLOK const struct refcounted_he *chain \ + |NN const char *key|U32 hash|U32 flags +Xpd |SV * |refcounted_he_fetch_sv|NULLOK const struct refcounted_he *chain \ + |NN SV *key|U32 hash|U32 flags +Xpd |struct refcounted_he *|refcounted_he_new_pvn \ + |NULLOK struct refcounted_he *parent \ + |NN const char *keypv|STRLEN keylen \ + |U32 hash|NULLOK SV *value|U32 flags +Xpd |struct refcounted_he *|refcounted_he_new_pv \ + |NULLOK struct refcounted_he *parent \ + |NN const char *key \ + |U32 hash|NULLOK SV *value|U32 flags +Xpd |struct refcounted_he *|refcounted_he_new_sv \ + |NULLOK struct refcounted_he *parent \ + |NN SV *key \ + |U32 hash|NULLOK SV *value|U32 flags +Xpd |void |refcounted_he_free|NULLOK struct refcounted_he *he +Xpd |struct refcounted_he *|refcounted_he_inc|NULLOK struct refcounted_he *he Abmd |SV** |hv_store |NULLOK HV *hv|NULLOK const char *key \ |I32 klen|NULLOK SV *val|U32 hash Abmd |HE* |hv_store_ent |NULLOK HV *hv|NULLOK SV *key|NULLOK SV *val\ @@ -1531,7 +1528,6 @@ sM |SV* |hv_delete_common|NULLOK HV *hv|NULLOK SV *keysv \ |NULLOK const char *key|STRLEN klen|int k_flags|I32 d_flags \ |U32 hash sM |void |clear_placeholders |NN HV *hv|U32 items -sM |SV * |refcounted_he_value |NN const struct refcounted_he *he #endif #if defined(PERL_IN_MG_C) diff --git a/embed.h b/embed.h index 057b628..2ef2c49 100644 --- a/embed.h +++ b/embed.h @@ -66,8 +66,6 @@ #define ck_warner Perl_ck_warner #define ck_warner_d Perl_ck_warner_d #endif -#define cop_hints_2hv(a) Perl_cop_hints_2hv(aTHX_ a) -#define cop_hints_fetchpvn(a,b,c,d,e) Perl_cop_hints_fetchpvn(aTHX_ a,b,c,d,e) #ifndef PERL_IMPLICIT_CONTEXT #define croak Perl_croak #endif @@ -1443,6 +1441,15 @@ #define pp_wantarray() Perl_pp_wantarray(aTHX) #define pp_warn() Perl_pp_warn(aTHX) #define pp_xor() Perl_pp_xor(aTHX) +#define refcounted_he_chain_2hv(a,b) Perl_refcounted_he_chain_2hv(aTHX_ a,b) +#define refcounted_he_fetch_pv(a,b,c,d) Perl_refcounted_he_fetch_pv(aTHX_ a,b,c,d) +#define refcounted_he_fetch_pvn(a,b,c,d,e) Perl_refcounted_he_fetch_pvn(aTHX_ a,b,c,d,e) +#define refcounted_he_fetch_sv(a,b,c,d) Perl_refcounted_he_fetch_sv(aTHX_ a,b,c,d) +#define refcounted_he_free(a) Perl_refcounted_he_free(aTHX_ a) +#define refcounted_he_inc(a) Perl_refcounted_he_inc(aTHX_ a) +#define refcounted_he_new_pv(a,b,c,d,e) Perl_refcounted_he_new_pv(aTHX_ a,b,c,d,e) +#define refcounted_he_new_pvn(a,b,c,d,e,f) Perl_refcounted_he_new_pvn(aTHX_ a,b,c,d,e,f) +#define refcounted_he_new_sv(a,b,c,d,e) Perl_refcounted_he_new_sv(aTHX_ a,b,c,d,e) #define report_evil_fh(a,b,c) Perl_report_evil_fh(aTHX_ a,b,c) #define rpeep(a) Perl_rpeep(aTHX_ a) #define rsignal_restore(a,b) Perl_rsignal_restore(aTHX_ a,b) @@ -1602,7 +1609,6 @@ #define hv_magic_check S_hv_magic_check #define hv_notallowed(a,b,c,d) S_hv_notallowed(aTHX_ a,b,c,d) #define new_he() S_new_he(aTHX) -#define refcounted_he_new_common(a,b,c,d,e,f,g) S_refcounted_he_new_common(aTHX_ a,b,c,d,e,f,g) #define refcounted_he_value(a) S_refcounted_he_value(aTHX_ a) #define save_hek_flags S_save_hek_flags #define share_hek_flags(a,b,c,d) S_share_hek_flags(aTHX_ a,b,c,d) diff --git a/ext/B/B.xs b/ext/B/B.xs index 2b6fb8d..3d51969 100644 --- a/ext/B/B.xs +++ b/ext/B/B.xs @@ -1274,7 +1274,7 @@ B::RHE COP_hints_hash(o) B::COP o CODE: - RETVAL = o->cop_hints_hash; + RETVAL = CopHINTHASH_get(o); OUTPUT: RETVAL @@ -2046,7 +2046,7 @@ SV* RHE_HASH(h) B::RHE h CODE: - RETVAL = newRV( (SV*)Perl_refcounted_he_chain_2hv(aTHX_ h) ); + RETVAL = newRV( (SV*)cophh_2hv(h, 0) ); OUTPUT: RETVAL diff --git a/ext/XS-APItest/APItest.xs b/ext/XS-APItest/APItest.xs index 632b9f6..55aa1de 100644 --- a/ext/XS-APItest/APItest.xs +++ b/ext/XS-APItest/APItest.xs @@ -1125,9 +1125,7 @@ refcounted_he_exists(key, level=0) if (level) { croak("level must be zero, not %"IVdf, level); } - RETVAL = (Perl_refcounted_he_fetch(aTHX_ PL_curcop->cop_hints_hash, - key, NULL, 0, 0, 0) - != &PL_sv_placeholder); + RETVAL = (cop_hints_fetch_sv(PL_curcop, key, 0, 0) != &PL_sv_placeholder); OUTPUT: RETVAL @@ -1139,8 +1137,7 @@ refcounted_he_fetch(key, level=0) if (level) { croak("level must be zero, not %"IVdf, level); } - RETVAL = Perl_refcounted_he_fetch(aTHX_ PL_curcop->cop_hints_hash, key, - NULL, 0, 0, 0); + RETVAL = cop_hints_fetch_sv(PL_curcop, key, 0, 0); SvREFCNT_inc(RETVAL); OUTPUT: RETVAL @@ -1589,12 +1586,12 @@ my_caller(level) gv = CvGV(dbcx->blk_sub.cv); ST(3) = isGV(gv) ? sv_2mortal(newSVpv(GvNAME(gv), 0)) : &PL_sv_undef; - ST(4) = cop_hints_fetchpvs(cx->blk_oldcop, "foo"); - ST(5) = cop_hints_fetchpvn(cx->blk_oldcop, "foo", 3, 0, 0); - ST(6) = cop_hints_fetchsv(cx->blk_oldcop, - sv_2mortal(newSVpvn("foo", 3)), 0); + ST(4) = cop_hints_fetch_pvs(cx->blk_oldcop, "foo", 0); + ST(5) = cop_hints_fetch_pvn(cx->blk_oldcop, "foo", 3, 0, 0); + ST(6) = cop_hints_fetch_sv(cx->blk_oldcop, + sv_2mortal(newSVpvn("foo", 3)), 0, 0); - hv = cop_hints_2hv(cx->blk_oldcop); + hv = cop_hints_2hv(cx->blk_oldcop, 0); ST(7) = hv ? sv_2mortal(newRV_noinc((SV *)hv)) : &PL_sv_undef; XSRETURN(8); @@ -1897,6 +1894,118 @@ cv_set_call_checker_multi_sum(CV *cv) cv_set_call_checker(cv, THX_ck_entersub_multi_sum, &PL_sv_undef); void +test_cophh() + PREINIT: + COPHH *a, *b; + CODE: +#define check_ph(EXPR) \ + do { if((EXPR) != &PL_sv_placeholder) croak("fail"); } while(0) +#define check_iv(EXPR, EXPECT) \ + do { if(SvIV(EXPR) != (EXPECT)) croak("fail"); } while(0) +#define msvpvs(STR) sv_2mortal(newSVpvs(STR)) +#define msviv(VALUE) sv_2mortal(newSViv(VALUE)) + a = cophh_new_empty(); + check_ph(cophh_fetch_pvn(a, "foo_1", 5, 0, 0)); + check_ph(cophh_fetch_pvs(a, "foo_1", 0)); + check_ph(cophh_fetch_pv(a, "foo_1", 0, 0)); + check_ph(cophh_fetch_sv(a, msvpvs("foo_1"), 0, 0)); + a = cophh_store_pvn(a, "foo_1abc", 5, 0, msviv(111), 0); + a = cophh_store_pvs(a, "foo_2", msviv(222), 0); + a = cophh_store_pv(a, "foo_3", 0, msviv(333), 0); + a = cophh_store_sv(a, msvpvs("foo_4"), 0, msviv(444), 0); + check_iv(cophh_fetch_pvn(a, "foo_1xyz", 5, 0, 0), 111); + check_iv(cophh_fetch_pvs(a, "foo_1", 0), 111); + check_iv(cophh_fetch_pv(a, "foo_1", 0, 0), 111); + check_iv(cophh_fetch_sv(a, msvpvs("foo_1"), 0, 0), 111); + check_iv(cophh_fetch_pvs(a, "foo_2", 0), 222); + check_iv(cophh_fetch_pvs(a, "foo_3", 0), 333); + check_iv(cophh_fetch_pvs(a, "foo_4", 0), 444); + check_ph(cophh_fetch_pvs(a, "foo_5", 0)); + b = cophh_copy(a); + b = cophh_store_pvs(b, "foo_1", msviv(1111), 0); + check_iv(cophh_fetch_pvs(a, "foo_1", 0), 111); + check_iv(cophh_fetch_pvs(a, "foo_2", 0), 222); + check_iv(cophh_fetch_pvs(a, "foo_3", 0), 333); + check_iv(cophh_fetch_pvs(a, "foo_4", 0), 444); + check_ph(cophh_fetch_pvs(a, "foo_5", 0)); + check_iv(cophh_fetch_pvs(b, "foo_1", 0), 1111); + check_iv(cophh_fetch_pvs(b, "foo_2", 0), 222); + check_iv(cophh_fetch_pvs(b, "foo_3", 0), 333); + check_iv(cophh_fetch_pvs(b, "foo_4", 0), 444); + check_ph(cophh_fetch_pvs(b, "foo_5", 0)); + a = cophh_delete_pvn(a, "foo_1abc", 5, 0, 0); + a = cophh_delete_pvs(a, "foo_2", 0); + b = cophh_delete_pv(b, "foo_3", 0, 0); + b = cophh_delete_sv(b, msvpvs("foo_4"), 0, 0); + check_ph(cophh_fetch_pvs(a, "foo_1", 0)); + check_ph(cophh_fetch_pvs(a, "foo_2", 0)); + check_iv(cophh_fetch_pvs(a, "foo_3", 0), 333); + check_iv(cophh_fetch_pvs(a, "foo_4", 0), 444); + check_ph(cophh_fetch_pvs(a, "foo_5", 0)); + check_iv(cophh_fetch_pvs(b, "foo_1", 0), 1111); + check_iv(cophh_fetch_pvs(b, "foo_2", 0), 222); + check_ph(cophh_fetch_pvs(b, "foo_3", 0)); + check_ph(cophh_fetch_pvs(b, "foo_4", 0)); + check_ph(cophh_fetch_pvs(b, "foo_5", 0)); + b = cophh_delete_pvs(b, "foo_3", 0); + b = cophh_delete_pvs(b, "foo_5", 0); + check_iv(cophh_fetch_pvs(b, "foo_1", 0), 1111); + check_iv(cophh_fetch_pvs(b, "foo_2", 0), 222); + check_ph(cophh_fetch_pvs(b, "foo_3", 0)); + check_ph(cophh_fetch_pvs(b, "foo_4", 0)); + check_ph(cophh_fetch_pvs(b, "foo_5", 0)); + cophh_free(b); + check_ph(cophh_fetch_pvs(a, "foo_1", 0)); + check_ph(cophh_fetch_pvs(a, "foo_2", 0)); + check_iv(cophh_fetch_pvs(a, "foo_3", 0), 333); + check_iv(cophh_fetch_pvs(a, "foo_4", 0), 444); + check_ph(cophh_fetch_pvs(a, "foo_5", 0)); + a = cophh_store_pvs(a, "foo_1", msviv(11111), COPHH_KEY_UTF8); + a = cophh_store_pvs(a, "foo_\xaa", msviv(123), 0); + a = cophh_store_pvs(a, "foo_\xc2\xbb", msviv(456), COPHH_KEY_UTF8); + a = cophh_store_pvs(a, "foo_\xc3\x8c", msviv(789), COPHH_KEY_UTF8); + a = cophh_store_pvs(a, "foo_\xd9\xa6", msviv(666), COPHH_KEY_UTF8); + check_iv(cophh_fetch_pvs(a, "foo_1", 0), 11111); + check_iv(cophh_fetch_pvs(a, "foo_1", COPHH_KEY_UTF8), 11111); + check_iv(cophh_fetch_pvs(a, "foo_\xaa", 0), 123); + check_iv(cophh_fetch_pvs(a, "foo_\xc2\xaa", COPHH_KEY_UTF8), 123); + check_ph(cophh_fetch_pvs(a, "foo_\xc2\xaa", 0)); + check_iv(cophh_fetch_pvs(a, "foo_\xbb", 0), 456); + check_iv(cophh_fetch_pvs(a, "foo_\xc2\xbb", COPHH_KEY_UTF8), 456); + check_ph(cophh_fetch_pvs(a, "foo_\xc2\xbb", 0)); + check_iv(cophh_fetch_pvs(a, "foo_\xcc", 0), 789); + check_iv(cophh_fetch_pvs(a, "foo_\xc3\x8c", COPHH_KEY_UTF8), 789); + check_ph(cophh_fetch_pvs(a, "foo_\xc2\x8c", 0)); + check_iv(cophh_fetch_pvs(a, "foo_\xd9\xa6", COPHH_KEY_UTF8), 666); + check_ph(cophh_fetch_pvs(a, "foo_\xd9\xa6", 0)); + cophh_free(a); +#undef check_ph +#undef check_iv +#undef msvpvs +#undef msviv + +HV * +example_cophh_2hv() + PREINIT: + COPHH *a; + CODE: +#define msviv(VALUE) sv_2mortal(newSViv(VALUE)) + a = cophh_new_empty(); + a = cophh_store_pvs(a, "foo_0", msviv(999), 0); + a = cophh_store_pvs(a, "foo_1", msviv(111), 0); + a = cophh_store_pvs(a, "foo_\xaa", msviv(123), 0); + a = cophh_store_pvs(a, "foo_\xc2\xbb", msviv(456), COPHH_KEY_UTF8); + a = cophh_store_pvs(a, "foo_\xc3\x8c", msviv(789), COPHH_KEY_UTF8); + a = cophh_store_pvs(a, "foo_\xd9\xa6", msviv(666), COPHH_KEY_UTF8); + a = cophh_delete_pvs(a, "foo_0", 0); + a = cophh_delete_pvs(a, "foo_2", 0); + RETVAL = cophh_2hv(a, 0); + cophh_free(a); +#undef msviv + OUTPUT: + RETVAL + +void test_savehints() PREINIT: SV **svp, *sv; @@ -1906,7 +2015,7 @@ test_savehints() #define hint_ok(KEY, EXPECT) \ ((svp = hv_fetchs(GvHV(PL_hintgv), KEY, 0)) && \ (sv = *svp) && SvIV(sv) == (EXPECT) && \ - (sv = cop_hints_fetchpvs(&PL_compiling, KEY)) && \ + (sv = cop_hints_fetch_pvs(&PL_compiling, KEY, 0)) && \ SvIV(sv) == (EXPECT)) #define check_hint(KEY, EXPECT) \ do { if (!hint_ok(KEY, EXPECT)) croak_fail(); } while(0) @@ -1953,15 +2062,18 @@ test_copyhints() ENTER; SAVEHINTS(); sv_setiv_mg(*hv_fetchs(GvHV(PL_hintgv), "t0", 1), 123); - if (SvIV(cop_hints_fetchpvs(&PL_compiling, "t0")) != 123) croak_fail(); + if (SvIV(cop_hints_fetch_pvs(&PL_compiling, "t0", 0)) != 123) + croak_fail(); a = newHVhv(GvHV(PL_hintgv)); sv_2mortal((SV*)a); sv_setiv_mg(*hv_fetchs(a, "t0", 1), 456); - if (SvIV(cop_hints_fetchpvs(&PL_compiling, "t0")) != 123) croak_fail(); + if (SvIV(cop_hints_fetch_pvs(&PL_compiling, "t0", 0)) != 123) + croak_fail(); b = hv_copy_hints_hv(a); sv_2mortal((SV*)b); sv_setiv_mg(*hv_fetchs(b, "t0", 1), 789); - if (SvIV(cop_hints_fetchpvs(&PL_compiling, "t0")) != 789) croak_fail(); + if (SvIV(cop_hints_fetch_pvs(&PL_compiling, "t0", 0)) != 789) + croak_fail(); LEAVE; void diff --git a/ext/XS-APItest/t/cophh.t b/ext/XS-APItest/t/cophh.t new file mode 100644 index 0000000..a3fe49b --- /dev/null +++ b/ext/XS-APItest/t/cophh.t @@ -0,0 +1,18 @@ +use warnings; +use strict; +use Test::More tests => 2; + +use XS::APItest; + +XS::APItest::test_cophh(); +ok 1; + +is_deeply XS::APItest::example_cophh_2hv(), { + "foo_1" => 111, + "foo_\x{aa}" => 123, + "foo_\x{bb}" => 456, + "foo_\x{cc}" => 789, + "foo_\x{666}" => 666, +}; + +1; diff --git a/global.sym b/global.sym index b0ac7a6..a5319b5 100644 --- a/global.sym +++ b/global.sym @@ -61,8 +61,6 @@ Perl_ck_warner Perl_ck_warner_d Perl_ckwarn Perl_ckwarn_d -Perl_cop_hints_2hv -Perl_cop_hints_fetchpvn Perl_croak Perl_croak_no_modify Perl_croak_sv @@ -457,8 +455,14 @@ Perl_re_intuit_string Perl_realloc Perl_ref Perl_refcounted_he_chain_2hv -Perl_refcounted_he_fetch -Perl_refcounted_he_new +Perl_refcounted_he_fetch_pv +Perl_refcounted_he_fetch_pvn +Perl_refcounted_he_fetch_sv +Perl_refcounted_he_free +Perl_refcounted_he_inc +Perl_refcounted_he_new_pv +Perl_refcounted_he_new_pvn +Perl_refcounted_he_new_sv Perl_reg_named_buff Perl_reg_named_buff_all Perl_reg_named_buff_exists diff --git a/gv.c b/gv.c index 17ffc2c..6d55245 100644 --- a/gv.c +++ b/gv.c @@ -2021,8 +2021,7 @@ Perl_amagic_call(pTHX_ SV *left, SV *right, int method, int flags) PERL_ARGS_ASSERT_AMAGIC_CALL; if ( PL_curcop->cop_hints & HINT_NO_AMAGIC ) { - SV *lex_mask = Perl_refcounted_he_fetch(aTHX_ PL_curcop->cop_hints_hash, - 0, "overloading", 11, 0, 0); + SV *lex_mask = cop_hints_fetch_pvs(PL_curcop, "overloading", 0); if ( !lex_mask || !SvOK(lex_mask) ) /* overloading lexically disabled */ diff --git a/hv.c b/hv.c index 591865f..c040e25 100644 --- a/hv.c +++ b/hv.c @@ -2627,93 +2627,44 @@ S_refcounted_he_value(pTHX_ const struct refcounted_he *he) SvUTF8_on(value); break; default: - Perl_croak(aTHX_ "panic: refcounted_he_value bad flags %x", - he->refcounted_he_data[0]); + Perl_croak(aTHX_ "panic: refcounted_he_value bad flags %"UVxf, + (UV)he->refcounted_he_data[0]); } return value; } /* -=for apidoc cop_hints_2hv +=for apidoc m|HV *|refcounted_he_chain_2hv|const struct refcounted_he *c|U32 flags -Generates and returns a C from the hinthash in the provided -C. Returns C if there isn't one there. +Generates and returns a C representing the content of a +C chain. +I is currently unused and must be zero. =cut */ HV * -Perl_cop_hints_2hv(pTHX_ const COP *cop) +Perl_refcounted_he_chain_2hv(pTHX_ const struct refcounted_he *chain, U32 flags) { - PERL_ARGS_ASSERT_COP_HINTS_2HV; - - if (!cop->cop_hints_hash) - return NULL; - - return Perl_refcounted_he_chain_2hv(aTHX_ cop->cop_hints_hash); -} - -/* -=for apidoc cop_hints_fetchsv - -Fetches an entry from the hinthash in the provided C. Returns NULL -if the entry isn't there. - -=for apidoc cop_hints_fetchpvn - -See L. If C includes C, C is -in UTF-8. - -=for apidoc cop_hints_fetchpv - -See L. If C includes C, C is -in UTF-8. - -=for apidoc cop_hints_fetchpvs - -See L. This is a macro that takes a constant string -for its argument, which is assumed to be ASCII (rather than UTF-8). - -=cut -*/ -SV * -Perl_cop_hints_fetchpvn(pTHX_ const COP *cop, const char *key, STRLEN klen, - int flags, U32 hash) -{ - PERL_ARGS_ASSERT_COP_HINTS_FETCHPVN; - - /* refcounted_he_fetch takes more flags than we do. Make sure - * noone's depending on being able to pass them here. */ - flags &= ~HVhek_UTF8; - - return Perl_refcounted_he_fetch(aTHX_ cop->cop_hints_hash, NULL, - key, klen, flags, hash); -} - -/* -=for apidoc refcounted_he_chain_2hv + dVAR; + HV *hv; + U32 placeholders, max; -Generates and returns a C by walking up the tree starting at the passed -in C. + if (flags) + Perl_croak(aTHX_ "panic: refcounted_he_chain_2hv bad flags %"UVxf, + (UV)flags); -=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); - + hv = newHV(); + max = HvMAX(hv); if (!HvARRAY(hv)) { char *array; Newxz(array, PERL_HV_ARRAY_ALLOC_BYTES(max + 1), char); HvARRAY(hv) = (HE**)array; } + placeholders = 0; while (chain) { #ifdef USE_ITHREADS U32 hash = chain->refcounted_he_hash; @@ -2790,190 +2741,316 @@ Perl_refcounted_he_chain_2hv(pTHX_ const struct refcounted_he *chain) return hv; } +/* +=for apidoc m|SV *|refcounted_he_fetch_pvn|const struct refcounted_he *chain|const char *keypv|STRLEN keylen|U32 hash|U32 flags + +Search along a C chain for an entry with the key specified +by I and I. If I has the C +bit set, the key octets are interpreted as UTF-8, otherwise they +are interpreted as Latin-1. I is a precomputed hash of the key +string, or zero if it has not been precomputed. Returns a mortal scalar +representing the value associated with the key, or C<&PL_sv_placeholder> +if there is no value associated with the key. + +=cut +*/ + SV * -Perl_refcounted_he_fetch(pTHX_ const struct refcounted_he *chain, SV *keysv, - const char *key, STRLEN klen, int flags, U32 hash) +Perl_refcounted_he_fetch_pvn(pTHX_ const struct refcounted_he *chain, + const char *keypv, STRLEN keylen, U32 hash, U32 flags) { 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; + U8 utf8_flag; + PERL_ARGS_ASSERT_REFCOUNTED_HE_FETCH_PVN; - if (chain) { - /* No point in doing any of this if there's nothing to find. */ - 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 (flags & ~REFCOUNTED_HE_KEY_UTF8) + Perl_croak(aTHX_ "panic: refcounted_he_fetch_pvn bad flags %"UVxf, + (UV)flags); + if (!chain) + return &PL_sv_placeholder; + if (flags & REFCOUNTED_HE_KEY_UTF8) { + /* For searching purposes, canonicalise to Latin-1 where possible. */ + const char *keyend = keypv + keylen, *p; + STRLEN nonascii_count = 0; + for (p = keypv; p != keyend; p++) { + U8 c = (U8)*p; + if (c & 0x80) { + if (!((c & 0xfe) == 0xc2 && ++p != keyend && + (((U8)*p) & 0xc0) == 0x80)) + goto canonicalised_key; + nonascii_count++; + } } - - if (!hash) { - if (keysv && (SvIsCOW_shared_hash(keysv))) { - hash = SvSHARED_HASH(keysv); - } else { - PERL_HASH(hash, key, klen); + if (nonascii_count) { + char *q; + const char *p = keypv, *keyend = keypv + keylen; + keylen -= nonascii_count; + Newx(q, keylen, char); + SAVEFREEPV(q); + keypv = q; + for (; p != keyend; p++, q++) { + U8 c = (U8)*p; + *q = (char) + ((c & 0x80) ? ((c & 0x03) << 6) | (((U8)*++p) & 0x3f) : c); } } + flags &= ~REFCOUNTED_HE_KEY_UTF8; + canonicalised_key: ; + } + utf8_flag = (flags & REFCOUNTED_HE_KEY_UTF8) ? HVhek_UTF8 : 0; + if (!hash) + PERL_HASH(hash, keypv, keylen); - for (; chain; chain = chain->refcounted_he_next) { + for (; chain; chain = chain->refcounted_he_next) { + if ( #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; + hash == chain->refcounted_he_hash && + keylen == chain->refcounted_he_keylen && + memEQ(REF_HE_KEY(chain), keypv, keylen) && + utf8_flag == (chain->refcounted_he_data[0] & HVhek_UTF8) #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; + hash == HEK_HASH(chain->refcounted_he_hek) && + keylen == (STRLEN)HEK_LEN(chain->refcounted_he_hek) && + memEQ(HEK_KEY(chain->refcounted_he_hek), keypv, keylen) && + utf8_flag == (HEK_FLAGS(chain->refcounted_he_hek) & HVhek_UTF8) #endif - - value = sv_2mortal(refcounted_he_value(chain)); - break; - } + ) + return sv_2mortal(refcounted_he_value(chain)); } + return &PL_sv_placeholder; +} - if (flags & HVhek_FREEKEY) - Safefree(key); +/* +=for apidoc m|SV *|refcounted_he_fetch_pv|const struct refcounted_he *chain|const char *key|U32 hash|U32 flags - return value; +Like L, but takes a nul-terminated string +instead of a string/length pair. + +=cut +*/ + +SV * +Perl_refcounted_he_fetch_pv(pTHX_ const struct refcounted_he *chain, + const char *key, U32 hash, U32 flags) +{ + PERL_ARGS_ASSERT_REFCOUNTED_HE_FETCH_PV; + return refcounted_he_fetch_pvn(chain, key, strlen(key), hash, flags); } /* -=for apidoc refcounted_he_new +=for apidoc m|SV *|refcounted_he_fetch_sv|const struct refcounted_he *chain|SV *key|U32 hash|U32 flags + +Like L, but takes a Perl scalar instead of a +string/length pair. + +=cut +*/ -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. +SV * +Perl_refcounted_he_fetch_sv(pTHX_ const struct refcounted_he *chain, + SV *key, U32 hash, U32 flags) +{ + const char *keypv; + STRLEN keylen; + PERL_ARGS_ASSERT_REFCOUNTED_HE_FETCH_SV; + if (flags & REFCOUNTED_HE_KEY_UTF8) + Perl_croak(aTHX_ "panic: refcounted_he_fetch_sv bad flags %"UVxf, + (UV)flags); + keypv = SvPV_const(key, keylen); + if (SvUTF8(key)) + flags |= REFCOUNTED_HE_KEY_UTF8; + if (!hash && SvIsCOW_shared_hash(key)) + hash = SvSHARED_HASH(key); + return refcounted_he_fetch_pvn(chain, keypv, keylen, hash, flags); +} + +/* +=for apidoc m|struct refcounted_he *|refcounted_he_new_pvn|struct refcounted_he *parent|const char *keypv|STRLEN keylen|U32 hash|SV *value|U32 flags + +Creates a new C. This consists of a single key/value +pair and a reference to an existing C chain (which may +be empty), and thus forms a longer chain. When using the longer chain, +the new key/value pair takes precedence over any entry for the same key +further along the chain. + +The new key is specified by I and I. If I has +the C bit set, the key octets are interpreted +as UTF-8, otherwise they are interpreted as Latin-1. I is +a precomputed hash of the key string, or zero if it has not been +precomputed. + +I is the scalar value to store for this key. I is copied +by this function, which thus does not take ownership of any reference +to it, and later changes to the scalar will not be reflected in the +value visible in the C. Complex types of scalar will not +be stored with referential integrity, but will be coerced to strings. +I may be either null or C<&PL_sv_placeholder> to indicate that no +value is to be associated with the key; this, as with any non-null value, +takes precedence over the existence of a value for the key further along +the chain. + +I points to the rest of the C chain to be +attached to the new C. This function takes ownership +of one reference to I, and returns one reference to the new +C. =cut */ struct refcounted_he * -Perl_refcounted_he_new(pTHX_ struct refcounted_he *const parent, - SV *const key, SV *const value) { +Perl_refcounted_he_new_pvn(pTHX_ struct refcounted_he *parent, + const char *keypv, STRLEN keylen, U32 hash, SV *value, U32 flags) +{ dVAR; - STRLEN key_len; - const char *key_p = SvPV_const(key, key_len); STRLEN value_len = 0; const char *value_p = NULL; + bool is_pv; char value_type; - char flags; - bool is_utf8 = SvUTF8(key) ? TRUE : FALSE; + char hekflags; + STRLEN key_offset = 1; + struct refcounted_he *he; + PERL_ARGS_ASSERT_REFCOUNTED_HE_NEW_PVN; - if (SvPOK(value)) { + if (!value || value == &PL_sv_placeholder) { + value_type = HVrhek_delete; + } else if (SvPOK(value)) { value_type = HVrhek_PV; } else if (SvIOK(value)) { value_type = SvUOK((const SV *)value) ? HVrhek_UV : 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) { + is_pv = value_type == HVrhek_PV; + if (is_pv) { /* Do it this way so that the SvUTF8() test is after the SvPV, in case the value is overloaded, and doesn't yet have the UTF-8flag set. */ value_p = SvPV_const(value, value_len); if (SvUTF8(value)) value_type = HVrhek_PV_UTF8; + key_offset = value_len + 2; + } + hekflags = value_type; + + if (flags & REFCOUNTED_HE_KEY_UTF8) { + /* Canonicalise to Latin-1 where possible. */ + const char *keyend = keypv + keylen, *p; + STRLEN nonascii_count = 0; + for (p = keypv; p != keyend; p++) { + U8 c = (U8)*p; + if (c & 0x80) { + if (!((c & 0xfe) == 0xc2 && ++p != keyend && + (((U8)*p) & 0xc0) == 0x80)) + goto canonicalised_key; + nonascii_count++; + } + } + if (nonascii_count) { + char *q; + const char *p = keypv, *keyend = keypv + keylen; + keylen -= nonascii_count; + Newx(q, keylen, char); + SAVEFREEPV(q); + keypv = q; + for (; p != keyend; p++, q++) { + U8 c = (U8)*p; + *q = (char) + ((c & 0x80) ? ((c & 0x03) << 6) | (((U8)*++p) & 0x3f) : c); + } + } + flags &= ~REFCOUNTED_HE_KEY_UTF8; + canonicalised_key: ; } - flags = value_type; - - 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; - } - - return refcounted_he_new_common(parent, key_p, key_len, flags, value_type, - ((value_type == HVrhek_PV - || value_type == HVrhek_PV_UTF8) ? - (void *)value_p : (void *)value), - value_len); -} - -static struct refcounted_he * -S_refcounted_he_new_common(pTHX_ struct refcounted_he *const parent, - const char *const key_p, const STRLEN key_len, - const char flags, char value_type, - const void *value, const STRLEN value_len) { - dVAR; - struct refcounted_he *he; - U32 hash; - const bool is_pv = value_type == HVrhek_PV || value_type == HVrhek_PV_UTF8; - STRLEN key_offset = is_pv ? value_len + 2 : 1; + if (flags & REFCOUNTED_HE_KEY_UTF8) + hekflags |= HVhek_UTF8; + if (!hash) + PERL_HASH(hash, keypv, keylen); - PERL_ARGS_ASSERT_REFCOUNTED_HE_NEW_COMMON; - -#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); +#ifdef USE_ITHREADS + + keylen #endif + + key_offset); he->refcounted_he_next = parent; if (is_pv) { - Copy((char *)value, he->refcounted_he_data + 1, value_len + 1, char); + Copy(value_p, he->refcounted_he_data + 1, value_len + 1, char); he->refcounted_he_val.refcounted_he_u_len = value_len; } else if (value_type == HVrhek_IV) { - he->refcounted_he_val.refcounted_he_u_iv = SvIVX((const SV *)value); + he->refcounted_he_val.refcounted_he_u_iv = SvIVX(value); } else if (value_type == HVrhek_UV) { - he->refcounted_he_val.refcounted_he_u_uv = SvUVX((const SV *)value); + he->refcounted_he_val.refcounted_he_u_uv = SvUVX(value); } - 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); + he->refcounted_he_keylen = keylen; + Copy(keypv, he->refcounted_he_data + key_offset, keylen, char); #else - he->refcounted_he_hek = share_hek_flags(key_p, key_len, hash, flags); + he->refcounted_he_hek = share_hek_flags(keypv, keylen, hash, hekflags); #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_data[0] = hekflags; he->refcounted_he_refcnt = 1; return he; } /* -=for apidoc refcounted_he_free +=for apidoc m|struct refcounted_he *|refcounted_he_new_pv|struct refcounted_he *parent|const char *key|U32 hash|SV *value|U32 flags -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. +Like L, but takes a nul-terminated string instead +of a string/length pair. + +=cut +*/ + +struct refcounted_he * +Perl_refcounted_he_new_pv(pTHX_ struct refcounted_he *parent, + const char *key, U32 hash, SV *value, U32 flags) +{ + PERL_ARGS_ASSERT_REFCOUNTED_HE_NEW_PV; + return refcounted_he_new_pvn(parent, key, strlen(key), hash, value, flags); +} + +/* +=for apidoc m|struct refcounted_he *|refcounted_he_new_sv|struct refcounted_he *parent|SV *key|U32 hash|SV *value|U32 flags + +Like L, but takes a Perl scalar instead of a +string/length pair. + +=cut +*/ + +struct refcounted_he * +Perl_refcounted_he_new_sv(pTHX_ struct refcounted_he *parent, + SV *key, U32 hash, SV *value, U32 flags) +{ + const char *keypv; + STRLEN keylen; + PERL_ARGS_ASSERT_REFCOUNTED_HE_NEW_SV; + if (flags & REFCOUNTED_HE_KEY_UTF8) + Perl_croak(aTHX_ "panic: refcounted_he_new_sv bad flags %"UVxf, + (UV)flags); + keypv = SvPV_const(key, keylen); + if (SvUTF8(key)) + flags |= REFCOUNTED_HE_KEY_UTF8; + if (!hash && SvIsCOW_shared_hash(key)) + hash = SvSHARED_HASH(key); + return refcounted_he_new_pvn(parent, keypv, keylen, hash, value, flags); +} + +/* +=for apidoc m|void|refcounted_he_free|struct refcounted_he *he + +Decrements the reference count of a C by one. If the +reference count reaches zero the structure's memory is freed, which +(recursively) causes a reduction of its parent C's +reference count. It is safe to pass a null pointer to this function: +no action occurs in this case. =cut */ @@ -3004,6 +3081,27 @@ Perl_refcounted_he_free(pTHX_ struct refcounted_he *he) { } } +/* +=for apidoc m|struct refcounted_he *|refcounted_he_inc|struct refcounted_he *he + +Increment the reference count of a C. The pointer to the +C is also returned. It is safe to pass a null pointer +to this function: no action occurs and a null pointer is returned. + +=cut +*/ + +struct refcounted_he * +Perl_refcounted_he_inc(pTHX_ struct refcounted_he *he) +{ + if (he) { + HINTS_REFCNT_LOCK; + he->refcounted_he_refcnt++; + HINTS_REFCNT_UNLOCK; + } + return he; +} + /* pp_entereval is aware that labels are stored with a key ':' at the top of the linked list. */ const char * @@ -3044,16 +3142,17 @@ void Perl_store_cop_label(pTHX_ COP *const cop, const char *label, STRLEN len, U32 flags) { + SV *labelsv; PERL_ARGS_ASSERT_STORE_COP_LABEL; if (flags & ~(SVf_UTF8)) Perl_croak(aTHX_ "panic: store_cop_label illegal flag bits 0x%" UVxf, (UV)flags); - + labelsv = sv_2mortal(newSVpvn(label, len)); + if (flags & SVf_UTF8) + SvUTF8_on(labelsv); cop->cop_hints_hash - = refcounted_he_new_common(cop->cop_hints_hash, ":", 1, HVrhek_PV, - flags & SVf_UTF8 ? HVrhek_PV_UTF8 : HVrhek_PV, - label, len); + = refcounted_he_new_pvs(cop->cop_hints_hash, ":", labelsv, 0); } /* diff --git a/hv.h b/hv.h index 83f90d9..6fa3252 100644 --- a/hv.h +++ b/hv.h @@ -451,18 +451,11 @@ C. between threads (because it hangs from OPs, which are shared), hence the alternate definition and mutex. */ -#define cop_hints_fetchsv(cop, keysv, hash) \ - Perl_cop_hints_fetchpvn(aTHX_ (cop), SvPV_nolen(keysv), SvCUR(keysv), \ - (SvUTF8(keysv) ? HVhek_UTF8 : 0), (hash)) - -#define cop_hints_fetchpv(cop, key, flags, hash) \ - Perl_cop_hints_fetchpvn(aTHX_ (cop), key, strlen(key), (flags), (hash)) - -#define cop_hints_fetchpvs(cop, key) \ - Perl_cop_hints_fetchpvn(aTHX_ (cop), STR_WITH_LEN(key), 0, 0) - struct refcounted_he; +/* flags for the refcounted_he API */ +#define REFCOUNTED_HE_KEY_UTF8 0x00000001 + #ifdef PERL_CORE /* Gosh. This really isn't a good name any longer. */ @@ -486,6 +479,30 @@ struct refcounted_he { char refcounted_he_data[1]; }; +/* +=for apidoc m|SV *|refcounted_he_fetch_pvs|const struct refcounted_he *chain|const char *key|U32 flags + +Like L, but takes a literal string instead of +a string/length pair, and no precomputed hash. + +=cut +*/ + +#define refcounted_he_fetch_pvs(chain, key, flags) \ + Perl_refcounted_he_fetch_pvn(aTHX_ chain, STR_WITH_LEN(key), 0, flags) + +/* +=for apidoc m|struct refcounted_he *|refcounted_he_new_pvs|struct refcounted_he *parent|const char *key|SV *value|U32 flags + +Like L, but takes a literal string instead of +a string/length pair, and no precomputed hash. + +=cut +*/ + +#define refcounted_he_new_pvs(parent, key, value, flags) \ + Perl_refcounted_he_new_pvn(aTHX_ parent, STR_WITH_LEN(key), 0, value, flags) + /* Flag bits are HVhek_UTF8, HVhek_WASUTF8, then */ #define HVrhek_undef 0x00 /* Value is undef. */ #define HVrhek_delete 0x10 /* Value is placeholder - signifies delete. */ diff --git a/mg.c b/mg.c index b96a1c1..03ff000 100644 --- a/mg.c +++ b/mg.c @@ -781,17 +781,13 @@ Perl_emulate_cop_io(pTHX_ const COP *const c, SV *const sv) sv_setpvs(sv, ""); SvUTF8_off(sv); if ((CopHINTS_get(c) & HINT_LEXICAL_IO_IN)) { - SV *const value = Perl_refcounted_he_fetch(aTHX_ - c->cop_hints_hash, - 0, "open<", 5, 0, 0); + SV *const value = cop_hints_fetch_pvs(c, "open<", 0); assert(value); sv_catsv(sv, value); } sv_catpvs(sv, "\0"); if ((CopHINTS_get(c) & HINT_LEXICAL_IO_OUT)) { - SV *const value = Perl_refcounted_he_fetch(aTHX_ - c->cop_hints_hash, - 0, "open>", 5, 0, 0); + SV *const value = cop_hints_fetch_pvs(c, "open>", 0); assert(value); sv_catsv(sv, value); } @@ -3170,8 +3166,8 @@ Perl_magic_sethint(pTHX_ SV *sv, MAGIC *mg) Doing this here saves a lot of doing it manually in perl code (and forgetting to do it, and consequent subtle errors. */ PL_hints |= HINT_LOCALIZE_HH; - PL_compiling.cop_hints_hash - = Perl_refcounted_he_new(aTHX_ PL_compiling.cop_hints_hash, key, sv); + CopHINTHASH_set(&PL_compiling, + cophh_store_sv(CopHINTHASH_get(&PL_compiling), key, 0, sv, 0)); return 0; } @@ -3196,9 +3192,9 @@ Perl_magic_clearhint(pTHX_ SV *sv, MAGIC *mg) PERL_UNUSED_ARG(sv); PL_hints |= HINT_LOCALIZE_HH; - PL_compiling.cop_hints_hash - = Perl_refcounted_he_new(aTHX_ PL_compiling.cop_hints_hash, - MUTABLE_SV(mg->mg_ptr), &PL_sv_placeholder); + CopHINTHASH_set(&PL_compiling, + cophh_delete_sv(CopHINTHASH_get(&PL_compiling), + MUTABLE_SV(mg->mg_ptr), 0, 0)); return 0; } @@ -3215,10 +3211,8 @@ Perl_magic_clearhints(pTHX_ SV *sv, MAGIC *mg) PERL_ARGS_ASSERT_MAGIC_CLEARHINTS; PERL_UNUSED_ARG(sv); PERL_UNUSED_ARG(mg); - if (PL_compiling.cop_hints_hash) { - Perl_refcounted_he_free(aTHX_ PL_compiling.cop_hints_hash); - PL_compiling.cop_hints_hash = NULL; - } + cophh_free(CopHINTHASH_get(&PL_compiling)); + CopHINTHASH_set(&PL_compiling, cophh_new_empty()); return 0; } diff --git a/op.c b/op.c index acffe22..ac9a41e 100644 --- a/op.c +++ b/op.c @@ -717,7 +717,7 @@ S_cop_free(pTHX_ COP* cop) CopSTASH_free(cop); if (! specialWARN(cop->cop_warnings)) PerlMemShared_free(cop->cop_warnings); - Perl_refcounted_he_free(aTHX_ cop->cop_hints_hash); + cophh_free(CopHINTHASH_get(cop)); } STATIC void @@ -3735,12 +3735,12 @@ Perl_newPMOP(pTHX_ I32 type, I32 flags) pmop->op_pmflags |= RXf_PMf_UNICODE; } if (PL_hints & HINT_RE_FLAGS) { - SV *reflags = Perl_refcounted_he_fetch(aTHX_ - PL_compiling.cop_hints_hash, 0, STR_WITH_LEN("reflags"), 0, 0 + SV *reflags = Perl_refcounted_he_fetch_pvn(aTHX_ + PL_compiling.cop_hints_hash, STR_WITH_LEN("reflags"), 0, 0 ); if (reflags && SvOK(reflags)) pmop->op_pmflags |= SvIV(reflags); - reflags = Perl_refcounted_he_fetch(aTHX_ - PL_compiling.cop_hints_hash, 0, STR_WITH_LEN("reflags_dul"), 0, 0 + reflags = Perl_refcounted_he_fetch_pvn(aTHX_ + PL_compiling.cop_hints_hash, STR_WITH_LEN("reflags_dul"), 0, 0 ); if (reflags && SvOK(reflags)) { pmop->op_pmflags &= ~(RXf_PMf_LOCALE|RXf_PMf_UNICODE); @@ -4798,12 +4798,7 @@ Perl_newSTATEOP(pTHX_ I32 flags, char *label, OP *o) CopHINTS and a possible value in cop_hints_hash, so no need to copy it. */ cop->cop_warnings = DUP_WARNINGS(PL_curcop->cop_warnings); - cop->cop_hints_hash = PL_curcop->cop_hints_hash; - if (cop->cop_hints_hash) { - HINTS_REFCNT_LOCK; - cop->cop_hints_hash->refcounted_he_refcnt++; - HINTS_REFCNT_UNLOCK; - } + CopHINTHASH_set(cop, cophh_copy(CopHINTHASH_get(PL_curcop))); if (label) { Perl_store_cop_label(aTHX_ cop, label, strlen(label), 0); diff --git a/perl.c b/perl.c index 95517bb23..157cd6b 100644 --- a/perl.c +++ b/perl.c @@ -1026,8 +1026,8 @@ perl_destruct(pTHXx) if (!specialWARN(PL_compiling.cop_warnings)) PerlMemShared_free(PL_compiling.cop_warnings); PL_compiling.cop_warnings = NULL; - Perl_refcounted_he_free(aTHX_ PL_compiling.cop_hints_hash); - PL_compiling.cop_hints_hash = NULL; + cophh_free(CopHINTHASH_get(&PL_compiling)); + CopHINTHASH_set(&PL_compiling, cophh_new_empty()); CopFILE_free(&PL_compiling); CopSTASH_free(&PL_compiling); diff --git a/perlio.c b/perlio.c index 547f0b4..1440048 100644 --- a/perlio.c +++ b/perlio.c @@ -5232,8 +5232,7 @@ Perl_PerlIO_context_layers(pTHX_ const char *mode) if (!direction) return NULL; - layers = Perl_refcounted_he_fetch(aTHX_ PL_curcop->cop_hints_hash, - 0, direction, 5, 0, 0); + layers = cop_hints_fetch_pvn(PL_curcop, direction, 5, 0, 0); assert(layers); return SvOK(layers) ? SvPV_nolen_const(layers) : NULL; diff --git a/pp_ctl.c b/pp_ctl.c index dfbd3ad..46c6a0b 100644 --- a/pp_ctl.c +++ b/pp_ctl.c @@ -194,8 +194,7 @@ PP(pp_regcomp) PM_SETRE(pm, NULL); /* crucial if regcomp aborts */ #endif } else if (PL_curcop->cop_hints_hash) { - SV *ptr = Perl_refcounted_he_fetch(aTHX_ PL_curcop->cop_hints_hash, 0, - "regcomp", 7, 0, 0); + SV *ptr = cop_hints_fetch_pvs(PL_curcop, "regcomp", 0); if (ptr && SvIOK(ptr) && SvIV(ptr)) eng = INT2PTR(regexp_engine*,SvIV(ptr)); } @@ -1915,9 +1914,7 @@ PP(pp_caller) } PUSHs(cx->blk_oldcop->cop_hints_hash ? - sv_2mortal(newRV_noinc( - MUTABLE_SV(Perl_refcounted_he_chain_2hv(aTHX_ - cx->blk_oldcop->cop_hints_hash)))) + sv_2mortal(newRV_noinc(MUTABLE_SV(cop_hints_2hv(cx->blk_oldcop, 0)))) : &PL_sv_undef); RETURN; } @@ -3892,25 +3889,18 @@ PP(pp_entereval) } SAVECOMPILEWARNINGS(); PL_compiling.cop_warnings = DUP_WARNINGS(PL_curcop->cop_warnings); - if (PL_compiling.cop_hints_hash) { - Perl_refcounted_he_free(aTHX_ PL_compiling.cop_hints_hash); - } + cophh_free(CopHINTHASH_get(&PL_compiling)); if (Perl_fetch_cop_label(aTHX_ PL_curcop, NULL, NULL)) { /* The label, if present, is the first entry on the chain. So rather than writing a blank label in front of it (which involves an allocation), just use the next entry in the chain. */ PL_compiling.cop_hints_hash - = PL_curcop->cop_hints_hash->refcounted_he_next; + = cophh_copy(PL_curcop->cop_hints_hash->refcounted_he_next); /* Check the assumption that this removed the label. */ assert(Perl_fetch_cop_label(aTHX_ &PL_compiling, NULL, NULL) == NULL); } else - PL_compiling.cop_hints_hash = PL_curcop->cop_hints_hash; - if (PL_compiling.cop_hints_hash) { - HINTS_REFCNT_LOCK; - PL_compiling.cop_hints_hash->refcounted_he_refcnt++; - HINTS_REFCNT_UNLOCK; - } + PL_compiling.cop_hints_hash = cophh_copy(PL_curcop->cop_hints_hash); /* special case: an eval '' executed within the DB package gets lexically * placed in the first non-DB CV rather than the current CV - this * allows the debugger to execute code, find lexicals etc, in the diff --git a/proto.h b/proto.h index ce9aaf3..cfa1242 100644 --- a/proto.h +++ b/proto.h @@ -532,29 +532,6 @@ PERL_CALLCONV bool Perl_ckwarn_d(pTHX_ U32 w); PERL_CALLCONV OP* Perl_convert(pTHX_ I32 optype, I32 flags, OP* o) __attribute__warn_unused_result__; -PERL_CALLCONV HV* Perl_cop_hints_2hv(pTHX_ const COP *cop) - __attribute__nonnull__(pTHX_1); -#define PERL_ARGS_ASSERT_COP_HINTS_2HV \ - assert(cop) - -/* PERL_CALLCONV SV* cop_hints_fetchpv(pTHX_ const COP *cop, const char *const key, int flags, U32 hash) - __attribute__nonnull__(pTHX_1) - __attribute__nonnull__(pTHX_2); */ - -PERL_CALLCONV SV* Perl_cop_hints_fetchpvn(pTHX_ const COP *cop, const char *key, STRLEN klen, int flags, U32 hash) - __attribute__nonnull__(pTHX_1) - __attribute__nonnull__(pTHX_2); -#define PERL_ARGS_ASSERT_COP_HINTS_FETCHPVN \ - assert(cop); assert(key) - -/* PERL_CALLCONV SV* cop_hints_fetchpvs(pTHX_ const COP *cop, const char *const key) - __attribute__nonnull__(pTHX_1) - __attribute__nonnull__(pTHX_2); */ - -/* PERL_CALLCONV SV* cop_hints_fetchsv(pTHX_ const COP *cop, SV *keysv, U32 hash) - __attribute__nonnull__(pTHX_1) - __attribute__nonnull__(pTHX_2); */ - PERL_CALLCONV PERL_CONTEXT* Perl_create_eval_scope(pTHX_ U32 flags); PERL_CALLCONV void Perl_croak(pTHX_ const char* pat, ...) __attribute__noreturn__ @@ -3292,10 +3269,39 @@ PERL_CALLCONV Malloc_t Perl_realloc(Malloc_t where, MEM_SIZE nbytes) __attribute__warn_unused_result__; /* PERL_CALLCONV OP* Perl_ref(pTHX_ OP* o, I32 type); */ -PERL_CALLCONV HV * Perl_refcounted_he_chain_2hv(pTHX_ const struct refcounted_he *c); -PERL_CALLCONV SV * Perl_refcounted_he_fetch(pTHX_ const struct refcounted_he *chain, SV *keysv, const char *key, STRLEN klen, int flags, U32 hash); +PERL_CALLCONV HV * Perl_refcounted_he_chain_2hv(pTHX_ const struct refcounted_he *c, U32 flags); +PERL_CALLCONV SV * Perl_refcounted_he_fetch_pv(pTHX_ const struct refcounted_he *chain, const char *key, U32 hash, U32 flags) + __attribute__nonnull__(pTHX_2); +#define PERL_ARGS_ASSERT_REFCOUNTED_HE_FETCH_PV \ + assert(key) + +PERL_CALLCONV SV * Perl_refcounted_he_fetch_pvn(pTHX_ const struct refcounted_he *chain, const char *keypv, STRLEN keylen, U32 hash, U32 flags) + __attribute__nonnull__(pTHX_2); +#define PERL_ARGS_ASSERT_REFCOUNTED_HE_FETCH_PVN \ + assert(keypv) + +PERL_CALLCONV SV * Perl_refcounted_he_fetch_sv(pTHX_ const struct refcounted_he *chain, SV *key, U32 hash, U32 flags) + __attribute__nonnull__(pTHX_2); +#define PERL_ARGS_ASSERT_REFCOUNTED_HE_FETCH_SV \ + assert(key) + PERL_CALLCONV void Perl_refcounted_he_free(pTHX_ struct refcounted_he *he); -PERL_CALLCONV struct refcounted_he * Perl_refcounted_he_new(pTHX_ struct refcounted_he *const parent, SV *const key, SV *const value); +PERL_CALLCONV struct refcounted_he * Perl_refcounted_he_inc(pTHX_ struct refcounted_he *he); +PERL_CALLCONV struct refcounted_he * Perl_refcounted_he_new_pv(pTHX_ struct refcounted_he *parent, const char *key, U32 hash, SV *value, U32 flags) + __attribute__nonnull__(pTHX_2); +#define PERL_ARGS_ASSERT_REFCOUNTED_HE_NEW_PV \ + assert(key) + +PERL_CALLCONV struct refcounted_he * Perl_refcounted_he_new_pvn(pTHX_ struct refcounted_he *parent, const char *keypv, STRLEN keylen, U32 hash, SV *value, U32 flags) + __attribute__nonnull__(pTHX_2); +#define PERL_ARGS_ASSERT_REFCOUNTED_HE_NEW_PVN \ + assert(keypv) + +PERL_CALLCONV struct refcounted_he * Perl_refcounted_he_new_sv(pTHX_ struct refcounted_he *parent, SV *key, U32 hash, SV *value, U32 flags) + __attribute__nonnull__(pTHX_2); +#define PERL_ARGS_ASSERT_REFCOUNTED_HE_NEW_SV \ + assert(key) + PERL_CALLCONV SV* Perl_reg_named_buff(pTHX_ REGEXP * const rx, SV * const key, SV * const value, const U32 flags) __attribute__nonnull__(pTHX_1); #define PERL_ARGS_ASSERT_REG_NAMED_BUFF \ @@ -5553,12 +5559,6 @@ STATIC HE* S_new_he(pTHX) __attribute__malloc__ __attribute__warn_unused_result__; -STATIC struct refcounted_he * S_refcounted_he_new_common(pTHX_ struct refcounted_he *const parent, const char *const key_p, const STRLEN key_len, const char flags, char value_type, const void *value, const STRLEN value_len) - __attribute__nonnull__(pTHX_2) - __attribute__nonnull__(pTHX_6); -#define PERL_ARGS_ASSERT_REFCOUNTED_HE_NEW_COMMON \ - assert(key_p); assert(value) - STATIC SV * S_refcounted_he_value(pTHX_ const struct refcounted_he *he) __attribute__nonnull__(pTHX_1); #define PERL_ARGS_ASSERT_REFCOUNTED_HE_VALUE \ diff --git a/scope.c b/scope.c index 0d07071..9c1831c 100644 --- a/scope.c +++ b/scope.c @@ -598,17 +598,12 @@ void Perl_save_hints(pTHX) { dVAR; - if (PL_compiling.cop_hints_hash) { - HINTS_REFCNT_LOCK; - PL_compiling.cop_hints_hash->refcounted_he_refcnt++; - HINTS_REFCNT_UNLOCK; - } + COPHH *save_cophh = cophh_copy(CopHINTHASH_get(&PL_compiling)); if (PL_hints & HINT_LOCALIZE_HH) { - save_pushptri32ptr(GvHV(PL_hintgv), PL_hints, - PL_compiling.cop_hints_hash, SAVEt_HINTS); + save_pushptri32ptr(GvHV(PL_hintgv), PL_hints, save_cophh, SAVEt_HINTS); GvHV(PL_hintgv) = hv_copy_hints_hv(GvHV(PL_hintgv)); } else { - save_pushi32ptr(PL_hints, PL_compiling.cop_hints_hash, SAVEt_HINTS); + save_pushi32ptr(PL_hints, save_cophh, SAVEt_HINTS); } } @@ -1019,8 +1014,8 @@ Perl_leave_scope(pTHX_ I32 base) SvREFCNT_dec(MUTABLE_SV(GvHV(PL_hintgv))); GvHV(PL_hintgv) = NULL; } - Perl_refcounted_he_free(aTHX_ PL_compiling.cop_hints_hash); - PL_compiling.cop_hints_hash = (struct refcounted_he *) SSPOPPTR; + cophh_free(CopHINTHASH_get(&PL_compiling)); + CopHINTHASH_set(&PL_compiling, (COPHH*)SSPOPPTR); *(I32*)&PL_hints = (I32)SSPOPINT; if (PL_hints & HINT_LOCALIZE_HH) { SvREFCNT_dec(MUTABLE_SV(GvHV(PL_hintgv))); diff --git a/sv.c b/sv.c index 500c7c7..bfafd73 100644 --- a/sv.c +++ b/sv.c @@ -12230,11 +12230,7 @@ Perl_ss_dup(pTHX_ PerlInterpreter *proto_perl, CLONE_PARAMS* param) break; case SAVEt_HINTS: ptr = POPPTR(ss,ix); - if (ptr) { - HINTS_REFCNT_LOCK; - ((struct refcounted_he *)ptr)->refcounted_he_refcnt++; - HINTS_REFCNT_UNLOCK; - } + ptr = cophh_copy((COPHH*)ptr); TOPPTR(nss,ix) = ptr; i = POPINT(ss,ix); TOPINT(nss,ix) = i; @@ -12587,11 +12583,7 @@ perl_clone_using(PerlInterpreter *proto_perl, UV flags, ptr_table_store(PL_ptr_table, &proto_perl->Icompiling, &PL_compiling); PL_compiling.cop_warnings = DUP_WARNINGS(PL_compiling.cop_warnings); - if (PL_compiling.cop_hints_hash) { - HINTS_REFCNT_LOCK; - PL_compiling.cop_hints_hash->refcounted_he_refcnt++; - HINTS_REFCNT_UNLOCK; - } + CopHINTHASH_set(&PL_compiling, cophh_copy(CopHINTHASH_get(&PL_compiling))); PL_curcop = (COP*)any_dup(proto_perl->Icurcop, proto_perl); #ifdef PERL_DEBUG_READONLY_OPS PL_slabs = NULL; -- 2.7.4