]> git.vpit.fr Git - perl/modules/Variable-Magic.git/blob - Magic.xs
Importing Variable-Magic-0.02.tar.gz
[perl/modules/Variable-Magic.git] / Magic.xs
1 #define PERL_NO_GET_CONTEXT
2 #include "EXTERN.h"
3 #include "perl.h"
4 #include "XSUB.h"
5
6 /* --- Compatibility ------------------------------------------------------- */
7
8 #ifndef Newx
9 # define Newx(v, n, c) New(0, v, n, c)
10 #endif
11
12 #ifndef SvMAGIC_set
13 # define SvMAGIC_set(sv, val) (SvMAGIC(sv) = (val))
14 #endif
15
16 #define SIG_WIZ ((U16) (1u << 8 - 1))
17
18 #define R(S) fprintf(stderr, "R(" #S ") = %d\n", SvREFCNT(sv))
19
20 typedef struct {
21  MGVTBL *vtbl;
22  U16 sig;
23  SV *cb_get, *cb_set, *cb_len, *cb_clear, *cb_free, *cb_data;
24 } MGWIZ;
25
26 #define MGWIZ2SV(W) (newSVuv(PTR2UV(W)))
27 #define SV2MGWIZ(S) (INT2PTR(MGWIZ*, SvUVX((SV *) (S))))
28
29 /* ... Construct private data .............................................. */
30
31 STATIC SV *vmg_data_new(pTHX_ SV *ctor, SV *sv) {
32 #define vmg_data_new(C, S) vmg_data_new(aTHX_ (C), (S))
33  SV *nsv;
34
35  dSP;
36  int count;
37
38  ENTER;
39  SAVETMPS;
40
41  PUSHMARK(SP);
42  XPUSHs(sv);
43  PUTBACK;
44
45  count = call_sv(ctor, G_SCALAR);
46
47  SPAGAIN;
48
49  if (count != 1) { croak("Callback needs to return 1 scalar\n"); }
50  nsv = POPs;
51  SvREFCNT_inc(nsv); /* Or it will be destroyed in FREETMPS */
52
53  PUTBACK;
54
55  FREETMPS;
56  LEAVE;
57
58  return nsv;
59 }
60
61 STATIC SV *vmg_data_get(SV *sv, U16 sig) {
62  MAGIC *mg, *moremagic;
63  MGWIZ *w;
64
65  if (SvTYPE(sv) >= SVt_PVMG) {
66   for (mg = SvMAGIC(sv); mg; mg = moremagic) {
67    moremagic = mg->mg_moremagic;
68    if ((mg->mg_type == PERL_MAGIC_ext) && (mg->mg_private == sig)) { break; }
69   }
70   if (mg) { return mg->mg_obj; }
71  }
72
73  return NULL;
74
75
76 /* ... Magic cast/dispell .................................................. */
77
78 STATIC UV vmg_cast(pTHX_ SV *sv, SV *wiz) {
79 #define vmg_cast(S, W) vmg_cast(aTHX_ (S), (W))
80  MAGIC *mg = NULL, *moremagic = NULL;
81  MGWIZ *w;
82  SV *data;
83
84  w = SV2MGWIZ(wiz);
85
86  if (SvTYPE(sv) >= SVt_PVMG) {
87   for (mg = SvMAGIC(sv); mg; mg = moremagic) {
88    moremagic = mg->mg_moremagic;
89    if ((mg->mg_type == PERL_MAGIC_ext) && (mg->mg_private == w->sig)) { break; }
90   }
91   if (mg) { return 1; }
92  }
93
94  data = (w->cb_data) ? vmg_data_new(w->cb_data, sv) : NULL;
95  mg = sv_magicext(sv, data, PERL_MAGIC_ext, w->vtbl,
96                             (const char *) wiz, HEf_SVKEY);
97  mg->mg_private = w->sig;
98
99  return 1;
100 }
101
102 STATIC UV vmg_dispell(pTHX_ SV *sv, U16 sig) {
103 #define vmg_dispell(S, Z) vmg_dispell(aTHX_ (S), (Z))
104  MAGIC *mg, *prevmagic, *moremagic = NULL;
105  MGWIZ *w;
106
107  if (SvTYPE(sv) < SVt_PVMG) { return 0; }
108
109  for (prevmagic = NULL, mg = SvMAGIC(sv); mg; prevmagic = mg, mg = moremagic) {
110   moremagic = mg->mg_moremagic;
111   if ((mg->mg_type == PERL_MAGIC_ext) && (mg->mg_private == sig)) { break; }
112  }
113  if (!mg) { return 0; }
114
115  if (prevmagic) {
116   prevmagic->mg_moremagic = moremagic;
117  } else {
118   SvMAGIC_set(sv, moremagic);
119  }
120  mg->mg_moremagic = NULL;
121
122  if (mg->mg_obj != sv) { SvREFCNT_dec(mg->mg_obj); } /* Destroy private data */
123  SvREFCNT_dec((SV *) mg->mg_ptr); /* Unreference the wizard */
124  Safefree(mg);
125
126  return 1;
127 }
128
129 /* ... svt callbacks ....................................................... */
130
131 STATIC int vmg_cb_call(pTHX_ SV *cb, SV *sv, SV *data) {
132 #define vmg_cb_call(I, S, D) vmg_cb_call(aTHX_ (I), (S), (D))
133  int ret;
134
135  dSP;
136  int count;
137
138  ENTER;
139  SAVETMPS;
140
141  PUSHMARK(SP);
142  switch (SvTYPE(sv)) {
143   case SVt_PVAV:
144   case SVt_PVHV: XPUSHs(sv_2mortal(newRV_inc(sv))); break;
145   default:       XPUSHs(sv);
146  }
147  if (data) { XPUSHs(data); }
148  PUTBACK;
149
150  count = call_sv(cb, G_SCALAR);
151
152  SPAGAIN;
153
154  if (count != 1) { croak("Callback needs to return 1 scalar\n"); }
155  ret = POPi;
156
157  PUTBACK;
158
159  FREETMPS;
160  LEAVE;
161
162  return ret;
163 }
164
165 STATIC int vmg_svt_get(pTHX_ SV *sv, MAGIC *mg) {
166  return vmg_cb_call(SV2MGWIZ(mg->mg_ptr)->cb_get, sv, mg->mg_obj);
167 }
168
169 STATIC int vmg_svt_set(pTHX_ SV *sv, MAGIC *mg) {
170  return vmg_cb_call(SV2MGWIZ(mg->mg_ptr)->cb_set, sv, mg->mg_obj);
171 }
172
173 STATIC U32 vmg_svt_len(pTHX_ SV *sv, MAGIC *mg) {
174  U32 ret;
175
176  dSP;
177  int count;
178
179  ENTER;
180  SAVETMPS;
181
182  PUSHMARK(SP);
183  switch (SvTYPE(sv)) {
184   case SVt_PVAV:
185   case SVt_PVHV: XPUSHs(sv_2mortal(newRV_inc(sv))); break;
186   default:       XPUSHs(sv);
187  }
188  XPUSHs((mg->mg_obj) ? (mg->mg_obj) : &PL_sv_undef);
189  if (SvTYPE(sv) == SVt_PVAV) {
190   XPUSHs(sv_2mortal(newSViv(av_len((AV *) sv) + 1)));
191  }
192  PUTBACK;
193
194  count = call_sv(SV2MGWIZ(mg->mg_ptr)->cb_len, G_SCALAR);
195
196  SPAGAIN;
197
198  if (count != 1) { croak("Callback needs to return 1 scalar\n"); }
199  ret = POPi;
200
201  PUTBACK;
202
203  FREETMPS;
204  LEAVE;
205
206  return ret - 1;
207 }
208
209 STATIC int vmg_svt_clear(pTHX_ SV *sv, MAGIC *mg) {
210  return vmg_cb_call(SV2MGWIZ(mg->mg_ptr)->cb_clear, sv, mg->mg_obj);
211 }
212
213 STATIC int vmg_svt_free(pTHX_ SV *sv, MAGIC *mg) {
214  /* So that it can survive tmp cleanup in vmg_cb_call */
215  if (SvREFCNT(sv) <= 0) { SvREFCNT_inc(sv); }
216  /* Perl_mg_free will get rid of the magic and decrement mg->mg_obj and
217   * mg->mg_ptr reference count */
218  return vmg_cb_call(SV2MGWIZ(mg->mg_ptr)->cb_free, sv, mg->mg_obj);
219 }
220
221 /* ... Wizard destructor ................................................... */
222
223 STATIC int vmg_wizard_free(pTHX_ SV *wiz, MAGIC *mg) {
224  MGWIZ *w = SV2MGWIZ(wiz);
225
226  if (w->cb_get   != NULL) { SvREFCNT_dec(SvRV(w->cb_get)); }
227  if (w->cb_set   != NULL) { SvREFCNT_dec(SvRV(w->cb_set)); }
228  if (w->cb_len   != NULL) { SvREFCNT_dec(SvRV(w->cb_len)); }
229  if (w->cb_clear != NULL) { SvREFCNT_dec(SvRV(w->cb_clear)); }
230  if (w->cb_free  != NULL) { SvREFCNT_dec(SvRV(w->cb_free)); }
231  if (w->cb_data  != NULL) { SvREFCNT_dec(SvRV(w->cb_data)); }
232  Safefree(w->vtbl);
233  Safefree(w);
234
235  return 0;
236 }
237
238 STATIC MGVTBL vmg_wizard_vtbl = {
239  NULL,            /* get */
240  NULL,            /* set */
241  NULL,            /* len */
242  NULL,            /* clear */
243  vmg_wizard_free, /* free */
244 #ifdef MGf_COPY
245  NULL,            /* copy */
246 #endif /* MGf_COPY */
247 #ifdef MGf_DUP
248  NULL,            /* dup */
249 #endif /* MGf_DUP */
250 };
251
252 STATIC const char vmg_invalid_wiz[] = "Invalid wizard object";
253 STATIC const char vmg_invalid_sv[]  = "Invalid variable";
254 STATIC const char vmg_invalid_sig[] = "Invalid numeric signature";
255
256 /* --- XS ------------------------------------------------------------------ */
257
258 MODULE = Variable::Magic            PACKAGE = Variable::Magic
259
260 PROTOTYPES: ENABLE
261
262 SV *_wizard(SV *sig, SV *cb_get, SV *cb_set, SV *cb_len, SV *cb_clear, SV *cb_free, SV *cb_data)
263 PROTOTYPE: $&&&&&
264 PREINIT:
265  MGWIZ *w;
266  MGVTBL *t;
267  MAGIC *mg;
268  SV *sv;
269 CODE:
270  if (!SvIOK(sig)) { croak(vmg_invalid_sig); }
271  
272  Newx(t, 1, MGVTBL);
273  t->svt_get   = (SvOK(cb_get))   ? vmg_svt_get   : NULL;
274  t->svt_set   = (SvOK(cb_set))   ? vmg_svt_set   : NULL;
275  t->svt_len   = (SvOK(cb_len))   ? vmg_svt_len   : NULL;
276  t->svt_clear = (SvOK(cb_clear)) ? vmg_svt_clear : NULL;
277  t->svt_free  = (SvOK(cb_free))  ? vmg_svt_free  : NULL;
278 #ifdef MGf_COPY
279  t->svt_copy  = NULL;
280 #endif /* MGf_COPY */
281 #ifdef MGf_DUP
282  t->svt_dup   = NULL;
283 #endif /* MGf_DUP */
284
285  Newx(w, 1, MGWIZ);
286  w->vtbl = t;
287  w->sig  = SvUVX(sig);
288  w->cb_get   = (SvROK(cb_get))   ? newRV_inc(SvRV(cb_get))   : NULL;
289  w->cb_set   = (SvROK(cb_set))   ? newRV_inc(SvRV(cb_set))   : NULL;
290  w->cb_len   = (SvROK(cb_len))   ? newRV_inc(SvRV(cb_len))   : NULL;
291  w->cb_clear = (SvROK(cb_clear)) ? newRV_inc(SvRV(cb_clear)) : NULL;
292  w->cb_free  = (SvROK(cb_free))  ? newRV_inc(SvRV(cb_free))  : NULL;
293  w->cb_data  = (SvROK(cb_data))  ? newRV_inc(SvRV(cb_data))  : NULL;
294
295  sv = MGWIZ2SV(w);
296  mg = sv_magicext(sv, NULL, PERL_MAGIC_ext, &vmg_wizard_vtbl, NULL, -1);
297  mg->mg_private = SIG_WIZ;
298
299  RETVAL = newRV_noinc(sv);
300 OUTPUT:
301  RETVAL
302
303 SV *getsig(SV *wiz)
304 PROTOTYPE: $
305 CODE:
306  if (!SvROK(wiz)) { croak(vmg_invalid_wiz); }
307  RETVAL = newSVuv(SV2MGWIZ(SvRV(wiz))->sig);
308 OUTPUT:
309  RETVAL
310
311 SV *cast(SV *sv, SV *wiz)
312 PROTOTYPE: \[$@%&*]$
313 CODE:
314  if (!SvROK(wiz)) { croak(vmg_invalid_wiz); }
315  RETVAL = newSVuv(vmg_cast(SvRV(sv), SvRV(wiz)));
316 OUTPUT:
317  RETVAL
318
319 SV *getdata(SV *sv, SV *wiz)
320 PROTOTYPE: \[$@%&*]$
321 PREINIT:
322  SV *data;
323 CODE:
324  if (!SvROK(wiz)) { croak(vmg_invalid_wiz); }
325  data = vmg_data_get(SvRV(sv), SV2MGWIZ(SvRV(wiz))->sig);
326  if (!data) { XSRETURN_UNDEF; }
327  ST(0) = newSVsv(data);
328  XSRETURN(1);
329
330 SV *dispell(SV *sv, SV *wiz)
331 PROTOTYPE: \[$@%&*]$
332 PREINIT:
333  U16 sig;
334 CODE:
335  if (SvROK(wiz)) {
336   sig = SV2MGWIZ(SvRV(wiz))->sig;
337  } else if (SvIOK(wiz)) {
338   sig = SvUVX(wiz);
339  } else {
340   croak(vmg_invalid_wiz);
341  }
342  RETVAL = newSVuv(vmg_dispell(SvRV(sv), sig));
343 OUTPUT:
344  RETVAL