]> git.vpit.fr Git - perl/modules/re-engine-Plugin.git/blob - Plugin.xs
5095c730d47133255bc6526a415295f16cd16428
[perl/modules/re-engine-Plugin.git] / Plugin.xs
1 /* This file is part of the re::engine::Plugin Perl module.
2  * See http://search.cpan.org/dist/re-engine-Plugin/ */
3
4 #define PERL_NO_GET_CONTEXT
5 #include "EXTERN.h"
6 #include "perl.h"
7 #include "XSUB.h"
8
9 #define __PACKAGE__     "re::engine::Plugin"
10 #define __PACKAGE_LEN__ (sizeof(__PACKAGE__)-1)
11
12 #define REP_HAS_PERL(R, V, S) (PERL_REVISION > (R) || (PERL_REVISION == (R) && (PERL_VERSION > (V) || (PERL_VERSION == (V) && (PERL_SUBVERSION >= (S))))))
13
14 #ifndef REP_WORKAROUND_REQUIRE_PROPAGATION
15 # define REP_WORKAROUND_REQUIRE_PROPAGATION !REP_HAS_PERL(5, 10, 1)
16 #endif
17
18 /* ... Thread safety and multiplicity ...................................... */
19
20 /* Safe unless stated otherwise in Makefile.PL */
21 #ifndef REP_FORKSAFE
22 # define REP_FORKSAFE 1
23 #endif
24
25 #ifndef REP_MULTIPLICITY
26 # if defined(MULTIPLICITY) || defined(PERL_IMPLICIT_CONTEXT)
27 #  define REP_MULTIPLICITY 1
28 # else
29 #  define REP_MULTIPLICITY 0
30 # endif
31 #endif
32 #if REP_MULTIPLICITY && !defined(tTHX)
33 # define tTHX PerlInterpreter*
34 #endif
35
36 #if REP_MULTIPLICITY && defined(USE_ITHREADS) && defined(dMY_CXT) && defined(MY_CXT) && defined(START_MY_CXT) && defined(MY_CXT_INIT) && (defined(MY_CXT_CLONE) || defined(dMY_CXT_SV))
37 # define REP_THREADSAFE 1
38 # ifndef MY_CXT_CLONE
39 #  define MY_CXT_CLONE \
40     dMY_CXT_SV;                                                      \
41     my_cxt_t *my_cxtp = (my_cxt_t*)SvPVX(newSV(sizeof(my_cxt_t)-1)); \
42     Copy(INT2PTR(my_cxt_t*, SvUV(my_cxt_sv)), my_cxtp, 1, my_cxt_t); \
43     sv_setuv(my_cxt_sv, PTR2UV(my_cxtp))
44 # endif
45 #else
46 # define REP_THREADSAFE 0
47 # undef  dMY_CXT
48 # define dMY_CXT      dNOOP
49 # undef  MY_CXT
50 # define MY_CXT       rep_globaldata
51 # undef  START_MY_CXT
52 # define START_MY_CXT STATIC my_cxt_t MY_CXT;
53 # undef  MY_CXT_INIT
54 # define MY_CXT_INIT  NOOP
55 # undef  MY_CXT_CLONE
56 # define MY_CXT_CLONE NOOP
57 #endif
58
59 /* --- Helpers ------------------------------------------------------------- */
60
61 /* ... Thread-safe hints ................................................... */
62
63 typedef struct {
64  SV *comp;
65  SV *exec;
66 #if REP_WORKAROUND_REQUIRE_PROPAGATION
67  IV  require_tag;
68 #endif
69 } rep_hint_t;
70
71 #if REP_THREADSAFE
72
73 #define PTABLE_VAL_FREE(V) { \
74  rep_hint_t *h = (V);        \
75  SvREFCNT_dec(h->comp);      \
76  SvREFCNT_dec(h->exec);      \
77  PerlMemShared_free(h);      \
78 }
79
80 #define pPTBL  pTHX
81 #define pPTBL_ pTHX_
82 #define aPTBL  aTHX
83 #define aPTBL_ aTHX_
84
85 #include "ptable.h"
86
87 #define ptable_store(T, K, V) ptable_store(aTHX_ (T), (K), (V))
88 #define ptable_free(T)        ptable_free(aTHX_ (T))
89
90 #define MY_CXT_KEY __PACKAGE__ "::_guts" XS_VERSION
91
92 typedef struct {
93  ptable *tbl;
94  tTHX    owner;
95 } my_cxt_t;
96
97 START_MY_CXT
98
99 typedef struct {
100  ptable *tbl;
101 #if REP_HAS_PERL(5, 13, 2)
102  CLONE_PARAMS *params;
103 #else
104  CLONE_PARAMS params;
105 #endif
106 } rep_ptable_clone_ud;
107
108 #if REP_HAS_PERL(5, 13, 2)
109 # define rep_ptable_clone_ud_init(U, T, O) \
110    (U).tbl    = (T); \
111    (U).params = Perl_clone_params_new((O), aTHX)
112 # define rep_ptable_clone_ud_deinit(U) Perl_clone_params_del((U).params)
113 # define rep_dup_inc(S, U)             SvREFCNT_inc(sv_dup((S), (U)->params))
114 #else
115 # define rep_ptable_clone_ud_init(U, T, O) \
116    (U).tbl               = (T);     \
117    (U).params.stashes    = newAV(); \
118    (U).params.flags      = 0;       \
119    (U).params.proto_perl = (O)
120 # define rep_ptable_clone_ud_deinit(U) SvREFCNT_dec((U).params.stashes)
121 # define rep_dup_inc(S, U)             SvREFCNT_inc(sv_dup((S), &((U)->params)))
122 #endif
123
124 STATIC void rep_ptable_clone(pTHX_ ptable_ent *ent, void *ud_) {
125  rep_ptable_clone_ud *ud = ud_;
126  rep_hint_t *h1 = ent->val;
127  rep_hint_t *h2;
128
129  h2              = PerlMemShared_malloc(sizeof *h2);
130  h2->comp        = rep_dup_inc(h1->comp, ud);
131  h2->exec        = rep_dup_inc(h1->exec, ud);
132 #if REP_WORKAROUND_REQUIRE_PROPAGATION
133  h2->require_tag = PTR2IV(rep_dup_inc(INT2PTR(SV *, h1->require_tag), ud));
134 #endif
135
136  ptable_store(ud->tbl, ent->key, h2);
137 }
138
139 STATIC void rep_thread_cleanup(pTHX_ void *ud) {
140  dMY_CXT;
141
142  ptable_free(MY_CXT.tbl);
143 }
144
145 STATIC int rep_endav_free(pTHX_ SV *sv, MAGIC *mg) {
146  SAVEDESTRUCTOR_X(rep_thread_cleanup, NULL);
147
148  return 0;
149 }
150
151 STATIC MGVTBL rep_endav_vtbl = {
152  0,
153  0,
154  0,
155  0,
156  rep_endav_free
157 #if MGf_COPY
158  , 0
159 #endif
160 #if MGf_DUP
161  , 0
162 #endif
163 #if MGf_LOCAL
164  , 0
165 #endif
166 };
167
168 #endif /* REP_THREADSAFE */
169
170 STATIC SV *rep_validate_callback(SV *code) {
171  if (!SvROK(code))
172   return NULL;
173
174  code = SvRV(code);
175  if (SvTYPE(code) < SVt_PVCV)
176   return NULL;
177
178  return SvREFCNT_inc_simple_NN(code);
179 }
180
181 #if REP_WORKAROUND_REQUIRE_PROPAGATION
182 STATIC IV rep_require_tag(pTHX) {
183 #define rep_require_tag() rep_require_tag(aTHX)
184  const CV *cv, *outside;
185
186  cv = PL_compcv;
187
188  if (!cv) {
189   /* If for some reason the pragma is operational at run-time, try to discover
190    * the current cv in use. */
191   const PERL_SI *si;
192
193   for (si = PL_curstackinfo; si; si = si->si_prev) {
194    I32 cxix;
195
196    for (cxix = si->si_cxix; cxix >= 0; --cxix) {
197     const PERL_CONTEXT *cx = si->si_cxstack + cxix;
198
199     switch (CxTYPE(cx)) {
200      case CXt_SUB:
201      case CXt_FORMAT:
202       /* The propagation workaround is only needed up to 5.10.0 and at that
203        * time format and sub contexts were still identical. And even later the
204        * cv members offsets should have been kept the same. */
205       cv = cx->blk_sub.cv;
206       goto get_enclosing_cv;
207      case CXt_EVAL:
208       cv = cx->blk_eval.cv;
209       goto get_enclosing_cv;
210      default:
211       break;
212     }
213    }
214   }
215
216   cv = PL_main_cv;
217  }
218
219 get_enclosing_cv:
220  for (outside = CvOUTSIDE(cv); outside; outside = CvOUTSIDE(cv))
221   cv = outside;
222
223  return PTR2IV(cv);
224 }
225 #endif /* REP_WORKAROUND_REQUIRE_PROPAGATION */
226
227 STATIC SV *rep_tag(pTHX_ SV *comp, SV *exec) {
228 #define rep_tag(C, E) rep_tag(aTHX_ (C), (E))
229  rep_hint_t *h;
230
231  h              = PerlMemShared_malloc(sizeof *h);
232  h->comp        = rep_validate_callback(comp);
233  h->exec        = rep_validate_callback(exec);
234 #if REP_WORKAROUND_REQUIRE_PROPAGATION
235  h->require_tag = rep_require_tag();
236 #endif /* REP_WORKAROUND_REQUIRE_PROPAGATION */
237
238 #if REP_THREADSAFE
239  {
240   dMY_CXT;
241   /* We only need for the key to be an unique tag for looking up the value later
242    * Allocated memory provides convenient unique identifiers, so that's why we
243    * use the hint as the key itself. */
244   ptable_store(MY_CXT.tbl, h, h);
245  }
246 #endif /* REP_THREADSAFE */
247
248  return newSViv(PTR2IV(h));
249 }
250
251 STATIC const rep_hint_t *rep_detag(pTHX_ const SV *hint) {
252 #define rep_detag(H) rep_detag(aTHX_ (H))
253  rep_hint_t *h;
254
255  if (!(hint && SvIOK(hint)))
256   return NULL;
257
258  h = INT2PTR(rep_hint_t *, SvIVX(hint));
259 #if REP_THREADSAFE
260  {
261   dMY_CXT;
262   h = ptable_fetch(MY_CXT.tbl, h);
263  }
264 #endif /* REP_THREADSAFE */
265
266 #if REP_WORKAROUND_REQUIRE_PROPAGATION
267  if (rep_require_tag() != h->require_tag)
268   return NULL;
269 #endif /* REP_WORKAROUND_REQUIRE_PROPAGATION */
270
271  return h;
272 }
273
274 STATIC U32 rep_hash = 0;
275
276 STATIC const rep_hint_t *rep_hint(pTHX) {
277 #define rep_hint() rep_hint(aTHX)
278  SV *hint;
279
280 #ifdef cop_hints_fetch_pvn
281  hint = cop_hints_fetch_pvn(PL_curcop,
282                             __PACKAGE__, __PACKAGE_LEN__, rep_hash, 0);
283 #else
284  /* We already require 5.9.5 for the regexp engine API. */
285  hint = Perl_refcounted_he_fetch(aTHX_ PL_curcop->cop_hints_hash,
286                                        NULL,
287                                        __PACKAGE__, __PACKAGE_LEN__,
288                                        0,
289                                        rep_hash);
290 #endif
291
292  return rep_detag(hint);
293 }
294
295 /* --- Custom regexp engine ------------------------------------------------ */
296
297 #define GET_SELF_FROM_PPRIVATE(pprivate)        \
298     re__engine__Plugin self;                    \
299     SELF_FROM_PPRIVATE(self,pprivate);
300
301 /* re__engine__Plugin self; SELF_FROM_PPRIVATE(self,rx->pprivate) */
302 #define SELF_FROM_PPRIVATE(self, pprivate)                   \
303     if (sv_isobject(pprivate)) {                             \
304         SV * ref = SvRV((SV*)pprivate);                      \
305         IV tmp = SvIV((SV*)ref);                             \
306         self = INT2PTR(re__engine__Plugin,tmp);              \
307     } else {                                                 \
308         Perl_croak(aTHX_ "Not an object");                   \
309     }
310
311 START_EXTERN_C
312 EXTERN_C const regexp_engine engine_plugin;
313 #if PERL_VERSION <= 10
314 EXTERN_C REGEXP * Plugin_comp(pTHX_ const SV * const, const U32);
315 #else
316 EXTERN_C REGEXP * Plugin_comp(pTHX_ SV * const, U32);
317 #endif
318 EXTERN_C I32      Plugin_exec(pTHX_ REGEXP * const, char *, char *,
319                               char *, I32, SV *, void *, U32);
320 EXTERN_C char *   Plugin_intuit(pTHX_ REGEXP * const, SV *, char *,
321                                 char *, U32, re_scream_pos_data *);
322 EXTERN_C SV *     Plugin_checkstr(pTHX_ REGEXP * const);
323 EXTERN_C void     Plugin_free(pTHX_ REGEXP * const);
324 EXTERN_C void *   Plugin_dupe(pTHX_ REGEXP * const, CLONE_PARAMS *);
325 EXTERN_C void     Plugin_numbered_buff_FETCH(pTHX_ REGEXP * const,
326                                              const I32, SV * const);
327 EXTERN_C void     Plugin_numbered_buff_STORE(pTHX_ REGEXP * const,
328                                              const I32, SV const * const);
329 EXTERN_C I32      Plugin_numbered_buff_LENGTH(pTHX_ REGEXP * const,
330                                               const SV * const, const I32);
331 EXTERN_C SV *     Plugin_named_buff (pTHX_ REGEXP * const, SV * const,
332                                      SV * const, const U32);
333 EXTERN_C SV *     Plugin_named_buff_iter (pTHX_ REGEXP * const, const SV * const,
334                                           const U32);
335 EXTERN_C SV *     Plugin_package(pTHX_ REGEXP * const);
336 #ifdef USE_ITHREADS
337 EXTERN_C void *   Plugin_dupe(pTHX_ REGEXP * const, CLONE_PARAMS *);
338 #endif
339 END_EXTERN_C
340
341 START_EXTERN_C
342 EXTERN_C const regexp_engine engine_plugin;
343 END_EXTERN_C
344
345 #define RE_ENGINE_PLUGIN (&engine_plugin)
346 const regexp_engine engine_plugin = {
347     Plugin_comp,
348     Plugin_exec,
349     Plugin_intuit,
350     Plugin_checkstr,
351     Plugin_free,
352     Plugin_numbered_buff_FETCH,
353     Plugin_numbered_buff_STORE,
354     Plugin_numbered_buff_LENGTH,
355     Plugin_named_buff,
356     Plugin_named_buff_iter,
357     Plugin_package,
358 #if defined(USE_ITHREADS)
359     Plugin_dupe,
360 #endif
361 };
362
363 typedef struct replug {
364     /* Pointer back to the containing regexp struct so that accessors
365      * can modify nparens, gofs etc. */
366     struct regexp * rx;
367
368     /* A copy of the pattern given to comp, for ->pattern */
369     SV * pattern;
370
371     /* A copy of the string being matched against, for ->str */
372     SV * str;
373
374     /* The ->stash */
375     SV * stash;
376
377     /* Callbacks */
378     SV * cb_exec;
379     SV * cb_free;
380
381     /* ->num_captures */
382     SV * cb_num_capture_buff_FETCH;
383     SV * cb_num_capture_buff_STORE;
384     SV * cb_num_capture_buff_LENGTH;
385 } *re__engine__Plugin;
386
387 #if PERL_VERSION >= 11
388 # define rxREGEXP(RX)  (SvANY(RX))
389 # define newREGEXP(RX) ((RX) = ((REGEXP*) newSV_type(SVt_REGEXP)))
390 #else
391 # define rxREGEXP(RX)  (RX)
392 # define newREGEXP(RX) (Newxz((RX), 1, struct regexp))
393 #endif
394
395 REGEXP *
396 #if PERL_VERSION <= 10
397 Plugin_comp(pTHX_ const SV * const pattern, const U32 flags)
398 #else
399 Plugin_comp(pTHX_ SV * const pattern, U32 flags)
400 #endif
401 {
402     dSP;
403     struct regexp * rx;
404     REGEXP *RX;
405
406     re__engine__Plugin re;
407     const rep_hint_t *h;
408
409     STRLEN plen;
410     char *pbuf;
411
412     SV *obj;
413
414     h = rep_hint();
415     if (!h) /* This looks like a pragma leak. Apply the default behaviour */
416         return re_compile(pattern, flags);
417
418     /* exp/xend version of the pattern & length */
419     pbuf = SvPV((SV*)pattern, plen);
420
421     /* Our blessed object */
422     obj = newSV(0);
423     SvREFCNT_inc_simple_void_NN(obj);
424     Newxz(re, 1, struct replug);
425     sv_setref_pv(obj, "re::engine::Plugin", (void*)re);
426
427     newREGEXP(RX);
428     rx = rxREGEXP(RX);
429
430     re->rx = rx;                   /* Make the rx accessible from self->rx */
431     rx->intflags = flags;          /* Flags for internal use */
432     rx->extflags = flags;          /* Flags for perl to use */
433     rx->engine = RE_ENGINE_PLUGIN; /* Compile to use this engine */
434
435 #if PERL_VERSION <= 10
436     rx->refcnt = 1;                /* Refcount so we won't be destroyed */
437
438     /* Precompiled pattern for pp_regcomp to use */
439     rx->prelen = plen;
440     rx->precomp = savepvn(pbuf, rx->prelen);
441
442     /* Set up qr// stringification to be equivalent to the supplied
443      * pattern, this should be done via overload eventually.
444      */
445     rx->wraplen = rx->prelen;
446     Newx(rx->wrapped, rx->wraplen, char);
447     Copy(rx->precomp, rx->wrapped, rx->wraplen, char);
448 #endif
449
450     /* Store our private object */
451     rx->pprivate = obj;
452
453     /* Store the pattern for ->pattern */
454     re->pattern = (SV*)pattern;
455     SvREFCNT_inc_simple_void(re->pattern);
456
457     /* If there's an exec callback, store it into the private object so
458      * that it will be the one to be called, even if the engine changes
459      * in between */
460     if (h->exec) {
461         re->cb_exec = h->exec;
462         SvREFCNT_inc_simple_void_NN(h->exec);
463     }
464
465     re->cb_num_capture_buff_FETCH  = NULL;
466     re->cb_num_capture_buff_STORE  = NULL;
467     re->cb_num_capture_buff_LENGTH = NULL;
468
469     /* Call our callback function if one was defined, if not we've
470      * already set up all the stuff we're going to to need for
471      * subsequent exec and other calls */
472     if (h->comp) {
473         ENTER;
474         SAVETMPS;
475
476         PUSHMARK(SP);
477         XPUSHs(obj);
478         PUTBACK;
479
480         call_sv(h->comp, G_DISCARD);
481
482         FREETMPS;
483         LEAVE;
484     }
485
486     /* If any of the comp-time accessors were called we'll have to
487      * update the regexp struct with the new info.
488      */
489
490     Newxz(rx->offs, rx->nparens + 1, regexp_paren_pair);
491
492     return RX;
493 }
494
495 I32
496 Plugin_exec(pTHX_ REGEXP * const RX, char *stringarg, char *strend,
497             char *strbeg, I32 minend, SV *sv, void *data, U32 flags)
498 {
499     dSP;
500     I32 matched;
501     struct regexp *rx = rxREGEXP(RX);
502     GET_SELF_FROM_PPRIVATE(rx->pprivate);
503
504     if (self->cb_exec) {
505         SV *ret;
506
507         /* Store the current str for ->str */
508         SvREFCNT_dec(self->str);
509         self->str = sv;
510         SvREFCNT_inc_simple_void(self->str);
511
512         ENTER;
513         SAVETMPS;
514
515         PUSHMARK(SP);
516         XPUSHs(rx->pprivate);
517         XPUSHs(sv);
518         PUTBACK;
519
520         call_sv(self->cb_exec, G_SCALAR);
521
522         SPAGAIN;
523
524         ret = POPs;
525         if (SvTRUE(ret))
526             matched = 1;
527         else
528             matched = 0;
529
530         PUTBACK;
531         FREETMPS;
532         LEAVE;
533     } else {
534         matched = 0;
535     }
536
537     return matched;
538 }
539
540 char *
541 Plugin_intuit(pTHX_ REGEXP * const RX, SV *sv, char *strpos,
542                      char *strend, U32 flags, re_scream_pos_data *data)
543 {
544     PERL_UNUSED_ARG(RX);
545     PERL_UNUSED_ARG(sv);
546     PERL_UNUSED_ARG(strpos);
547     PERL_UNUSED_ARG(strend);
548     PERL_UNUSED_ARG(flags);
549     PERL_UNUSED_ARG(data);
550     return NULL;
551 }
552
553 SV *
554 Plugin_checkstr(pTHX_ REGEXP * const RX)
555 {
556     PERL_UNUSED_ARG(RX);
557     return NULL;
558 }
559
560 void
561 Plugin_free(pTHX_ REGEXP * const RX)
562 {
563     struct regexp *rx;
564     re__engine__Plugin self;
565
566     if (PL_dirty)
567         return;
568
569     rx = rxREGEXP(RX);
570     SELF_FROM_PPRIVATE(self, rx->pprivate);
571
572     SvREFCNT_dec(self->pattern);
573     SvREFCNT_dec(self->str);
574
575     SvREFCNT_dec(self->cb_exec);
576
577     SvREFCNT_dec(self->cb_num_capture_buff_FETCH);
578     SvREFCNT_dec(self->cb_num_capture_buff_STORE);
579     SvREFCNT_dec(self->cb_num_capture_buff_LENGTH);
580
581     self->rx = NULL;
582     Safefree(self);
583
584 /*
585     dSP;
586     SV * callback;
587
588     callback = self->cb_free;
589
590     if (callback) {
591         ENTER;
592         SAVETMPS;
593
594         PUSHMARK(SP);
595         XPUSHs(rx->pprivate);
596         PUTBACK;
597
598         call_sv(callback, G_DISCARD);
599
600         PUTBACK;
601         FREETMPS;
602         LEAVE;
603     }
604     return;
605 */
606 }
607
608 void *
609 Plugin_dupe(pTHX_ REGEXP * const RX, CLONE_PARAMS *param)
610 {
611     struct regexp *rx = rxREGEXP(RX);
612     Perl_croak(aTHX_ "dupe not supported yet");
613     return rx->pprivate;
614 }
615
616
617 void
618 Plugin_numbered_buff_FETCH(pTHX_ REGEXP * const RX, const I32 paren,
619                            SV * const sv)
620 {
621     dSP;
622     I32 items;
623     SV * callback;
624     struct regexp *rx = rxREGEXP(RX);
625     GET_SELF_FROM_PPRIVATE(rx->pprivate);
626
627     callback = self->cb_num_capture_buff_FETCH;
628
629     if (callback) {
630         ENTER;
631         SAVETMPS;
632
633         PUSHMARK(SP);
634         XPUSHs(rx->pprivate);
635         XPUSHs(sv_2mortal(newSViv(paren)));
636         PUTBACK;
637
638         items = call_sv(callback, G_SCALAR);
639
640         if (items == 1) {
641             SV *ret;
642
643             SPAGAIN;
644             ret = POPs;
645             sv_setsv(sv, ret);
646         } else {
647             sv_setsv(sv, &PL_sv_undef);
648         }
649
650         PUTBACK;
651         FREETMPS;
652         LEAVE;
653     } else {
654         sv_setsv(sv, &PL_sv_undef);
655     }
656 }
657
658 void
659 Plugin_numbered_buff_STORE(pTHX_ REGEXP * const RX, const I32 paren,
660                            SV const * const value)
661 {
662     dSP;
663     SV * callback;
664     struct regexp *rx = rxREGEXP(RX);
665     GET_SELF_FROM_PPRIVATE(rx->pprivate);
666
667     callback = self->cb_num_capture_buff_STORE;
668
669     if (callback) {
670         ENTER;
671         SAVETMPS;
672
673         PUSHMARK(SP);
674         XPUSHs(rx->pprivate);
675         XPUSHs(sv_2mortal(newSViv(paren)));
676         XPUSHs((SV *) value);
677         PUTBACK;
678
679         call_sv(callback, G_DISCARD);
680
681         PUTBACK;
682         FREETMPS;
683         LEAVE;
684     }
685 }
686
687 I32
688 Plugin_numbered_buff_LENGTH(pTHX_ REGEXP * const RX, const SV * const sv,
689                               const I32 paren)
690 {
691     dSP;
692     SV * callback;
693     struct regexp *rx = rxREGEXP(RX);
694     GET_SELF_FROM_PPRIVATE(rx->pprivate);
695
696     callback = self->cb_num_capture_buff_LENGTH;
697
698     if (callback) {
699         IV ret;
700
701         ENTER;
702         SAVETMPS;
703
704         PUSHMARK(SP);
705         XPUSHs(rx->pprivate);
706         XPUSHs(sv_2mortal(newSViv(paren)));
707         PUTBACK;
708
709         call_sv(callback, G_SCALAR);
710
711         SPAGAIN;
712
713         ret = POPi;
714
715         PUTBACK;
716         FREETMPS;
717         LEAVE;
718
719         return (I32)ret;
720     } else {
721         /* TODO: call FETCH and get the length on that value */
722         return 0;
723     }
724 }
725
726
727 SV*
728 Plugin_named_buff (pTHX_ REGEXP * const RX, SV * const key, SV * const value,
729                    const U32 flags)
730 {
731     return NULL;
732 }
733
734 SV*
735 Plugin_named_buff_iter (pTHX_ REGEXP * const RX, const SV * const lastkey,
736                         const U32 flags)
737 {
738     return NULL;
739 }
740
741 SV*
742 Plugin_package(pTHX_ REGEXP * const RX)
743 {
744     PERL_UNUSED_ARG(RX);
745     return newSVpvs("re::engine::Plugin");
746 }
747
748 #if REP_THREADSAFE
749
750 STATIC U32 rep_initialized = 0;
751
752 STATIC void rep_teardown(pTHX_ void *root) {
753  if (!rep_initialized || aTHX != root)
754   return;
755
756  {
757   dMY_CXT;
758   ptable_free(MY_CXT.tbl);
759  }
760
761  rep_initialized = 0;
762 }
763
764 STATIC void rep_setup(pTHX) {
765 #define rep_setup() rep_setup(aTHX)
766  if (rep_initialized)
767   return;
768
769  {
770   MY_CXT_INIT;
771   MY_CXT.tbl   = ptable_new();
772   MY_CXT.owner = aTHX;
773  }
774
775  call_atexit(rep_teardown, aTHX);
776
777  rep_initialized = 1;
778 }
779
780 #else  /*  REP_THREADSAFE */
781
782 #define rep_setup()
783
784 #endif /* !REP_THREADSAFE */
785
786 STATIC U32 rep_booted = 0;
787
788 /* --- XS ------------------------------------------------------------------ */
789
790 MODULE = re::engine::Plugin       PACKAGE = re::engine::Plugin
791
792 PROTOTYPES: DISABLE
793
794 BOOT:
795 {
796     if (!rep_booted++) {
797         HV *stash;
798
799         PERL_HASH(rep_hash, __PACKAGE__, __PACKAGE_LEN__);
800
801         stash = gv_stashpvn(__PACKAGE__, __PACKAGE_LEN__, 1);
802         newCONSTSUB(stash, "REP_THREADSAFE",  newSVuv(REP_THREADSAFE));
803         newCONSTSUB(stash, "REP_FORKSAFE",    newSVuv(REP_FORKSAFE));
804     }
805
806     rep_setup();
807 }
808
809 #if REP_THREADSAFE
810
811 void
812 CLONE(...)
813 PREINIT:
814     ptable *t;
815     GV     *gv;
816 PPCODE:
817     {
818         rep_ptable_clone_ud ud;
819         dMY_CXT;
820
821         t = ptable_new();
822         rep_ptable_clone_ud_init(ud, t, MY_CXT.owner);
823         ptable_walk(MY_CXT.tbl, rep_ptable_clone, &ud);
824         rep_ptable_clone_ud_deinit(ud);
825     }
826     {
827         MY_CXT_CLONE;
828         MY_CXT.tbl   = t;
829         MY_CXT.owner = aTHX;
830     }
831     gv = gv_fetchpv(__PACKAGE__ "::_THREAD_CLEANUP", 0, SVt_PVCV);
832     if (gv) {
833         CV *cv = GvCV(gv);
834         if (!PL_endav)
835             PL_endav = newAV();
836         SvREFCNT_inc(cv);
837         if (!av_store(PL_endav, av_len(PL_endav) + 1, (SV *) cv))
838             SvREFCNT_dec(cv);
839         sv_magicext((SV *) PL_endav, NULL, PERL_MAGIC_ext, &rep_endav_vtbl, NULL, 0);
840     }
841     XSRETURN(0);
842
843 void
844 _THREAD_CLEANUP(...)
845 PROTOTYPE: DISABLE
846 PPCODE:
847     rep_thread_cleanup(aTHX_ NULL);
848     XSRETURN(0);
849
850 #endif /* REP_THREADSAFE */
851
852 void
853 pattern(re::engine::Plugin self, ...)
854 PPCODE:
855     XPUSHs(self->pattern);
856
857 void
858 str(re::engine::Plugin self, ...)
859 PPCODE:
860     XPUSHs(self->str);
861
862 void
863 mod(re::engine::Plugin self)
864 PREINIT:
865     U32 flags;
866     char mods[5 + 1];
867     int n = 0, i;
868 PPCODE:
869     flags = self->rx->intflags;
870     if (flags & PMf_FOLD)         /* /i */
871         mods[n++] = 'i';
872     if (flags & PMf_MULTILINE)    /* /m */
873         mods[n++] = 'm';
874     if (flags & PMf_SINGLELINE)   /* /s */
875         mods[n++] = 's';
876     if (flags & PMf_EXTENDED)     /* /x */
877         mods[n++] = 'x';
878     if (flags & RXf_PMf_KEEPCOPY) /* /p */
879         mods[n++] = 'p';
880     mods[n] = '\0';
881     EXTEND(SP, 2 * n);
882     for (i = 0; i < n; ++i) {
883         mPUSHp(mods + i, 1);
884         PUSHs(&PL_sv_yes);
885     }
886     XSRETURN(2 * n);
887
888 void
889 stash(re::engine::Plugin self, ...)
890 PPCODE:
891     if (items > 1) {
892         SvREFCNT_dec(self->stash);
893         self->stash = ST(1);
894         SvREFCNT_inc_simple_void(self->stash);
895         XSRETURN_EMPTY;
896     } else {
897         XPUSHs(self->stash);
898     }
899
900 void
901 minlen(re::engine::Plugin self, ...)
902 PPCODE:
903     if (items > 1) {
904         self->rx->minlen = (I32)SvIV(ST(1));
905         XSRETURN_EMPTY;
906     } else {
907         if (self->rx->minlen) {
908             XPUSHs(sv_2mortal(newSViv(self->rx->minlen)));
909         } else {
910             XPUSHs(sv_2mortal(&PL_sv_undef));
911         }
912     }
913
914 void
915 gofs(re::engine::Plugin self, ...)
916 PPCODE:
917     if (items > 1) {
918         self->rx->gofs = (U32)SvIV(ST(1));
919         XSRETURN_EMPTY;
920     } else {
921         if (self->rx->gofs) {
922             XPUSHs(sv_2mortal(newSVuv(self->rx->gofs)));
923         } else {
924             XPUSHs(sv_2mortal(&PL_sv_undef));
925         }
926     }
927
928 void
929 nparens(re::engine::Plugin self, ...)
930 PPCODE:
931     if (items > 1) {
932         self->rx->nparens = (U32)SvIV(ST(1));
933         XSRETURN_EMPTY;
934     } else {
935         if (self->rx->nparens) {
936             XPUSHs(sv_2mortal(newSVuv(self->rx->nparens)));
937         } else {
938             XPUSHs(sv_2mortal(&PL_sv_undef));
939         }
940     }
941
942 void
943 _exec(re::engine::Plugin self, ...)
944 PPCODE:
945     if (items > 1) {
946         SvREFCNT_dec(self->cb_exec);
947         self->cb_exec = ST(1);
948         SvREFCNT_inc_simple_void(self->cb_exec);
949     }
950
951 void
952 _num_capture_buff_FETCH(re::engine::Plugin self, ...)
953 PPCODE:
954     if (items > 1) {
955         SvREFCNT_dec(self->cb_num_capture_buff_FETCH);
956         self->cb_num_capture_buff_FETCH = ST(1);
957         SvREFCNT_inc_simple_void(self->cb_num_capture_buff_FETCH);
958     }
959
960 void
961 _num_capture_buff_STORE(re::engine::Plugin self, ...)
962 PPCODE:
963     if (items > 1) {
964         SvREFCNT_dec(self->cb_num_capture_buff_STORE);
965         self->cb_num_capture_buff_STORE = ST(1);
966         SvREFCNT_inc_simple_void(self->cb_num_capture_buff_STORE);
967     }
968
969 void
970 _num_capture_buff_LENGTH(re::engine::Plugin self, ...)
971 PPCODE:
972     if (items > 1) {
973         SvREFCNT_dec(self->cb_num_capture_buff_LENGTH);
974         self->cb_num_capture_buff_LENGTH = ST(1);
975         SvREFCNT_inc_simple_void(self->cb_num_capture_buff_LENGTH);
976     }
977
978 SV *
979 _tag(SV *comp, SV *exec)
980 CODE:
981     RETVAL = rep_tag(comp, exec);
982 OUTPUT:
983     RETVAL
984
985 void
986 ENGINE()
987 PPCODE:
988     XPUSHs(sv_2mortal(newSViv(PTR2IV(&engine_plugin))));