X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=ext%2FHash%2FUtil%2FFieldHash%2FFieldHash.xs;h=972f99dad3b3f0e44f40e9ac79f898992cde5c9f;hb=d74d639bc8e61d741d9a79acc1bd92b4db9c8347;hp=14e60f15a5c19b7e788ec211f614a54c1369c41d;hpb=a607227a3e6385121d3bf032568fb6b9c2dd6e68;p=p5sagit%2Fp5-mst-13.2.git diff --git a/ext/Hash/Util/FieldHash/FieldHash.xs b/ext/Hash/Util/FieldHash/FieldHash.xs index 14e60f1..972f99d 100644 --- a/ext/Hash/Util/FieldHash/FieldHash.xs +++ b/ext/Hash/Util/FieldHash/FieldHash.xs @@ -4,12 +4,13 @@ /* support for Hash::Util::FieldHash, prefix HUF_ */ -/* The object registry, a package variable */ -#define HUF_OB_REG "Hash::Util::FieldHash::ob_reg" +/* A Perl sub that returns a hashref to the object registry */ +#define HUF_OB_REG "Hash::Util::FieldHash::_ob_reg" /* Magic cookies to recognize object id's. Hi, Eva, David */ #define HUF_COOKIE 2805.1980 #define HUF_REFADDR_COOKIE 1811.1976 - +/* Identifier for PERL_MAGIC_ext magic */ +#define HUF_IDCACHE 0x4944 /* For global cache of object registry */ #define MY_CXT_KEY "Hash::Util::FieldHash::_guts" XS_VERSION @@ -18,6 +19,29 @@ typedef struct { } my_cxt_t; START_MY_CXT +/* Inquire the object registry (a lexical hash) from perl */ +HV* HUF_get_ob_reg(void) { + dSP; + HV* ob_reg = NULL; + I32 items; + ENTER; + SAVETMPS; + + PUSHMARK(SP); + items = call_pv(HUF_OB_REG, G_SCALAR|G_NOARGS); + SPAGAIN; + + if (items == 1 && TOPs && SvROK(TOPs) && SvTYPE(SvRV(TOPs)) == SVt_PVHV) + ob_reg = (HV*)SvRV(POPs); + PUTBACK; + FREETMPS; + LEAVE; + + if (!ob_reg) + Perl_die(aTHX_ "Can't get object registry hash"); + return ob_reg; +} + /* Deal with global context */ #define HUF_INIT 1 #define HUF_CLONE 0 @@ -26,44 +50,49 @@ START_MY_CXT void HUF_global(I32 how) { if (how == HUF_INIT) { MY_CXT_INIT; - MY_CXT.ob_reg = get_hv(HUF_OB_REG, 1); + MY_CXT.ob_reg = HUF_get_ob_reg(); } else if (how == HUF_CLONE) { MY_CXT_CLONE; - MY_CXT.ob_reg = get_hv(HUF_OB_REG, 0); + MY_CXT.ob_reg = HUF_get_ob_reg(); } else if (how == HUF_RESET) { dMY_CXT; - MY_CXT.ob_reg = get_hv(HUF_OB_REG, 0); + MY_CXT.ob_reg = HUF_get_ob_reg(); } } -/* the id as an SV, optionally marked in the nv (unused feature) */ -SV* HUF_id(SV* ref, NV cookie) { - SV* id = sv_newmortal(); - if (cookie == 0 ) { - SvUPGRADE(id, SVt_PVIV); - } else { - SvUPGRADE(id, SVt_PVNV); - SvNV_set(id, cookie); - SvNOK_on(id); - } - SvIV_set(id, (IV)SvRV(ref)); - SvIOK_on(id); - return id; -} +/* Object id */ -/* plain id, only used for field hash entries in field lists */ -SV* HUF_field_id(SV* obj) { - return HUF_id(obj, 0.0); -} +/* definition of id transformation */ +#define HUF_OBJ_ID(x) newSVuv(PTR2UV(x)) -/* object id (may be different in future) */ SV* HUF_obj_id(SV* obj) { - return HUF_id(obj, 0.0); + SV *item = SvRV(obj); + MAGIC *mg; + SV *id; + + /* Get cached object ID, if it exists */ + if (SvTYPE(item) >= SVt_PVMG) { + for ( mg = SvMAGIC(item); mg; mg = mg->mg_moremagic ) { + if ((mg->mg_type == PERL_MAGIC_ext) && + (mg->mg_private == HUF_IDCACHE) + ) { + return mg->mg_obj; + } + } + } + + /* Create an object ID, cache it */ + id = HUF_OBJ_ID(item); + mg = sv_magicext(item, id, PERL_MAGIC_ext, NULL, NULL, 0); + mg->mg_private = HUF_IDCACHE; + + /* Return the object ID */ + return id; } /* set up uvar magic for any sv */ void HUF_add_uvar_magic( - SV* sv, /* the sv to enchant, visible to * get/set */ + SV* sv, /* the sv to enchant, visible to get/set */ I32(* val)(pTHX_ IV, SV*), /* "get" function */ I32(* set)(pTHX_ IV, SV*), /* "set" function */ I32 index, /* get/set will see this */ @@ -85,7 +114,8 @@ AV* HUF_get_trigger_content(SV* trigger) { } /* Delete an object from all field hashes it may occur in. Also delete - * the object's entry from the object registry. + * the object's entry from the object registry. This function goes in + * the uf_set field of the uvar magic of a trigger. */ I32 HUF_destroy_obj(pTHX_ IV index, SV* trigger) { /* Do nothing if the weakref wasn't undef'd. Also don't bother @@ -104,17 +134,19 @@ I32 HUF_destroy_obj(pTHX_ IV index, SV* trigger) { } /* make it safe in case we must run in global clenaup, after all */ if (PL_in_clean_all) - HUF_global(HUF_RESET); + HUF_global(HUF_RESET); /* shoudn't be needed */ hv_delete_ent(MY_CXT.ob_reg, ob_id, G_DISCARD, 0); } return 0; } -/* Create a trigger for an object. The trigger is a magical weak ref - * that fires when the weak ref expires. it holds the original id of - * the object, and a list of field hashes from which the object may - * have to be deleted. The trigger is stored in the object registry - * and also deleted when the object expires. +/* Create a trigger for an object. The trigger is a magical SV + * that holds a weak ref to the object. The magic fires when the object + * expires and takes care of garbage collection in registred hashes. + * For that purpose, the magic structure holds the original id of + * the object, and a list (a hash, really) of hashes from which the + * object may * have to be deleted. The trigger is stored in the + * object registry and is also deleted when the object expires. */ SV* HUF_new_trigger(SV* obj, SV* ob_id) { dMY_CXT; @@ -138,41 +170,68 @@ SV* HUF_ask_trigger(SV* ob_id) { } /* get the trigger for an object, creating it if necessary */ -SV* HUF_get_trigger(SV* obj, SV* ob_id) { +SV* HUF_get_trigger0(SV* obj, SV* ob_id) { SV* trigger; if (!(trigger = HUF_ask_trigger(ob_id))) trigger = HUF_new_trigger(obj, ob_id); return trigger; } -/* mark an object (trigger) as having been used with a field */ +SV* HUF_get_trigger(SV* obj, SV* ob_id) { + SV* trigger = HUF_ask_trigger(ob_id); + if (!trigger) + trigger = HUF_new_trigger(obj, ob_id); + return( trigger); +} + +/* mark an object (trigger) as having been used with a field + (a clenup-liability) +*/ void HUF_mark_field(SV* trigger, SV* field) { AV* cont = HUF_get_trigger_content(trigger); HV* field_tab = (HV*) *av_fetch(cont, 1, 0); SV* field_ref = newRV_inc(field); - SV* field_id = HUF_field_id(field_ref); - hv_store_ent(field_tab, field_id, field_ref, 0); + UV field_addr = PTR2UV(field); + hv_store(field_tab, (char *)&field_addr, sizeof(field_addr), field_ref, 0); } -#define HV_FETCH_ISSTORE 0x01 -#define HV_FETCH_ISEXISTS 0x02 -#define HV_FETCH_LVALUE 0x04 -#define HV_FETCH_JUST_SV 0x08 - -#define HUF_WOULD_CREATE_KEY(x) ((x) != -1 && ((x) & (HV_FETCH_ISSTORE | HV_FETCH_LVALUE))) +/* Determine, from the value of action, whether this call may create a new + * hash key */ +#define HUF_WOULD_CREATE_KEY(x) ((x) != HV_DELETE && ((x) & (HV_FETCH_ISSTORE | HV_FETCH_LVALUE))) -/* The key exchange function. It communicates with S_hv_magic_uvar_xkey +/* The key exchange functions. They communicate with S_hv_magic_uvar_xkey * in hv.c */ -I32 HUF_watch_key(pTHX_ IV action, SV* field) { +I32 HUF_watch_key_safe(pTHX_ IV action, SV* field) { MAGIC* mg = mg_find(field, PERL_MAGIC_uvar); - SV* keysv = mg->mg_obj; - if (keysv && SvROK(keysv)) { - SV* ob_id = HUF_obj_id(keysv); - mg->mg_obj = ob_id; /* key replacement */ - if (HUF_WOULD_CREATE_KEY(action)) { - SV* trigger = HUF_get_trigger(keysv, ob_id); - HUF_mark_field(trigger, field); + SV* keysv; + if (mg && (keysv = mg->mg_obj)) { + if (SvROK(keysv)) { /* ref key */ + SV* ob_id = HUF_obj_id(keysv); + mg->mg_obj = ob_id; /* key replacement */ + if (HUF_WOULD_CREATE_KEY(action)) { + SV* trigger = HUF_get_trigger(keysv, ob_id); + HUF_mark_field(trigger, field); + } + } else if (HUF_WOULD_CREATE_KEY(action)) { /* string key */ + /* registered as object id? */ + SV* trigger; + if ( trigger = HUF_ask_trigger(keysv)) + HUF_mark_field( trigger, field); } + } else { + Perl_die(aTHX_ "Rogue call of 'HUF_watch_key_safe'"); + } + return 0; +} + +I32 HUF_watch_key_id(pTHX_ IV action, SV* field) { + MAGIC* mg = mg_find(field, PERL_MAGIC_uvar); + SV* keysv; + if (mg && (keysv = mg->mg_obj)) { + if (SvROK(keysv)) /* ref key */ + mg->mg_obj = HUF_obj_id(keysv); /* key replacement */ + } else { + Perl_die(aTHX_ "Rogue call of 'HUF_watch_key_id'"); } return 0; } @@ -181,17 +240,40 @@ I32 HUF_watch_key(pTHX_ IV action, SV* field) { int HUF_get_status(HV* hash) { int ans = 0; if (hash && (SvTYPE(hash) == SVt_PVHV)) { - dMY_CXT; MAGIC* mg; struct ufuncs* uf; - ans = (mg = mg_find((SV*)hash, PERL_MAGIC_uvar)) && + if ((mg = mg_find((SV*)hash, PERL_MAGIC_uvar)) && (uf = (struct ufuncs *)mg->mg_ptr) && - (uf->uf_val == &HUF_watch_key) && - (uf->uf_set == NULL); + (uf->uf_set == NULL) + ) { + ans = HUF_func_2mode(uf->uf_val); + } } return ans; } +int HUF_func_2mode( I32(* val)(pTHX_ IV, SV*)) { + int ans = 0; + if (val == &HUF_watch_key_id) + ans = 1; + if (val == &HUF_watch_key_safe) + ans = 2; + return(ans); +} + +I32(* HUF_mode_2func( int mode))(pTHX_ IV, SV*) { + I32(* ans)(pTHX_ IV, SV*) = NULL; + switch (mode) { + case 1: + ans = &HUF_watch_key_id; + break; + case 2: + ans = &HUF_watch_key_safe; + break; + } + return(ans); +} + /* Thread support. These routines are called by CLONE (and nothing else) */ /* Fix entries for one object in all field hashes */ @@ -204,11 +286,11 @@ void HUF_fix_trigger(SV* trigger, SV* new_id) { hv_iterinit(field_tab); while (ent = hv_iternext(field_tab)) { SV* field_ref = HeVAL(ent); - SV* field_id = HUF_field_id(field_ref); HV* field = (HV*)SvRV(field_ref); + UV field_addr = PTR2UV(field); SV* val; /* recreate field tab entry */ - hv_store_ent(new_tab, field_id, SvREFCNT_inc(field_ref), 0); + hv_store(new_tab, (char *)&field_addr, sizeof(field_addr), SvREFCNT_inc(field_ref), 0); /* recreate field entry, if any */ if (val = hv_delete_ent(field, old_id, 0, 0)) hv_store_ent(field, new_id, SvREFCNT_inc(val), 0); @@ -221,7 +303,7 @@ void HUF_fix_trigger(SV* trigger, SV* new_id) { /* Go over object registry and fix all objects. Also fix the object * registry. */ -void HUF_fix_objects() { +void HUF_fix_objects(void) { dMY_CXT; I32 i, len; HE* ent; @@ -233,7 +315,20 @@ void HUF_fix_objects() { for (i = 0; i <= len; ++i) { SV* old_id = *av_fetch(oblist, i, 0); SV* trigger = hv_delete_ent(MY_CXT.ob_reg, old_id, 0, 0); - SV* new_id = HUF_obj_id(trigger); + SV* obj = SvRV(trigger); + MAGIC *mg; + + SV* new_id = HUF_OBJ_ID(obj); + + /* Replace cached object ID with this new one */ + for (mg = SvMAGIC(obj); mg; mg = mg->mg_moremagic) { + if ((mg->mg_type == PERL_MAGIC_ext) && + (mg->mg_private == HUF_IDCACHE) + ) { + mg->mg_obj = new_id; + } + } + HUF_fix_trigger(trigger, new_id); hv_store_ent(MY_CXT.ob_reg, new_id, SvREFCNT_inc(trigger), 0); } @@ -265,9 +360,10 @@ CODE: (field = (HV*)SvRV(href)) && SvTYPE(field) == SVt_PVHV ) { + HUF_add_uvar_magic( SvRV(href), - &HUF_watch_key, + HUF_mode_2func( mode), NULL, 0, NULL @@ -278,23 +374,58 @@ OUTPUT: RETVAL void -CLONE(char* class) +id(SV* ref) +PPCODE: + if (SvROK(ref)) { + XPUSHs(HUF_obj_id(ref)); + } else { + XPUSHs(ref); + } + +SV* +id_2obj(SV* id) +PROTOTYPE: $ CODE: - if (0 == strcmp(class, "Hash::Util::FieldHash")) { - HUF_global(HUF_CLONE); - HUF_fix_objects(); + SV* obj = HUF_ask_trigger(id); + if (obj) { + RETVAL = newRV_inc(SvRV(obj)); + } else { + RETVAL = &PL_sv_undef; } +OUTPUT: + RETVAL SV* -_get_obj_id(SV* obj) +register(SV* obj, ...) +PROTOTYPE: $@ CODE: + SV* trigger; + int i; RETVAL = NULL; - if (SvROK(obj)) - RETVAL = HUF_obj_id(obj); + if (!SvROK(obj)) { + Perl_die(aTHX_ "Attempt to register a non-ref"); + } else { + RETVAL = newRV_inc(SvRV(obj)); + } + trigger = HUF_get_trigger(obj, HUF_obj_id(obj)); + for (i = 1; i < items; ++ i) { + SV* field_ref = POPs; + if (SvROK(field_ref) && (SvTYPE(SvRV(field_ref)) == SVt_PVHV)) { + HUF_mark_field(trigger, SvRV(field_ref)); + } + } OUTPUT: RETVAL -SV* +void +CLONE(char* classname) +CODE: + if (0 == strcmp(classname, "Hash::Util::FieldHash")) { + HUF_global(HUF_CLONE); + HUF_fix_objects(); + } + +void _active_fields(SV* obj) PPCODE: if (SvROK(obj)) {