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