X-Git-Url: http://git.vpit.fr/?a=blobdiff_plain;f=Magic.xs;h=14874e19527ef9c408e59e8ad264e2fbcb4b7d07;hb=8556481280524737222300317146a23b801f6be0;hp=be5e8f8f234f7c3dc0bb9bee1b7a7a59456856d6;hpb=ad7c749baf8ebc2ff3e49d44b414f67f13f4ebf2;p=perl%2Fmodules%2FVariable-Magic.git diff --git a/Magic.xs b/Magic.xs index be5e8f8..14874e1 100644 --- a/Magic.xs +++ b/Magic.xs @@ -1,8 +1,18 @@ +/* This file is part of the Variable::Magic Perl module. + * See http://search.cpan.org/dist/Variable-Magic/ */ + +#include /* rand(), RAND_MAX */ +#include /* sprintf() */ + #define PERL_NO_GET_CONTEXT #include "EXTERN.h" #include "perl.h" #include "XSUB.h" +#define __PACKAGE__ "Variable::Magic" + +#define R(S) fprintf(stderr, "R(" #S ") = %d\n", SvREFCNT(S)) + /* --- Compatibility ------------------------------------------------------- */ #ifndef Newx @@ -13,9 +23,48 @@ # define SvMAGIC_set(sv, val) (SvMAGIC(sv) = (val)) #endif +#ifndef dMY_CXT +# define MY_CXT vmg_globaldata +# define dMY_CXT +# define START_MY_CXT STATIC my_cxt_t MY_CXT; +# define MY_CXT_INIT +#endif + +/* --- Context-safe global data -------------------------------------------- */ + +#define MY_CXT_KEY __PACKAGE__ "::_guts" XS_VERSION + +typedef struct { + HV *wizz; + U16 count; +} my_cxt_t; + +START_MY_CXT + +/* --- Signatures ---------------------------------------------------------- */ + +#define SIG_MIN ((U16) (1u << 8)) +#define SIG_MAX ((U16) (1u << 16 - 1)) +#define SIG_NBR (SIG_MAX - SIG_MIN + 1) #define SIG_WIZ ((U16) (1u << 8 - 1)) -#define R(S) fprintf(stderr, "R(" #S ") = %d\n", SvREFCNT(sv)) +/* ... Generate signatures ................................................. */ + +STATIC U16 vmg_gensig(pTHX) { +#define vmg_gensig() vmg_gensig(aTHX) + U16 sig; + char buf[8]; + dMY_CXT; + + do { + double u = rand() / (RAND_MAX + 1.0); + sig = SIG_NBR * u + SIG_MIN; + } while (hv_exists(MY_CXT.wizz, buf, sprintf(buf, "%u", sig))); + + return sig; +} + +/* --- MGWIZ structure ----------------------------------------------------- */ typedef struct { MGVTBL *vtbl; @@ -28,8 +77,8 @@ typedef struct { /* ... Construct private data .............................................. */ -STATIC SV *vmg_data_new(pTHX_ SV *ctor, SV *sv) { -#define vmg_data_new(C, S) vmg_data_new(aTHX_ (C), (S)) +STATIC SV *vmg_data_new(pTHX_ SV *ctor, SV *sv, AV *args) { +#define vmg_data_new(C, S, A) vmg_data_new(aTHX_ (C), (S), (A)) SV *nsv; dSP; @@ -39,7 +88,11 @@ STATIC SV *vmg_data_new(pTHX_ SV *ctor, SV *sv) { SAVETMPS; PUSHMARK(SP); - XPUSHs(sv); + XPUSHs(sv_2mortal(newRV_inc(sv))); + if (args != NULL) { + I32 i, alen = av_len(args); + for (i = 0; i < alen; ++i) { XPUSHs(*av_fetch(args, i, 0)); } + } PUTBACK; count = call_sv(ctor, G_SCALAR); @@ -75,8 +128,8 @@ STATIC SV *vmg_data_get(SV *sv, U16 sig) { /* ... Magic cast/dispell .................................................. */ -STATIC UV vmg_cast(pTHX_ SV *sv, SV *wiz) { -#define vmg_cast(S, W) vmg_cast(aTHX_ (S), (W)) +STATIC UV vmg_cast(pTHX_ SV *sv, SV *wiz, AV *args) { +#define vmg_cast(S, W, A) vmg_cast(aTHX_ (S), (W), (A)) MAGIC *mg = NULL, *moremagic = NULL; MGWIZ *w; SV *data; @@ -91,7 +144,7 @@ STATIC UV vmg_cast(pTHX_ SV *sv, SV *wiz) { if (mg) { return 1; } } - data = (w->cb_data) ? vmg_data_new(w->cb_data, sv) : NULL; + data = (w->cb_data) ? vmg_data_new(w->cb_data, sv, args) : NULL; mg = sv_magicext(sv, data, PERL_MAGIC_ext, w->vtbl, (const char *) wiz, HEf_SVKEY); mg->mg_private = w->sig; @@ -139,11 +192,7 @@ STATIC int vmg_cb_call(pTHX_ SV *cb, SV *sv, SV *data) { SAVETMPS; PUSHMARK(SP); - switch (SvTYPE(sv)) { - case SVt_PVAV: - case SVt_PVHV: XPUSHs(sv_2mortal(newRV_inc(sv))); break; - default: XPUSHs(sv); - } + XPUSHs(sv_2mortal(newRV_inc(sv))); if (data) { XPUSHs(data); } PUTBACK; @@ -180,11 +229,7 @@ STATIC U32 vmg_svt_len(pTHX_ SV *sv, MAGIC *mg) { SAVETMPS; PUSHMARK(SP); - switch (SvTYPE(sv)) { - case SVt_PVAV: - case SVt_PVHV: XPUSHs(sv_2mortal(newRV_inc(sv))); break; - default: XPUSHs(sv); - } + XPUSHs(sv_2mortal(newRV_inc(sv))); XPUSHs((mg->mg_obj) ? (mg->mg_obj) : &PL_sv_undef); if (SvTYPE(sv) == SVt_PVAV) { XPUSHs(sv_2mortal(newSViv(av_len((AV *) sv) + 1))); @@ -212,7 +257,7 @@ STATIC int vmg_svt_clear(pTHX_ SV *sv, MAGIC *mg) { STATIC int vmg_svt_free(pTHX_ SV *sv, MAGIC *mg) { /* So that it can survive tmp cleanup in vmg_cb_call */ - if (SvREFCNT(sv) <= 0) { SvREFCNT_inc(sv); } + SvREFCNT_inc(sv); /* Perl_mg_free will get rid of the magic and decrement mg->mg_obj and * mg->mg_ptr reference count */ return vmg_cb_call(SV2MGWIZ(mg->mg_ptr)->cb_free, sv, mg->mg_obj); @@ -221,7 +266,19 @@ STATIC int vmg_svt_free(pTHX_ SV *sv, MAGIC *mg) { /* ... Wizard destructor ................................................... */ STATIC int vmg_wizard_free(pTHX_ SV *wiz, MAGIC *mg) { - MGWIZ *w = SV2MGWIZ(wiz); + char buf[8]; + MGWIZ *w; + dMY_CXT; + + w = SV2MGWIZ(wiz); + + SvREFCNT_inc(wiz); /* Fake survival - it's gonna be deleted anyway */ +#if PERL_API_REVISION >= 5 && PERL_API_VERSION >= 9 && PERL_API_SUBVERSION >= 5 + SvREFCNT_inc(wiz); /* One more push */ +#endif + if (hv_delete(MY_CXT.wizz, buf, sprintf(buf, "%u", w->sig), 0)) { + --MY_CXT.count; + } if (w->cb_get != NULL) { SvREFCNT_dec(SvRV(w->cb_get)); } if (w->cb_set != NULL) { SvREFCNT_dec(SvRV(w->cb_set)); } @@ -249,9 +306,30 @@ STATIC MGVTBL vmg_wizard_vtbl = { #endif /* MGf_DUP */ }; -STATIC const char vmg_invalid_wiz[] = "Invalid wizard object"; -STATIC const char vmg_invalid_sv[] = "Invalid variable"; -STATIC const char vmg_invalid_sig[] = "Invalid numeric signature"; +STATIC const char vmg_invalid_wiz[] = "Invalid wizard object"; +STATIC const char vmg_invalid_sv[] = "Invalid variable"; +STATIC const char vmg_invalid_sig[] = "Invalid numeric signature"; +STATIC const char vmg_toomanysigs[] = "Too many magic signatures used"; +STATIC const char vmg_argstorefailed[] = "Error while storing arguments"; + +STATIC U16 vmg_sv2sig(pTHX_ SV *sv) { +#define vmg_sv2sig(S) vmg_sv2sig(aTHX_ (S)) + U16 sig; + + if (SvIOK(sv)) { + sig = SvUVX(sv); + } else if (SvNOK(sv)) { + sig = SvNVX(sv); + } else if ((SvPOK(sv) && grok_number(SvPVX(sv), SvCUR(sv), NULL))) { + sig = SvUV(sv); + } else { + croak(vmg_invalid_sig); + } + if (sig < SIG_MIN) { sig += SIG_MIN; } + if (sig > SIG_MAX) { sig %= SIG_MAX + 1; } + + return sig; +} /* --- XS ------------------------------------------------------------------ */ @@ -259,15 +337,44 @@ MODULE = Variable::Magic PACKAGE = Variable::Magic PROTOTYPES: ENABLE -SV *_wizard(SV *sig, SV *cb_get, SV *cb_set, SV *cb_len, SV *cb_clear, SV *cb_free, SV *cb_data) -PROTOTYPE: $&&&&& +BOOT: +{ + HV *stash; + MY_CXT_INIT; + MY_CXT.wizz = newHV(); + MY_CXT.count = 0; + stash = gv_stashpv(__PACKAGE__, 1); + newCONSTSUB(stash, "SIG_MIN", newSVuv(SIG_MIN)); + newCONSTSUB(stash, "SIG_MAX", newSVuv(SIG_MAX)); + newCONSTSUB(stash, "SIG_NBR", newSVuv(SIG_NBR)); +/* + newCONSTSUB(stash, "MGf_COPY", newSVuv(MGf_COPY)); + newCONSTSUB(stash, "MGf_DUP", newSVuv(MGf_DUP)); +*/ +} + +SV *_wizard(SV *svsig, SV *cb_get, SV *cb_set, SV *cb_len, SV *cb_clear, SV *cb_free, SV *cb_data) +PROTOTYPE: $&&&&&& PREINIT: + U16 sig; + char buf[8]; MGWIZ *w; MGVTBL *t; MAGIC *mg; SV *sv; CODE: - if (!SvIOK(sig)) { croak(vmg_invalid_sig); } + dMY_CXT; + if (SvOK(svsig)) { + SV **old; + sig = vmg_sv2sig(svsig); + if (old = hv_fetch(MY_CXT.wizz, buf, sprintf(buf, "%u", sig), 0)) { + ST(0) = sv_2mortal(newRV_inc(*old)); + XSRETURN(1); + } + } else { + if (MY_CXT.count >= SIG_NBR) { croak(vmg_toomanysigs); } + sig = vmg_gensig(); + } Newx(t, 1, MGVTBL); t->svt_get = (SvOK(cb_get)) ? vmg_svt_get : NULL; @@ -284,7 +391,7 @@ CODE: Newx(w, 1, MGWIZ); w->vtbl = t; - w->sig = SvUVX(sig); + w->sig = sig; w->cb_get = (SvROK(cb_get)) ? newRV_inc(SvRV(cb_get)) : NULL; w->cb_set = (SvROK(cb_set)) ? newRV_inc(SvRV(cb_set)) : NULL; w->cb_len = (SvROK(cb_len)) ? newRV_inc(SvRV(cb_len)) : NULL; @@ -296,10 +403,22 @@ CODE: mg = sv_magicext(sv, NULL, PERL_MAGIC_ext, &vmg_wizard_vtbl, NULL, -1); mg->mg_private = SIG_WIZ; + hv_store(MY_CXT.wizz, buf, sprintf(buf, "%u", sig), sv, 0); + ++MY_CXT.count; + RETVAL = newRV_noinc(sv); OUTPUT: RETVAL +SV *gensig() +PROTOTYPE: +CODE: + dMY_CXT; + if (MY_CXT.count >= SIG_NBR) { croak(vmg_toomanysigs); } + RETVAL = newSVuv(vmg_gensig()); +OUTPUT: + RETVAL + SV *getsig(SV *wiz) PROTOTYPE: $ CODE: @@ -308,11 +427,40 @@ CODE: OUTPUT: RETVAL -SV *cast(SV *sv, SV *wiz) -PROTOTYPE: \[$@%&*]$ +SV *cast(SV *sv, SV *wiz, ...) +PROTOTYPE: \[$@%&*]$@ +PREINIT: + AV *args = NULL; + SV *ret; CODE: - if (!SvROK(wiz)) { croak(vmg_invalid_wiz); } - RETVAL = newSVuv(vmg_cast(SvRV(sv), SvRV(wiz))); + dMY_CXT; + if (SvROK(wiz)) { + wiz = SvRV(wiz); + } else if (SvOK(wiz)) { + char buf[8]; + SV **old; + U16 sig = vmg_sv2sig(wiz); + if (old = hv_fetch(MY_CXT.wizz, buf, sprintf(buf, "%u", sig), 0)) { + wiz = *old; + } else { + XSRETURN_UNDEF; + } + } else { + croak(vmg_invalid_sig); + } + if (items > 2) { + I32 i; + args = newAV(); + av_fill(args, items - 2); + for (i = 2; i < items; ++i) { + SV *arg = ST(i); + SvREFCNT_inc(arg); + if (av_store(args, i - 2, arg) == NULL) { croak(vmg_argstorefailed); } + } + } + ret = newSVuv(vmg_cast(SvRV(sv), wiz, args)); + SvREFCNT_dec(args); + RETVAL = ret; OUTPUT: RETVAL @@ -320,9 +468,21 @@ SV *getdata(SV *sv, SV *wiz) PROTOTYPE: \[$@%&*]$ PREINIT: SV *data; + U16 sig; CODE: - if (!SvROK(wiz)) { croak(vmg_invalid_wiz); } - data = vmg_data_get(SvRV(sv), SV2MGWIZ(SvRV(wiz))->sig); + dMY_CXT; + if (SvROK(wiz)) { + sig = SV2MGWIZ(SvRV(wiz))->sig; + } else if (SvOK(wiz)) { + char buf[8]; + sig = vmg_sv2sig(wiz); + if (!hv_fetch(MY_CXT.wizz, buf, sprintf(buf, "%u", sig), 0)) { + XSRETURN_UNDEF; + } + } else { + croak(vmg_invalid_wiz); + } + data = vmg_data_get(SvRV(sv), sig); if (!data) { XSRETURN_UNDEF; } ST(0) = newSVsv(data); XSRETURN(1); @@ -332,10 +492,15 @@ PROTOTYPE: \[$@%&*]$ PREINIT: U16 sig; CODE: + dMY_CXT; if (SvROK(wiz)) { sig = SV2MGWIZ(SvRV(wiz))->sig; - } else if (SvIOK(wiz)) { - sig = SvUVX(wiz); + } else if (SvOK(wiz)) { + char buf[8]; + sig = vmg_sv2sig(wiz); + if (!hv_fetch(MY_CXT.wizz, buf, sprintf(buf, "%u", sig), 0)) { + XSRETURN_UNDEF; + } } else { croak(vmg_invalid_wiz); }