1 /* This file is part of the Variable::Magic Perl module.
2 * See http://search.cpan.org/dist/Variable-Magic/ */
4 #include <stdio.h> /* sprintf() */
6 #define PERL_NO_GET_CONTEXT
11 #define __PACKAGE__ "Variable::Magic"
13 #define R(S) fprintf(stderr, "R(" #S ") = %d\n", SvREFCNT(S))
15 #define PERL_VERSION_LE(R, V, S) (PERL_REVISION < (R) || (PERL_REVISION == (R) && (PERL_VERSION < (V) || (PERL_VERSION == (V) && (PERL_SUBVERSION <= (S))))))
17 #define PERL_API_VERSION_GE(R, V, S) (PERL_API_REVISION > (R) || (PERL_API_REVISION == (R) && (PERL_API_VERSION > (V) || (PERL_API_VERSION == (V) && (PERL_API_SUBVERSION >= (S))))))
19 /* --- Compatibility ------------------------------------------------------- */
22 # define Newx(v, n, c) New(0, v, n, c)
26 # define SvMAGIC_set(sv, val) (SvMAGIC(sv) = (val))
30 # define MY_CXT vmg_globaldata
32 # define START_MY_CXT STATIC my_cxt_t MY_CXT;
36 #ifndef PERL_MAGIC_ext
37 # define PERL_MAGIC_ext '~'
40 /* --- Our sv_magicext ----------------------------------------------------- */
43 STATIC MAGIC *vmg_sv_magicext(pTHX_ SV *sv, SV *obj, MGVTBL *vtbl, SV *obj2, I32 flag) {
44 return sv_magicext(sv, obj, PERL_MAGIC_ext, vtbl, (const char *) obj2, flag);
46 #else /* Stub inspired from 5.7.3's sv_magicext */
47 STATIC MAGIC *vmg_sv_magicext(pTHX_ SV *sv, SV *obj, MGVTBL *vtbl, SV *obj2, I32 flag) {
50 if (SvTYPE(sv) < SVt_PVMG) {
51 SvUPGRADE(sv, SVt_PVMG);
54 mg->mg_moremagic = SvMAGIC(sv);
57 if (!obj || obj == sv ||
58 (SvTYPE(obj) == SVt_PVGV &&
59 (GvSV(obj) == sv || GvHV(obj) == (HV *) sv || GvAV(obj) == (AV *) sv ||
60 GvCV(obj) == (CV *) sv || GvIOp(obj) == (IO *) sv ||
61 GvFORM(obj) == (CV *) sv))) {
64 mg->mg_obj = SvREFCNT_inc(obj);
65 mg->mg_flags |= MGf_REFCOUNTED;
68 mg->mg_type = PERL_MAGIC_ext;
71 if (flag == HEf_SVKEY) {
72 mg->mg_ptr = (char *) SvREFCNT_inc((SV *) obj2);
74 mg->mg_ptr = (char *) obj2;
77 mg->mg_virtual = vtbl;
81 SvFLAGS(sv) &= ~(SVf_IOK | SVf_NOK | SVf_POK);
87 #define vmg_sv_magicext(S, O, V, OO, F) vmg_sv_magicext(aTHX_ (S), (O), (V), (OO), (F))
89 /* --- Context-safe global data -------------------------------------------- */
91 #define MY_CXT_KEY __PACKAGE__ "::_guts" XS_VERSION
100 /* --- Signatures ---------------------------------------------------------- */
102 #define SIG_MIN ((U16) (1u << 8))
103 #define SIG_MAX ((U16) (1u << 16 - 1))
104 #define SIG_NBR (SIG_MAX - SIG_MIN + 1)
105 #define SIG_WIZ ((U16) (1u << 8 - 1))
107 /* ... Generate signatures ................................................. */
109 STATIC U16 vmg_gensig(pTHX) {
110 #define vmg_gensig() vmg_gensig(aTHX)
116 sig = SIG_NBR * Drand01() + SIG_MIN;
117 } while (hv_exists(MY_CXT.wizz, buf, sprintf(buf, "%u", sig)));
122 /* --- MGWIZ structure ----------------------------------------------------- */
127 SV *cb_get, *cb_set, *cb_len, *cb_clear, *cb_free, *cb_data;
130 #define MGWIZ2SV(W) (newSVuv(PTR2UV(W)))
131 #define SV2MGWIZ(S) (INT2PTR(MGWIZ*, SvUVX((SV *) (S))))
133 /* ... Construct private data .............................................. */
135 STATIC SV *vmg_data_new(pTHX_ SV *ctor, SV *sv, AV *args) {
136 #define vmg_data_new(C, S, A) vmg_data_new(aTHX_ (C), (S), (A))
146 XPUSHs(sv_2mortal(newRV_inc(sv)));
148 I32 i, alen = av_len(args);
149 for (i = 0; i < alen; ++i) { XPUSHs(*av_fetch(args, i, 0)); }
153 count = call_sv(ctor, G_SCALAR);
157 if (count != 1) { croak("Callback needs to return 1 scalar\n"); }
159 #if PERL_VERSION_LE(5, 8, 2)
160 nsv = sv_newref(nsv); /* Workaround some bug in SvREFCNT_inc() */
162 SvREFCNT_inc(nsv); /* Or it will be destroyed in FREETMPS */
173 STATIC SV *vmg_data_get(SV *sv, U16 sig) {
174 MAGIC *mg, *moremagic;
177 if (SvTYPE(sv) >= SVt_PVMG) {
178 for (mg = SvMAGIC(sv); mg; mg = moremagic) {
179 moremagic = mg->mg_moremagic;
180 if ((mg->mg_type == PERL_MAGIC_ext) && (mg->mg_private == sig)) { break; }
182 if (mg) { return mg->mg_obj; }
188 /* ... Magic cast/dispell .................................................. */
190 STATIC UV vmg_cast(pTHX_ SV *sv, SV *wiz, AV *args) {
191 #define vmg_cast(S, W, A) vmg_cast(aTHX_ (S), (W), (A))
192 MAGIC *mg = NULL, *moremagic = NULL;
198 if (SvTYPE(sv) >= SVt_PVMG) {
199 for (mg = SvMAGIC(sv); mg; mg = moremagic) {
200 moremagic = mg->mg_moremagic;
201 if ((mg->mg_type == PERL_MAGIC_ext) && (mg->mg_private == w->sig)) { break; }
203 if (mg) { return 1; }
206 data = (w->cb_data) ? vmg_data_new(w->cb_data, sv, args) : NULL;
207 mg = vmg_sv_magicext(sv, data, w->vtbl, wiz, HEf_SVKEY);
208 mg->mg_private = w->sig;
213 STATIC UV vmg_dispell(pTHX_ SV *sv, U16 sig) {
214 #define vmg_dispell(S, Z) vmg_dispell(aTHX_ (S), (Z))
215 MAGIC *mg, *prevmagic, *moremagic = NULL;
218 if (SvTYPE(sv) < SVt_PVMG) { return 0; }
220 for (prevmagic = NULL, mg = SvMAGIC(sv); mg; prevmagic = mg, mg = moremagic) {
221 moremagic = mg->mg_moremagic;
222 if ((mg->mg_type == PERL_MAGIC_ext) && (mg->mg_private == sig)) { break; }
224 if (!mg) { return 0; }
227 prevmagic->mg_moremagic = moremagic;
229 SvMAGIC_set(sv, moremagic);
231 mg->mg_moremagic = NULL;
233 if (mg->mg_obj != sv) { SvREFCNT_dec(mg->mg_obj); } /* Destroy private data */
234 SvREFCNT_dec((SV *) mg->mg_ptr); /* Unreference the wizard */
240 /* ... svt callbacks ....................................................... */
242 STATIC int vmg_cb_call(pTHX_ SV *cb, SV *sv, SV *data) {
243 #define vmg_cb_call(I, S, D) vmg_cb_call(aTHX_ (I), (S), (D))
253 XPUSHs(sv_2mortal(newRV_inc(sv)));
254 if (data) { XPUSHs(data); }
257 count = call_sv(cb, G_SCALAR);
261 if (count != 1) { croak("Callback needs to return 1 scalar\n"); }
272 STATIC int vmg_svt_get(pTHX_ SV *sv, MAGIC *mg) {
273 return vmg_cb_call(SV2MGWIZ(mg->mg_ptr)->cb_get, sv, mg->mg_obj);
276 STATIC int vmg_svt_set(pTHX_ SV *sv, MAGIC *mg) {
277 return vmg_cb_call(SV2MGWIZ(mg->mg_ptr)->cb_set, sv, mg->mg_obj);
280 STATIC U32 vmg_svt_len(pTHX_ SV *sv, MAGIC *mg) {
290 XPUSHs(sv_2mortal(newRV_inc(sv)));
291 XPUSHs((mg->mg_obj) ? (mg->mg_obj) : &PL_sv_undef);
292 if (SvTYPE(sv) == SVt_PVAV) {
293 XPUSHs(sv_2mortal(newSViv(av_len((AV *) sv) + 1)));
297 count = call_sv(SV2MGWIZ(mg->mg_ptr)->cb_len, G_SCALAR);
301 if (count != 1) { croak("Callback needs to return 1 scalar\n"); }
312 STATIC int vmg_svt_clear(pTHX_ SV *sv, MAGIC *mg) {
313 return vmg_cb_call(SV2MGWIZ(mg->mg_ptr)->cb_clear, sv, mg->mg_obj);
316 STATIC int vmg_svt_free(pTHX_ SV *sv, MAGIC *mg) {
317 /* So that it can survive tmp cleanup in vmg_cb_call */
319 /* Perl_mg_free will get rid of the magic and decrement mg->mg_obj and
320 * mg->mg_ptr reference count */
321 return vmg_cb_call(SV2MGWIZ(mg->mg_ptr)->cb_free, sv, mg->mg_obj);
324 /* ... Wizard destructor ................................................... */
326 STATIC int vmg_wizard_free(pTHX_ SV *wiz, MAGIC *mg) {
333 SvREFCNT_inc(wiz); /* Fake survival - it's gonna be deleted anyway */
334 #if PERL_API_VERSION_GE(5, 9, 5)
335 SvREFCNT_inc(wiz); /* One more push */
337 if (hv_delete(MY_CXT.wizz, buf, sprintf(buf, "%u", w->sig), 0)) {
341 if (w->cb_get != NULL) { SvREFCNT_dec(SvRV(w->cb_get)); }
342 if (w->cb_set != NULL) { SvREFCNT_dec(SvRV(w->cb_set)); }
343 if (w->cb_len != NULL) { SvREFCNT_dec(SvRV(w->cb_len)); }
344 if (w->cb_clear != NULL) { SvREFCNT_dec(SvRV(w->cb_clear)); }
345 if (w->cb_free != NULL) { SvREFCNT_dec(SvRV(w->cb_free)); }
346 if (w->cb_data != NULL) { SvREFCNT_dec(SvRV(w->cb_data)); }
353 STATIC MGVTBL vmg_wizard_vtbl = {
358 vmg_wizard_free, /* free */
361 #endif /* MGf_COPY */
367 STATIC const char vmg_invalid_wiz[] = "Invalid wizard object";
368 STATIC const char vmg_invalid_sv[] = "Invalid variable";
369 STATIC const char vmg_invalid_sig[] = "Invalid numeric signature";
370 STATIC const char vmg_toomanysigs[] = "Too many magic signatures used";
371 STATIC const char vmg_argstorefailed[] = "Error while storing arguments";
373 STATIC U16 vmg_sv2sig(pTHX_ SV *sv) {
374 #define vmg_sv2sig(S) vmg_sv2sig(aTHX_ (S))
379 } else if (SvNOK(sv)) {
381 } else if ((SvPOK(sv) && grok_number(SvPVX(sv), SvCUR(sv), NULL))) {
384 croak(vmg_invalid_sig);
386 if (sig < SIG_MIN) { sig += SIG_MIN; }
387 if (sig > SIG_MAX) { sig %= SIG_MAX + 1; }
392 /* --- XS ------------------------------------------------------------------ */
394 MODULE = Variable::Magic PACKAGE = Variable::Magic
402 MY_CXT.wizz = newHV();
404 stash = gv_stashpv(__PACKAGE__, 1);
405 newCONSTSUB(stash, "SIG_MIN", newSVuv(SIG_MIN));
406 newCONSTSUB(stash, "SIG_MAX", newSVuv(SIG_MAX));
407 newCONSTSUB(stash, "SIG_NBR", newSVuv(SIG_NBR));
409 newCONSTSUB(stash, "MGf_COPY", newSVuv(MGf_COPY));
410 newCONSTSUB(stash, "MGf_DUP", newSVuv(MGf_DUP));
414 SV *_wizard(SV *svsig, SV *cb_get, SV *cb_set, SV *cb_len, SV *cb_clear, SV *cb_free, SV *cb_data)
427 sig = vmg_sv2sig(svsig);
428 if (old = hv_fetch(MY_CXT.wizz, buf, sprintf(buf, "%u", sig), 0)) {
429 ST(0) = sv_2mortal(newRV_inc(*old));
433 if (MY_CXT.count >= SIG_NBR) { croak(vmg_toomanysigs); }
438 t->svt_get = (SvOK(cb_get)) ? vmg_svt_get : NULL;
439 t->svt_set = (SvOK(cb_set)) ? vmg_svt_set : NULL;
440 t->svt_len = (SvOK(cb_len)) ? vmg_svt_len : NULL;
441 t->svt_clear = (SvOK(cb_clear)) ? vmg_svt_clear : NULL;
442 t->svt_free = (SvOK(cb_free)) ? vmg_svt_free : NULL;
445 #endif /* MGf_COPY */
453 w->cb_get = (SvROK(cb_get)) ? newRV_inc(SvRV(cb_get)) : NULL;
454 w->cb_set = (SvROK(cb_set)) ? newRV_inc(SvRV(cb_set)) : NULL;
455 w->cb_len = (SvROK(cb_len)) ? newRV_inc(SvRV(cb_len)) : NULL;
456 w->cb_clear = (SvROK(cb_clear)) ? newRV_inc(SvRV(cb_clear)) : NULL;
457 w->cb_free = (SvROK(cb_free)) ? newRV_inc(SvRV(cb_free)) : NULL;
458 w->cb_data = (SvROK(cb_data)) ? newRV_inc(SvRV(cb_data)) : NULL;
461 mg = vmg_sv_magicext(sv, NULL, &vmg_wizard_vtbl, NULL, -1);
462 mg->mg_private = SIG_WIZ;
464 hv_store(MY_CXT.wizz, buf, sprintf(buf, "%u", sig), sv, 0);
467 RETVAL = newRV_noinc(sv);
475 if (MY_CXT.count >= SIG_NBR) { croak(vmg_toomanysigs); }
476 RETVAL = newSVuv(vmg_gensig());
483 if (!SvROK(wiz)) { croak(vmg_invalid_wiz); }
484 RETVAL = newSVuv(SV2MGWIZ(SvRV(wiz))->sig);
488 SV *cast(SV *sv, SV *wiz, ...)
489 PROTOTYPE: \[$@%&*]$@
497 } else if (SvOK(wiz)) {
500 U16 sig = vmg_sv2sig(wiz);
501 if (old = hv_fetch(MY_CXT.wizz, buf, sprintf(buf, "%u", sig), 0)) {
507 croak(vmg_invalid_sig);
512 av_fill(args, items - 2);
513 for (i = 2; i < items; ++i) {
516 if (av_store(args, i - 2, arg) == NULL) { croak(vmg_argstorefailed); }
519 ret = newSVuv(vmg_cast(SvRV(sv), wiz, args));
525 SV *getdata(SV *sv, SV *wiz)
533 sig = SV2MGWIZ(SvRV(wiz))->sig;
534 } else if (SvOK(wiz)) {
536 sig = vmg_sv2sig(wiz);
537 if (!hv_fetch(MY_CXT.wizz, buf, sprintf(buf, "%u", sig), 0)) {
541 croak(vmg_invalid_wiz);
543 data = vmg_data_get(SvRV(sv), sig);
544 if (!data) { XSRETURN_UNDEF; }
545 ST(0) = newSVsv(data);
548 SV *dispell(SV *sv, SV *wiz)
555 sig = SV2MGWIZ(SvRV(wiz))->sig;
556 } else if (SvOK(wiz)) {
558 sig = vmg_sv2sig(wiz);
559 if (!hv_fetch(MY_CXT.wizz, buf, sprintf(buf, "%u", sig), 0)) {
563 croak(vmg_invalid_wiz);
565 RETVAL = newSVuv(vmg_dispell(SvRV(sv), sig));