]> git.vpit.fr Git - perl/modules/Variable-Magic.git/blob - Magic.xs
Importing Variable-Magic-0.10.tar.gz
[perl/modules/Variable-Magic.git] / Magic.xs
1 /* This file is part of the Variable::Magic Perl module.
2  * See http://search.cpan.org/dist/Variable-Magic/ */
3
4 #include <stdio.h>  /* sprintf() */
5
6 #define PERL_NO_GET_CONTEXT
7 #include "EXTERN.h"
8 #include "perl.h"
9 #include "XSUB.h"
10
11 #define __PACKAGE__ "Variable::Magic"
12
13 #define R(S) fprintf(stderr, "R(" #S ") = %d\n", SvREFCNT(S))
14
15 #define PERL_VERSION_GE(R, V, S) (PERL_REVISION > (R) || (PERL_REVISION == (R) && (PERL_VERSION > (V) || (PERL_VERSION == (V) && (PERL_SUBVERSION >= (S))))))
16
17 #define PERL_VERSION_LE(R, V, S) (PERL_REVISION < (R) || (PERL_REVISION == (R) && (PERL_VERSION < (V) || (PERL_VERSION == (V) && (PERL_SUBVERSION <= (S))))))
18
19 #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))))))
20
21 #define PERL_API_VERSION_LE(R, V, S) (PERL_API_REVISION < (R) || (PERL_API_REVISION == (R) && (PERL_API_VERSION < (V) || (PERL_API_VERSION == (V) && (PERL_API_SUBVERSION <= (S))))))
22
23 /* --- Compatibility ------------------------------------------------------- */
24
25 #ifndef Newx
26 # define Newx(v, n, c) New(0, v, n, c)
27 #endif
28
29 #ifndef SvMAGIC_set
30 # define SvMAGIC_set(sv, val) (SvMAGIC(sv) = (val))
31 #endif
32
33 #ifndef dMY_CXT
34 # define MY_CXT vmg_globaldata
35 # define dMY_CXT
36 # define START_MY_CXT STATIC my_cxt_t MY_CXT;
37 # define MY_CXT_INIT
38 #endif
39
40 #ifndef PERL_MAGIC_ext
41 # define PERL_MAGIC_ext '~'
42 #endif
43
44 #ifndef MGf_COPY
45 # define MGf_COPY 0
46 #endif
47
48 #undef MGf_DUP /* Disable it for now. */
49 #ifndef MGf_DUP
50 # define MGf_DUP 0
51 #endif
52
53 #ifndef MGf_LOCAL
54 # define MGf_LOCAL 0
55 #endif
56
57 #if PERL_API_VERSION_GE(5, 10, 0)
58 # define VMG_UVAR 1
59 #else
60 # define VMG_UVAR 0
61 #endif
62
63 #if PERL_VERSION_GE(5, 9, 3)
64 # define VMG_COMPAT_ARRAY_PUSH_NOLEN 1
65 #else
66 # define VMG_COMPAT_ARRAY_PUSH_NOLEN 0
67 #endif
68
69 /* since 5.9.5 - see #43357 */
70 #if PERL_VERSION_GE(5, 9, 5)
71 # define VMG_COMPAT_ARRAY_UNDEF_CLEAR 1
72 #else
73 # define VMG_COMPAT_ARRAY_UNDEF_CLEAR 0
74 #endif
75
76 #if MGf_COPY && PERL_VERSION_GE(5, 9, 4)
77 # define VMG_COMPAT_HASH_LISTASSIGN_COPY 1
78 #else
79 # define VMG_COMPAT_HASH_LISTASSIGN_COPY 0
80 #endif
81
82 #if VMG_UVAR
83
84 /* Bug-free mg_magical - see http://www.xray.mpe.mpg.de/mailing-lists/perl5-porters/2008-01/msg00036.html */
85 STATIC void vmg_mg_magical(pTHX_ SV *sv) {
86 #define vmg_mg_magical(S) vmg_mg_magical(aTHX_ (S))
87  const MAGIC* mg;
88  PERL_UNUSED_CONTEXT;
89  if ((mg = SvMAGIC(sv))) {
90   SvRMAGICAL_off(sv);
91   do {
92    const MGVTBL* const vtbl = mg->mg_virtual;
93    if (vtbl) {
94     if (vtbl->svt_get && !(mg->mg_flags & MGf_GSKIP))
95      SvGMAGICAL_on(sv);
96     if (vtbl->svt_set)
97      SvSMAGICAL_on(sv);
98     if (vtbl->svt_clear)
99      SvRMAGICAL_on(sv);
100    }
101   } while ((mg = mg->mg_moremagic));
102   if (!(SvFLAGS(sv) & (SVs_GMG|SVs_SMG)))
103    SvRMAGICAL_on(sv);
104  }
105 }
106
107 #endif /* VMG_UVAR */
108
109 /* --- Context-safe global data -------------------------------------------- */
110
111 #define MY_CXT_KEY __PACKAGE__ "::_guts" XS_VERSION
112
113 typedef struct {
114  HV *wizz;
115  U16 count;
116 } my_cxt_t;
117
118 START_MY_CXT
119
120 /* --- Signatures ---------------------------------------------------------- */
121
122 #define SIG_MIN ((U16) (1u << 8))
123 #define SIG_MAX ((U16) ((1u << 16) - 1))
124 #define SIG_NBR (SIG_MAX - SIG_MIN + 1)
125 #define SIG_WIZ ((U16) ((1u << 8) - 1))
126
127 /* ... Generate signatures ................................................. */
128
129 STATIC U16 vmg_gensig(pTHX) {
130 #define vmg_gensig() vmg_gensig(aTHX)
131  U16 sig;
132  char buf[8];
133  dMY_CXT;
134
135  do {
136   sig = SIG_NBR * Drand01() + SIG_MIN;
137  } while (hv_exists(MY_CXT.wizz, buf, sprintf(buf, "%u", sig)));
138
139  return sig;
140 }
141
142 /* --- MGWIZ structure ----------------------------------------------------- */
143
144 typedef struct {
145  MGVTBL *vtbl;
146  U16 sig;
147  U16 uvar;
148  SV *cb_data;
149  SV *cb_get, *cb_set, *cb_len, *cb_clear, *cb_free;
150 #if MGf_COPY
151  SV *cb_copy;
152 #endif /* MGf_COPY */
153 #if MGf_DUP
154  SV *cb_dup;
155 #endif /* MGf_DUP */
156 #if MGf_LOCAL
157  SV *cb_local;
158 #endif /* MGf_LOCAL */
159 #if VMG_UVAR
160  SV *cb_fetch, *cb_store, *cb_exists, *cb_delete;
161 #endif /* VMG_UVAR */
162 } MGWIZ;
163
164 #define MGWIZ2SV(W) (newSVuv(PTR2UV(W)))
165 #define SV2MGWIZ(S) (INT2PTR(MGWIZ*, SvUVX((SV *) (S))))
166
167 /* ... Construct private data .............................................. */
168
169 STATIC SV *vmg_data_new(pTHX_ SV *ctor, SV *sv, AV *args) {
170 #define vmg_data_new(C, S, A) vmg_data_new(aTHX_ (C), (S), (A))
171  SV *nsv;
172
173  dSP;
174  int count;
175
176  ENTER;
177  SAVETMPS;
178
179  PUSHMARK(SP);
180  XPUSHs(sv_2mortal(newRV_inc(sv)));
181  if (args != NULL) {
182   I32 i, alen = av_len(args);
183   for (i = 0; i < alen; ++i) { XPUSHs(*av_fetch(args, i, 0)); }
184  }
185  PUTBACK;
186
187  count = call_sv(ctor, G_SCALAR);
188
189  SPAGAIN;
190
191  if (count != 1) { croak("Callback needs to return 1 scalar\n"); }
192  nsv = POPs;
193 #if PERL_VERSION_LE(5, 8, 2)
194  nsv = sv_newref(nsv); /* Workaround some bug in SvREFCNT_inc() */
195 #else
196  SvREFCNT_inc(nsv);    /* Or it will be destroyed in FREETMPS */
197 #endif
198
199  PUTBACK;
200
201  FREETMPS;
202  LEAVE;
203
204  return nsv;
205 }
206
207 STATIC SV *vmg_data_get(SV *sv, U16 sig) {
208  MAGIC *mg, *moremagic;
209
210  if (SvTYPE(sv) >= SVt_PVMG) {
211   for (mg = SvMAGIC(sv); mg; mg = moremagic) {
212    moremagic = mg->mg_moremagic;
213    if ((mg->mg_type == PERL_MAGIC_ext) && (mg->mg_private == sig)) { break; }
214   }
215   if (mg) { return mg->mg_obj; }
216  }
217
218  return NULL;
219
220
221 /* ... Magic cast/dispell .................................................. */
222
223 STATIC I32 vmg_svt_val(pTHX_ IV, SV *);
224
225 STATIC void vmg_uvar_del(SV *sv, MAGIC *prevmagic, MAGIC *mg, MAGIC *moremagic) {
226  if (prevmagic) {
227   prevmagic->mg_moremagic = moremagic;
228  } else {
229   SvMAGIC_set(sv, moremagic);
230  }
231  mg->mg_moremagic = NULL;
232  Safefree(mg->mg_ptr);
233  Safefree(mg);
234 }
235
236 STATIC UV vmg_cast(pTHX_ SV *sv, SV *wiz, AV *args) {
237 #define vmg_cast(S, W, A) vmg_cast(aTHX_ (S), (W), (A))
238  MAGIC *mg = NULL, *moremagic = NULL;
239  MGWIZ *w;
240  SV *data;
241
242  w = SV2MGWIZ(wiz);
243
244  if (SvTYPE(sv) >= SVt_PVMG) {
245   for (mg = SvMAGIC(sv); mg; mg = moremagic) {
246    moremagic = mg->mg_moremagic;
247    if ((mg->mg_type == PERL_MAGIC_ext) && (mg->mg_private == w->sig)) { break; }
248   }
249   if (mg) { return 1; }
250  }
251
252  data = (w->cb_data) ? vmg_data_new(w->cb_data, sv, args) : NULL;
253  mg = sv_magicext(sv, data, PERL_MAGIC_ext, w->vtbl, (const char *) wiz, HEf_SVKEY);
254  mg->mg_private = w->sig;
255  mg->mg_flags   = mg->mg_flags
256 #if MGf_COPY
257                 | MGf_COPY
258 #endif /* MGf_COPY */
259 #if MGf_DUP
260                 | MGf_DUP
261 #endif /* MGf_DUP */
262 #if MGf_LOCAL
263                 | MGf_LOCAL
264 #endif /* MGf_LOCAL */
265                 ;
266
267 #if VMG_UVAR
268  if (w->uvar && SvTYPE(sv) >= SVt_PVHV) {
269   MAGIC *prevmagic;
270   int add_uvar = 1;
271   struct ufuncs uf[2];
272
273   uf[0].uf_val   = vmg_svt_val;
274   uf[0].uf_set   = NULL;
275   uf[0].uf_index = 0;
276   uf[1].uf_val   = NULL;
277   uf[1].uf_set   = NULL;
278   uf[1].uf_index = 0;
279
280   /* One uvar magic in the chain is enough. */
281   for (prevmagic = NULL, mg = SvMAGIC(sv); mg; prevmagic = mg, mg = moremagic) {
282    moremagic = mg->mg_moremagic;
283    if (mg->mg_type == PERL_MAGIC_uvar) { break; }
284   }
285
286   if (mg) { /* Found another uvar magic. */
287    struct ufuncs *olduf = (struct ufuncs *) mg->mg_ptr;
288    if (olduf->uf_val == vmg_svt_val) {
289     /* It's our uvar magic, nothing to do. */
290     add_uvar = 0;
291    } else {
292     /* It's another uvar magic, backup it and replace it by ours. */
293     uf[1] = *olduf;
294     vmg_uvar_del(sv, prevmagic, mg, moremagic);
295    }
296   }
297
298   if (add_uvar) {
299    sv_magic(sv, NULL, PERL_MAGIC_uvar, (const char *) &uf, sizeof(uf));
300    vmg_mg_magical(sv);
301   }
302
303  }
304 #endif /* VMG_UVAR */
305
306  return 1;
307 }
308
309 STATIC UV vmg_dispell(pTHX_ SV *sv, U16 sig) {
310 #define vmg_dispell(S, Z) vmg_dispell(aTHX_ (S), (Z))
311 #if VMG_UVAR
312  U32 uvars = 0;
313 #endif /* VMG_UVAR */
314  MAGIC *mg, *prevmagic, *moremagic = NULL;
315
316  if (SvTYPE(sv) < SVt_PVMG) { return 0; }
317
318  for (prevmagic = NULL, mg = SvMAGIC(sv); mg; prevmagic = mg, mg = moremagic) {
319   moremagic = mg->mg_moremagic;
320   if (mg->mg_type == PERL_MAGIC_ext) {
321    if (mg->mg_private == sig) {
322 #if VMG_UVAR
323     /* If the current has no uvar, short-circuit uvar deletion. */
324     uvars = (SV2MGWIZ(mg->mg_ptr)->uvar) ? (uvars + 1) : 0;
325 #endif /* VMG_UVAR */
326     break;
327 #if VMG_UVAR
328    } else if ((mg->mg_private >= SIG_MIN) &&
329               (mg->mg_private <= SIG_MAX) &&
330                SV2MGWIZ(mg->mg_ptr)->uvar) {
331     ++uvars;
332     /* We can't break here since we need to find the ext magic to delete. */
333 #endif /* VMG_UVAR */
334    }
335   }
336  }
337  if (!mg) { return 0; }
338
339  if (prevmagic) {
340   prevmagic->mg_moremagic = moremagic;
341  } else {
342   SvMAGIC_set(sv, moremagic);
343  }
344  mg->mg_moremagic = NULL;
345
346  if (mg->mg_obj != sv) { SvREFCNT_dec(mg->mg_obj); } /* Destroy private data */
347  SvREFCNT_dec((SV *) mg->mg_ptr); /* Unreference the wizard */
348  Safefree(mg);
349
350 #if VMG_UVAR
351  if (uvars == 1 && SvTYPE(sv) >= SVt_PVHV) {
352   /* mg was the first ext magic in the chain that had uvar */
353
354   for (mg = moremagic; mg; mg = mg->mg_moremagic) {
355    if ((mg->mg_type == PERL_MAGIC_ext) &&
356        (mg->mg_private >= SIG_MIN) &&
357        (mg->mg_private <= SIG_MAX) &&
358         SV2MGWIZ(mg->mg_ptr)->uvar) {
359     ++uvars;
360     break;
361    }
362   }
363
364   if (uvars == 1) {
365    struct ufuncs *uf;
366    for (prevmagic = NULL, mg = SvMAGIC(sv); mg; prevmagic = mg, mg = moremagic){
367     moremagic = mg->mg_moremagic;
368     if (mg->mg_type == PERL_MAGIC_uvar) { break; }
369    }
370    /* assert(mg); */
371    uf = (struct ufuncs *) mg->mg_ptr;
372    /* assert(uf->uf_val == vmg_svt_val); */
373    if (uf[1].uf_val || uf[1].uf_set) {
374     /* Revert the original uvar magic. */
375     uf[0] = uf[1];
376     Renew(uf, 1, struct ufuncs);
377     mg->mg_len = sizeof(struct ufuncs);
378    } else {
379     /* Remove the uvar magic. */
380     vmg_uvar_del(sv, prevmagic, mg, moremagic);
381    }
382   }
383  }
384 #endif /* VMG_UVAR */
385
386  return 1;
387 }
388
389 /* ... svt callbacks ....................................................... */
390
391 STATIC int vmg_cb_call(pTHX_ SV *cb, SV *sv, SV *data) {
392 #define vmg_cb_call(I, S, D) vmg_cb_call(aTHX_ (I), (S), (D))
393  SV *svr;
394  int ret;
395
396  dSP;
397  int count;
398
399  ENTER;
400  SAVETMPS;
401
402  PUSHMARK(SP);
403  XPUSHs(sv_2mortal(newRV_inc(sv)));
404  if (data) { XPUSHs(data); }
405  PUTBACK;
406
407  count = call_sv(cb, G_SCALAR);
408
409  SPAGAIN;
410
411  if (count != 1) { croak("Callback needs to return 1 scalar\n"); }
412  svr = POPs;
413  ret = SvOK(svr) ? SvIV(svr) : 0;
414
415  PUTBACK;
416
417  FREETMPS;
418  LEAVE;
419
420  return ret;
421 }
422
423 #if MGf_COPY || VMG_UVAR
424 STATIC int vmg_cb_call2(pTHX_ SV *cb, SV *sv, SV *data, SV *sv2) {
425 #define vmg_cb_call2(I, S, D, S2) vmg_cb_call2(aTHX_ (I), (S), (D), (S2))
426  SV *svr;
427  int ret;
428
429  dSP;
430  int count;
431
432  ENTER;
433  SAVETMPS;
434
435  PUSHMARK(SP);
436  XPUSHs(sv_2mortal(newRV_inc(sv)));
437  XPUSHs(data ? data : &PL_sv_undef);
438  if (sv2) { XPUSHs(sv2); }
439  PUTBACK;
440
441  count = call_sv(cb, G_SCALAR);
442
443  SPAGAIN;
444
445  if (count != 1) { croak("Callback needs to return 1 scalar\n"); }
446  svr = POPs;
447  ret = SvOK(svr) ? SvIV(svr) : 0;
448
449  PUTBACK;
450
451  FREETMPS;
452  LEAVE;
453
454  return ret;
455 }
456 #endif /* MGf_COPY || VMG_UVAR */
457
458 STATIC int vmg_svt_get(pTHX_ SV *sv, MAGIC *mg) {
459  return vmg_cb_call(SV2MGWIZ(mg->mg_ptr)->cb_get, sv, mg->mg_obj);
460 }
461
462 STATIC int vmg_svt_set(pTHX_ SV *sv, MAGIC *mg) {
463  return vmg_cb_call(SV2MGWIZ(mg->mg_ptr)->cb_set, sv, mg->mg_obj);
464 }
465
466 STATIC U32 vmg_svt_len(pTHX_ SV *sv, MAGIC *mg) {
467  SV *svr;
468  I32 len;
469  U32 ret;
470
471  dSP;
472  int count;
473
474  ENTER;
475  SAVETMPS;
476
477  PUSHMARK(SP);
478  XPUSHs(sv_2mortal(newRV_inc(sv)));
479  XPUSHs(mg->mg_obj ? mg->mg_obj : &PL_sv_undef);
480  if (SvTYPE(sv) == SVt_PVAV) {
481   len = av_len((AV *) sv) + 1;
482   XPUSHs(sv_2mortal(newSViv(len)));
483  }
484  PUTBACK;
485
486  count = call_sv(SV2MGWIZ(mg->mg_ptr)->cb_len, G_SCALAR);
487
488  SPAGAIN;
489
490  if (count != 1) { croak("Callback needs to return 1 scalar\n"); }
491  svr = POPs;
492  ret = SvOK(svr) ? SvUV(svr)
493                  : ((SvTYPE(sv) == SVt_PVAV) ? len : 1);
494
495  PUTBACK;
496
497  FREETMPS;
498  LEAVE;
499
500  return ret - 1;
501 }
502
503 STATIC int vmg_svt_clear(pTHX_ SV *sv, MAGIC *mg) {
504  return vmg_cb_call(SV2MGWIZ(mg->mg_ptr)->cb_clear, sv, mg->mg_obj);
505 }
506
507 STATIC int vmg_svt_free(pTHX_ SV *sv, MAGIC *mg) {
508  /* So that it can survive tmp cleanup in vmg_cb_call */
509  SvREFCNT_inc(sv);
510  /* Perl_mg_free will get rid of the magic and decrement mg->mg_obj and
511   * mg->mg_ptr reference count */
512  return vmg_cb_call(SV2MGWIZ(mg->mg_ptr)->cb_free, sv, mg->mg_obj);
513 }
514
515 #if MGf_COPY
516 STATIC int vmg_svt_copy(pTHX_ SV *sv, MAGIC *mg, SV *nsv, const char *name, int namelen) {
517  return vmg_cb_call2(SV2MGWIZ(mg->mg_ptr)->cb_copy, sv, mg->mg_obj, nsv);
518 }
519 #endif /* MGf_COPY */
520
521 #if MGf_DUP
522 STATIC int vmg_svt_dup(pTHX_ MAGIC *mg, CLONE_PARAMS *param) {
523  return 0;
524 }
525 #endif /* MGf_DUP */
526
527 #if MGf_LOCAL
528 STATIC int vmg_svt_local(pTHX_ SV *nsv, MAGIC *mg) {
529  return vmg_cb_call(SV2MGWIZ(mg->mg_ptr)->cb_local, nsv, mg->mg_obj);
530 }
531 #endif /* MGf_LOCAL */
532
533 #if VMG_UVAR
534 STATIC I32 vmg_svt_val(pTHX_ IV action, SV *sv) {
535  struct ufuncs *uf;
536  MAGIC *mg;
537  SV *key = NULL;
538
539  mg  = mg_find(sv, PERL_MAGIC_uvar);
540  /* mg can't be NULL or we wouldn't be there. */
541  key = mg->mg_obj;
542  uf  = (struct ufuncs *) mg->mg_ptr;
543
544  if (uf[1].uf_val != NULL) { uf[1].uf_val(aTHX_ action, sv); }
545  if (uf[1].uf_set != NULL) { uf[1].uf_set(aTHX_ action, sv); }
546
547  action &= HV_FETCH_ISSTORE | HV_FETCH_ISEXISTS | HV_FETCH_LVALUE | HV_DELETE;
548  for (mg = SvMAGIC(sv); mg; mg = mg->mg_moremagic) {
549   MGWIZ *w;
550   if ((mg->mg_type != PERL_MAGIC_ext)
551    || (mg->mg_private < SIG_MIN)
552    || (mg->mg_private > SIG_MAX)) { continue; }
553   w = SV2MGWIZ(mg->mg_ptr);
554   if (!w->uvar) { continue; }
555   switch (action) {
556    case 0:
557     if (w->cb_fetch) { vmg_cb_call2(w->cb_fetch, sv, mg->mg_obj, key); }
558     break;
559    case HV_FETCH_ISSTORE:
560    case HV_FETCH_LVALUE:
561    case (HV_FETCH_ISSTORE|HV_FETCH_LVALUE):
562     if (w->cb_store) { vmg_cb_call2(w->cb_store, sv, mg->mg_obj, key); }
563     break;
564    case HV_FETCH_ISEXISTS:
565     if (w->cb_exists) { vmg_cb_call2(w->cb_exists, sv, mg->mg_obj, key); }
566     break;
567    case HV_DELETE:
568     if (w->cb_delete) { vmg_cb_call2(w->cb_delete, sv, mg->mg_obj, key); }
569     break;
570   }
571  }
572
573  return 0;
574 }
575 #endif /* VMG_UVAR */
576
577 /* ... Wizard destructor ................................................... */
578
579 STATIC int vmg_wizard_free(pTHX_ SV *wiz, MAGIC *mg) {
580  char buf[8];
581  MGWIZ *w;
582  dMY_CXT;
583
584  w = SV2MGWIZ(wiz);
585
586  SvREFCNT_inc(wiz); /* Fake survival - it's gonna be deleted anyway */
587 #if PERL_API_VERSION_GE(5, 9, 5)
588  SvREFCNT_inc(wiz); /* One more push */
589 #endif
590  if (hv_delete(MY_CXT.wizz, buf, sprintf(buf, "%u", w->sig), 0)) {
591   --MY_CXT.count;
592  }
593
594  if (w->cb_data  != NULL) { SvREFCNT_dec(SvRV(w->cb_data)); }
595  if (w->cb_get   != NULL) { SvREFCNT_dec(SvRV(w->cb_get)); }
596  if (w->cb_set   != NULL) { SvREFCNT_dec(SvRV(w->cb_set)); }
597  if (w->cb_len   != NULL) { SvREFCNT_dec(SvRV(w->cb_len)); }
598  if (w->cb_clear != NULL) { SvREFCNT_dec(SvRV(w->cb_clear)); }
599  if (w->cb_free  != NULL) { SvREFCNT_dec(SvRV(w->cb_free)); }
600 #if MGf_COPY
601  if (w->cb_copy  != NULL) { SvREFCNT_dec(SvRV(w->cb_copy)); }
602 #endif /* MGf_COPY */
603 #if MGf_DUP
604  if (w->cb_dup   != NULL) { SvREFCNT_dec(SvRV(w->cb_dup)); }
605 #endif /* MGf_DUP */
606 #if MGf_LOCAL
607  if (w->cb_local != NULL) { SvREFCNT_dec(SvRV(w->cb_local)); }
608 #endif /* MGf_LOCAL */
609 #if VMG_UVAR
610  if (w->cb_fetch  != NULL) { SvREFCNT_dec(SvRV(w->cb_fetch)); }
611  if (w->cb_store  != NULL) { SvREFCNT_dec(SvRV(w->cb_store)); }
612  if (w->cb_exists != NULL) { SvREFCNT_dec(SvRV(w->cb_exists)); }
613  if (w->cb_delete != NULL) { SvREFCNT_dec(SvRV(w->cb_delete)); }
614 #endif /* VMG_UVAR */
615  Safefree(w->vtbl);
616  Safefree(w);
617
618  return 0;
619 }
620
621 STATIC MGVTBL vmg_wizard_vtbl = {
622  NULL,            /* get */
623  NULL,            /* set */
624  NULL,            /* len */
625  NULL,            /* clear */
626  vmg_wizard_free, /* free */
627 #if MGf_COPY
628  NULL,            /* copy */
629 #endif /* MGf_COPY */
630 #if MGf_DUP
631  NULL,            /* dup */
632 #endif /* MGf_DUP */
633 #if MGf_LOCAL
634  NULL,            /* local */
635 #endif /* MGf_LOCAL */
636 };
637
638 STATIC const char vmg_invalid_wiz[]    = "Invalid wizard object";
639 STATIC const char vmg_invalid_sv[]     = "Invalid variable";
640 STATIC const char vmg_invalid_sig[]    = "Invalid numeric signature";
641 STATIC const char vmg_wrongargnum[]    = "Wrong number of arguments";
642 STATIC const char vmg_toomanysigs[]    = "Too many magic signatures used";
643 STATIC const char vmg_argstorefailed[] = "Error while storing arguments";
644
645 STATIC U16 vmg_sv2sig(pTHX_ SV *sv) {
646 #define vmg_sv2sig(S) vmg_sv2sig(aTHX_ (S))
647  U16 sig;
648
649  if (SvIOK(sv)) {
650   sig = SvUVX(sv);
651  } else if (SvNOK(sv)) {
652   sig = SvNVX(sv);
653  } else if ((SvPOK(sv) && grok_number(SvPVX(sv), SvCUR(sv), NULL))) {
654   sig = SvUV(sv);
655  } else {
656   croak(vmg_invalid_sig);
657  }
658  if (sig < SIG_MIN) { sig += SIG_MIN; }
659  if (sig > SIG_MAX) { sig %= SIG_MAX + 1; }
660
661  return sig;
662 }
663
664 #define VMG_SET_CB(S, N)              \
665  cb = (S);                            \
666  w->cb_ ## N = (SvOK(cb) && SvROK(cb)) ? newRV_inc(SvRV(cb)) : NULL;
667
668 #define VMG_SET_SVT_CB(S, N)          \
669  cb = (S);                            \
670  if (SvOK(cb) && SvROK(cb)) {         \
671   t->svt_ ## N = vmg_svt_ ## N;       \
672   w->cb_  ## N = newRV_inc(SvRV(cb)); \
673  } else {                             \
674   t->svt_ ## N = NULL;                \
675   w->cb_  ## N = NULL;                \
676  }
677
678
679 /* --- XS ------------------------------------------------------------------ */
680
681 MODULE = Variable::Magic            PACKAGE = Variable::Magic
682
683 PROTOTYPES: ENABLE
684
685 BOOT:
686 {
687  HV *stash;
688  MY_CXT_INIT;
689  MY_CXT.wizz = newHV();
690  MY_CXT.count = 0;
691  stash = gv_stashpv(__PACKAGE__, 1);
692  newCONSTSUB(stash, "SIG_MIN",   newSVuv(SIG_MIN));
693  newCONSTSUB(stash, "SIG_MAX",   newSVuv(SIG_MAX));
694  newCONSTSUB(stash, "SIG_NBR",   newSVuv(SIG_NBR));
695  newCONSTSUB(stash, "MGf_COPY",  newSVuv(MGf_COPY));
696  newCONSTSUB(stash, "MGf_DUP",   newSVuv(MGf_DUP));
697  newCONSTSUB(stash, "MGf_LOCAL", newSVuv(MGf_LOCAL));
698  newCONSTSUB(stash, "VMG_UVAR",  newSVuv(VMG_UVAR));
699  newCONSTSUB(stash, "VMG_COMPAT_ARRAY_PUSH_NOLEN",
700                     newSVuv(VMG_COMPAT_ARRAY_PUSH_NOLEN));
701  newCONSTSUB(stash, "VMG_COMPAT_ARRAY_UNDEF_CLEAR",
702                     newSVuv(VMG_COMPAT_ARRAY_UNDEF_CLEAR));
703  newCONSTSUB(stash, "VMG_COMPAT_HASH_LISTASSIGN_COPY",
704                     newSVuv(VMG_COMPAT_HASH_LISTASSIGN_COPY));
705 }
706
707 SV *_wizard(...)
708 PROTOTYPE: DISABLE
709 PREINIT:
710  I32 i = 0;
711  U16 sig;
712  char buf[8];
713  MGWIZ *w;
714  MGVTBL *t;
715  MAGIC *mg;
716  SV *sv;
717  SV *svsig;
718  SV *cb;
719 CODE:
720  dMY_CXT;
721
722  if (items != 7
723 #if MGf_COPY
724               + 1
725 #endif /* MGf_COPY */
726 #if MGf_DUP
727               + 1
728 #endif /* MGf_DUP */
729 #if MGf_LOCAL
730               + 1
731 #endif /* MGf_LOCAL */
732 #if VMG_UVAR
733               + 4
734 #endif /* VMG_UVAR */
735               ) { croak(vmg_wrongargnum); }
736
737  svsig = ST(i++);
738  if (SvOK(svsig)) {
739   SV **old;
740   sig = vmg_sv2sig(svsig);
741   if ((old = hv_fetch(MY_CXT.wizz, buf, sprintf(buf, "%u", sig), 0))) {
742    ST(0) = sv_2mortal(newRV_inc(*old));
743    XSRETURN(1);
744   }
745  } else {
746   if (MY_CXT.count >= SIG_NBR) { croak(vmg_toomanysigs); }
747   sig = vmg_gensig();
748  }
749  
750  Newx(t, 1, MGVTBL);
751  Newx(w, 1, MGWIZ);
752
753  VMG_SET_CB(ST(i++), data);
754  VMG_SET_SVT_CB(ST(i++), get);
755  VMG_SET_SVT_CB(ST(i++), set);
756  VMG_SET_SVT_CB(ST(i++), len);
757  VMG_SET_SVT_CB(ST(i++), clear);
758  VMG_SET_SVT_CB(ST(i++), free);
759 #if MGf_COPY
760  VMG_SET_SVT_CB(ST(i++), copy);
761 #endif /* MGf_COPY */
762 #if MGf_DUP
763  VMG_SET_SVT_CB(ST(i++), dup);
764 #endif /* MGf_DUP */
765 #if MGf_LOCAL
766  VMG_SET_SVT_CB(ST(i++), local);
767 #endif /* MGf_LOCAL */
768 #if VMG_UVAR
769  VMG_SET_CB(ST(i++), fetch);
770  VMG_SET_CB(ST(i++), store);
771  VMG_SET_CB(ST(i++), exists);
772  VMG_SET_CB(ST(i++), delete);
773 #endif /* VMG_UVAR */
774
775  w->vtbl = t;
776  w->sig  = sig;
777 #if VMG_UVAR
778  w->uvar = (w->cb_fetch || w->cb_store || w->cb_exists || w->cb_delete);
779 #endif /* VMG_UVAR */
780
781  sv = MGWIZ2SV(w);
782  mg = sv_magicext(sv, NULL, PERL_MAGIC_ext, &vmg_wizard_vtbl, NULL, -1);
783  mg->mg_private = SIG_WIZ;
784
785  hv_store(MY_CXT.wizz, buf, sprintf(buf, "%u", sig), sv, 0);
786  ++MY_CXT.count;
787  
788  RETVAL = newRV_noinc(sv);
789 OUTPUT:
790  RETVAL
791
792 SV *gensig()
793 PROTOTYPE:
794 CODE:
795  dMY_CXT;
796  if (MY_CXT.count >= SIG_NBR) { croak(vmg_toomanysigs); }
797  RETVAL = newSVuv(vmg_gensig());
798 OUTPUT:
799  RETVAL
800
801 SV *getsig(SV *wiz)
802 PROTOTYPE: $
803 CODE:
804  if (!SvROK(wiz)) { croak(vmg_invalid_wiz); }
805  RETVAL = newSVuv(SV2MGWIZ(SvRV(wiz))->sig);
806 OUTPUT:
807  RETVAL
808
809 SV *cast(SV *sv, SV *wiz, ...)
810 PROTOTYPE: \[$@%&*]$@
811 PREINIT:
812  AV *args = NULL;
813  SV *ret;
814 CODE:
815  dMY_CXT;
816  if (SvROK(wiz)) {
817   wiz = SvRV(wiz);
818  } else if (SvOK(wiz)) {
819   char buf[8];
820   SV **old;
821   U16 sig = vmg_sv2sig(wiz);
822   if ((old = hv_fetch(MY_CXT.wizz, buf, sprintf(buf, "%u", sig), 0))) {
823    wiz = *old;
824   } else {
825    XSRETURN_UNDEF;
826   }
827  } else {
828   croak(vmg_invalid_sig);
829  }
830  if (items > 2) {
831   I32 i;
832   args = newAV();
833   av_fill(args, items - 2);
834   for (i = 2; i < items; ++i) {
835    SV *arg = ST(i);
836    SvREFCNT_inc(arg);
837    if (av_store(args, i - 2, arg) == NULL) { croak(vmg_argstorefailed); }
838   }
839  }
840  ret = newSVuv(vmg_cast(SvRV(sv), wiz, args));
841  SvREFCNT_dec(args);
842  RETVAL = ret;
843 OUTPUT:
844  RETVAL
845
846 SV *getdata(SV *sv, SV *wiz)
847 PROTOTYPE: \[$@%&*]$
848 PREINIT:
849  SV *data;
850  U16 sig;
851 CODE:
852  dMY_CXT;
853  if (SvROK(wiz)) {
854   sig = SV2MGWIZ(SvRV(wiz))->sig;
855  } else if (SvOK(wiz)) {
856   char buf[8];
857   sig = vmg_sv2sig(wiz);
858   if (!hv_fetch(MY_CXT.wizz, buf, sprintf(buf, "%u", sig), 0)) {
859    XSRETURN_UNDEF;
860   }
861  } else {
862   croak(vmg_invalid_wiz);
863  }
864  data = vmg_data_get(SvRV(sv), sig);
865  if (!data) { XSRETURN_UNDEF; }
866  ST(0) = data;
867  XSRETURN(1);
868
869 SV *dispell(SV *sv, SV *wiz)
870 PROTOTYPE: \[$@%&*]$
871 PREINIT:
872  U16 sig;
873 CODE:
874  dMY_CXT;
875  if (SvROK(wiz)) {
876   sig = SV2MGWIZ(SvRV(wiz))->sig;
877  } else if (SvOK(wiz)) {
878   char buf[8];
879   sig = vmg_sv2sig(wiz);
880   if (!hv_fetch(MY_CXT.wizz, buf, sprintf(buf, "%u", sig), 0)) {
881    XSRETURN_UNDEF;
882   }
883  } else {
884   croak(vmg_invalid_wiz);
885  }
886  RETVAL = newSVuv(vmg_dispell(SvRV(sv), sig));
887 OUTPUT:
888  RETVAL