]> git.vpit.fr Git - perl/modules/re-engine-Plugin.git/blob - Plugin.xs
2b0fe041b8270ce8d0b71a4e14077107585d23a5
[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 REP_HAS_PERL(5, 11, 0)
314 EXTERN_C REGEXP * Plugin_comp(pTHX_ SV * const, U32);
315 #else
316 EXTERN_C REGEXP * Plugin_comp(pTHX_ const SV * const, 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
340 EXTERN_C const regexp_engine engine_plugin;
341 END_EXTERN_C
342
343 #define RE_ENGINE_PLUGIN (&engine_plugin)
344 const regexp_engine engine_plugin = {
345     Plugin_comp,
346     Plugin_exec,
347     Plugin_intuit,
348     Plugin_checkstr,
349     Plugin_free,
350     Plugin_numbered_buff_FETCH,
351     Plugin_numbered_buff_STORE,
352     Plugin_numbered_buff_LENGTH,
353     Plugin_named_buff,
354     Plugin_named_buff_iter,
355     Plugin_package,
356 #if defined(USE_ITHREADS)
357     Plugin_dupe,
358 #endif
359 };
360
361 typedef struct replug {
362     /* Pointer back to the containing regexp struct so that accessors
363      * can modify nparens, gofs etc. */
364     struct regexp * rx;
365
366     /* A copy of the pattern given to comp, for ->pattern */
367     SV * pattern;
368
369     /* A copy of the string being matched against, for ->str */
370     SV * str;
371
372     /* The ->stash */
373     SV * stash;
374
375     /* Callbacks */
376     SV * cb_exec;
377     SV * cb_free;
378
379     /* ->num_captures */
380     SV * cb_num_capture_buff_FETCH;
381     SV * cb_num_capture_buff_STORE;
382     SV * cb_num_capture_buff_LENGTH;
383 } *re__engine__Plugin;
384
385 #if REP_HAS_PERL(5, 11, 0)
386 # define rxREGEXP(RX)  (SvANY(RX))
387 # define newREGEXP(RX) ((RX) = ((REGEXP*) newSV_type(SVt_REGEXP)))
388 #else
389 # define rxREGEXP(RX)  (RX)
390 # define newREGEXP(RX) (Newxz((RX), 1, struct regexp))
391 #endif
392
393 REGEXP *
394 #if REP_HAS_PERL(5, 11, 0)
395 Plugin_comp(pTHX_ SV * const pattern, U32 flags)
396 #else
397 Plugin_comp(pTHX_ const SV * const pattern, const U32 flags)
398 #endif
399 {
400     dSP;
401     struct regexp * rx;
402     REGEXP *RX;
403
404     re__engine__Plugin re;
405     const rep_hint_t *h;
406
407     STRLEN plen;
408     char *pbuf;
409
410     SV *obj;
411
412     h = rep_hint();
413     if (!h) /* This looks like a pragma leak. Apply the default behaviour */
414         return re_compile(pattern, flags);
415
416     /* exp/xend version of the pattern & length */
417     pbuf = SvPV((SV*)pattern, plen);
418
419     /* Our blessed object */
420     obj = newSV(0);
421     SvREFCNT_inc_simple_void_NN(obj);
422     Newxz(re, 1, struct replug);
423     sv_setref_pv(obj, "re::engine::Plugin", (void*)re);
424
425     newREGEXP(RX);
426     rx = rxREGEXP(RX);
427
428     re->rx = rx;                   /* Make the rx accessible from self->rx */
429     rx->intflags = flags;          /* Flags for internal use */
430     rx->extflags = flags;          /* Flags for perl to use */
431     rx->engine = RE_ENGINE_PLUGIN; /* Compile to use this engine */
432
433 #if !REP_HAS_PERL(5, 11, 0)
434     rx->refcnt = 1;                /* Refcount so we won't be destroyed */
435
436     /* Precompiled pattern for pp_regcomp to use */
437     rx->prelen = plen;
438     rx->precomp = savepvn(pbuf, rx->prelen);
439
440     /* Set up qr// stringification to be equivalent to the supplied
441      * pattern, this should be done via overload eventually.
442      */
443     rx->wraplen = rx->prelen;
444     Newx(rx->wrapped, rx->wraplen, char);
445     Copy(rx->precomp, rx->wrapped, rx->wraplen, char);
446 #endif
447
448     /* Store our private object */
449     rx->pprivate = obj;
450
451     /* Store the pattern for ->pattern */
452     re->pattern = (SV*)pattern;
453     SvREFCNT_inc_simple_void(re->pattern);
454
455     /* If there's an exec callback, store it into the private object so
456      * that it will be the one to be called, even if the engine changes
457      * in between */
458     if (h->exec) {
459         re->cb_exec = h->exec;
460         SvREFCNT_inc_simple_void_NN(h->exec);
461     }
462
463     re->cb_num_capture_buff_FETCH  = NULL;
464     re->cb_num_capture_buff_STORE  = NULL;
465     re->cb_num_capture_buff_LENGTH = NULL;
466
467     /* Call our callback function if one was defined, if not we've
468      * already set up all the stuff we're going to to need for
469      * subsequent exec and other calls */
470     if (h->comp) {
471         ENTER;
472         SAVETMPS;
473
474         PUSHMARK(SP);
475         XPUSHs(obj);
476         PUTBACK;
477
478         call_sv(h->comp, G_DISCARD);
479
480         FREETMPS;
481         LEAVE;
482     }
483
484     /* If any of the comp-time accessors were called we'll have to
485      * update the regexp struct with the new info.
486      */
487
488     Newxz(rx->offs, rx->nparens + 1, regexp_paren_pair);
489
490     return RX;
491 }
492
493 I32
494 Plugin_exec(pTHX_ REGEXP * const RX, char *stringarg, char *strend,
495             char *strbeg, I32 minend, SV *sv, void *data, U32 flags)
496 {
497     dSP;
498     I32 matched;
499     struct regexp *rx = rxREGEXP(RX);
500     GET_SELF_FROM_PPRIVATE(rx->pprivate);
501
502     if (self->cb_exec) {
503         SV *ret;
504
505         /* Store the current str for ->str */
506         SvREFCNT_dec(self->str);
507         self->str = sv;
508         SvREFCNT_inc_simple_void(self->str);
509
510         ENTER;
511         SAVETMPS;
512
513         PUSHMARK(SP);
514         XPUSHs(rx->pprivate);
515         XPUSHs(sv);
516         PUTBACK;
517
518         call_sv(self->cb_exec, G_SCALAR);
519
520         SPAGAIN;
521
522         ret = POPs;
523         if (SvTRUE(ret))
524             matched = 1;
525         else
526             matched = 0;
527
528         PUTBACK;
529         FREETMPS;
530         LEAVE;
531     } else {
532         matched = 0;
533     }
534
535     return matched;
536 }
537
538 char *
539 Plugin_intuit(pTHX_ REGEXP * const RX, SV *sv, char *strpos,
540                      char *strend, U32 flags, re_scream_pos_data *data)
541 {
542     PERL_UNUSED_ARG(RX);
543     PERL_UNUSED_ARG(sv);
544     PERL_UNUSED_ARG(strpos);
545     PERL_UNUSED_ARG(strend);
546     PERL_UNUSED_ARG(flags);
547     PERL_UNUSED_ARG(data);
548     return NULL;
549 }
550
551 SV *
552 Plugin_checkstr(pTHX_ REGEXP * const RX)
553 {
554     PERL_UNUSED_ARG(RX);
555     return NULL;
556 }
557
558 void
559 Plugin_free(pTHX_ REGEXP * const RX)
560 {
561     struct regexp *rx;
562     re__engine__Plugin self;
563
564     if (PL_dirty)
565         return;
566
567     rx = rxREGEXP(RX);
568     SELF_FROM_PPRIVATE(self, rx->pprivate);
569
570     SvREFCNT_dec(self->pattern);
571     SvREFCNT_dec(self->str);
572
573     SvREFCNT_dec(self->cb_exec);
574
575     SvREFCNT_dec(self->cb_num_capture_buff_FETCH);
576     SvREFCNT_dec(self->cb_num_capture_buff_STORE);
577     SvREFCNT_dec(self->cb_num_capture_buff_LENGTH);
578
579     self->rx = NULL;
580     Safefree(self);
581
582 /*
583     dSP;
584     SV * callback;
585
586     callback = self->cb_free;
587
588     if (callback) {
589         ENTER;
590         SAVETMPS;
591
592         PUSHMARK(SP);
593         XPUSHs(rx->pprivate);
594         PUTBACK;
595
596         call_sv(callback, G_DISCARD);
597
598         PUTBACK;
599         FREETMPS;
600         LEAVE;
601     }
602     return;
603 */
604 }
605
606 void *
607 Plugin_dupe(pTHX_ REGEXP * const RX, CLONE_PARAMS *param)
608 {
609     struct regexp *rx = rxREGEXP(RX);
610     Perl_croak(aTHX_ "dupe not supported yet");
611     return rx->pprivate;
612 }
613
614
615 void
616 Plugin_numbered_buff_FETCH(pTHX_ REGEXP * const RX, const I32 paren,
617                            SV * const sv)
618 {
619     dSP;
620     I32 items;
621     SV * callback;
622     struct regexp *rx = rxREGEXP(RX);
623     GET_SELF_FROM_PPRIVATE(rx->pprivate);
624
625     callback = self->cb_num_capture_buff_FETCH;
626
627     if (callback) {
628         ENTER;
629         SAVETMPS;
630
631         PUSHMARK(SP);
632         XPUSHs(rx->pprivate);
633         XPUSHs(sv_2mortal(newSViv(paren)));
634         PUTBACK;
635
636         items = call_sv(callback, G_SCALAR);
637
638         if (items == 1) {
639             SV *ret;
640
641             SPAGAIN;
642             ret = POPs;
643             sv_setsv(sv, ret);
644         } else {
645             sv_setsv(sv, &PL_sv_undef);
646         }
647
648         PUTBACK;
649         FREETMPS;
650         LEAVE;
651     } else {
652         sv_setsv(sv, &PL_sv_undef);
653     }
654 }
655
656 void
657 Plugin_numbered_buff_STORE(pTHX_ REGEXP * const RX, const I32 paren,
658                            SV const * const value)
659 {
660     dSP;
661     SV * callback;
662     struct regexp *rx = rxREGEXP(RX);
663     GET_SELF_FROM_PPRIVATE(rx->pprivate);
664
665     callback = self->cb_num_capture_buff_STORE;
666
667     if (callback) {
668         ENTER;
669         SAVETMPS;
670
671         PUSHMARK(SP);
672         XPUSHs(rx->pprivate);
673         XPUSHs(sv_2mortal(newSViv(paren)));
674         XPUSHs((SV *) value);
675         PUTBACK;
676
677         call_sv(callback, G_DISCARD);
678
679         PUTBACK;
680         FREETMPS;
681         LEAVE;
682     }
683 }
684
685 I32
686 Plugin_numbered_buff_LENGTH(pTHX_ REGEXP * const RX, const SV * const sv,
687                               const I32 paren)
688 {
689     dSP;
690     SV * callback;
691     struct regexp *rx = rxREGEXP(RX);
692     GET_SELF_FROM_PPRIVATE(rx->pprivate);
693
694     callback = self->cb_num_capture_buff_LENGTH;
695
696     if (callback) {
697         IV ret;
698
699         ENTER;
700         SAVETMPS;
701
702         PUSHMARK(SP);
703         XPUSHs(rx->pprivate);
704         XPUSHs(sv_2mortal(newSViv(paren)));
705         PUTBACK;
706
707         call_sv(callback, G_SCALAR);
708
709         SPAGAIN;
710
711         ret = POPi;
712
713         PUTBACK;
714         FREETMPS;
715         LEAVE;
716
717         return (I32)ret;
718     } else {
719         /* TODO: call FETCH and get the length on that value */
720         return 0;
721     }
722 }
723
724
725 SV*
726 Plugin_named_buff (pTHX_ REGEXP * const RX, SV * const key, SV * const value,
727                    const U32 flags)
728 {
729     return NULL;
730 }
731
732 SV*
733 Plugin_named_buff_iter (pTHX_ REGEXP * const RX, const SV * const lastkey,
734                         const U32 flags)
735 {
736     return NULL;
737 }
738
739 SV*
740 Plugin_package(pTHX_ REGEXP * const RX)
741 {
742     PERL_UNUSED_ARG(RX);
743     return newSVpvs("re::engine::Plugin");
744 }
745
746 #if REP_THREADSAFE
747
748 STATIC U32 rep_initialized = 0;
749
750 STATIC void rep_teardown(pTHX_ void *root) {
751  if (!rep_initialized || aTHX != root)
752   return;
753
754  {
755   dMY_CXT;
756   ptable_free(MY_CXT.tbl);
757  }
758
759  rep_initialized = 0;
760 }
761
762 STATIC void rep_setup(pTHX) {
763 #define rep_setup() rep_setup(aTHX)
764  if (rep_initialized)
765   return;
766
767  {
768   MY_CXT_INIT;
769   MY_CXT.tbl   = ptable_new();
770   MY_CXT.owner = aTHX;
771  }
772
773  call_atexit(rep_teardown, aTHX);
774
775  rep_initialized = 1;
776 }
777
778 #else  /*  REP_THREADSAFE */
779
780 #define rep_setup()
781
782 #endif /* !REP_THREADSAFE */
783
784 STATIC U32 rep_booted = 0;
785
786 /* --- XS ------------------------------------------------------------------ */
787
788 MODULE = re::engine::Plugin       PACKAGE = re::engine::Plugin
789
790 PROTOTYPES: DISABLE
791
792 BOOT:
793 {
794     if (!rep_booted++) {
795         HV *stash;
796
797         PERL_HASH(rep_hash, __PACKAGE__, __PACKAGE_LEN__);
798
799         stash = gv_stashpvn(__PACKAGE__, __PACKAGE_LEN__, 1);
800         newCONSTSUB(stash, "REP_THREADSAFE",  newSVuv(REP_THREADSAFE));
801         newCONSTSUB(stash, "REP_FORKSAFE",    newSVuv(REP_FORKSAFE));
802     }
803
804     rep_setup();
805 }
806
807 #if REP_THREADSAFE
808
809 void
810 CLONE(...)
811 PREINIT:
812     ptable *t;
813     GV     *gv;
814 PPCODE:
815     {
816         rep_ptable_clone_ud ud;
817         dMY_CXT;
818
819         t = ptable_new();
820         rep_ptable_clone_ud_init(ud, t, MY_CXT.owner);
821         ptable_walk(MY_CXT.tbl, rep_ptable_clone, &ud);
822         rep_ptable_clone_ud_deinit(ud);
823     }
824     {
825         MY_CXT_CLONE;
826         MY_CXT.tbl   = t;
827         MY_CXT.owner = aTHX;
828     }
829     gv = gv_fetchpv(__PACKAGE__ "::_THREAD_CLEANUP", 0, SVt_PVCV);
830     if (gv) {
831         CV *cv = GvCV(gv);
832         if (!PL_endav)
833             PL_endav = newAV();
834         SvREFCNT_inc(cv);
835         if (!av_store(PL_endav, av_len(PL_endav) + 1, (SV *) cv))
836             SvREFCNT_dec(cv);
837         sv_magicext((SV *) PL_endav, NULL, PERL_MAGIC_ext, &rep_endav_vtbl, NULL, 0);
838     }
839     XSRETURN(0);
840
841 void
842 _THREAD_CLEANUP(...)
843 PROTOTYPE: DISABLE
844 PPCODE:
845     rep_thread_cleanup(aTHX_ NULL);
846     XSRETURN(0);
847
848 #endif /* REP_THREADSAFE */
849
850 void
851 pattern(re::engine::Plugin self, ...)
852 PPCODE:
853     XPUSHs(self->pattern);
854
855 void
856 str(re::engine::Plugin self, ...)
857 PPCODE:
858     XPUSHs(self->str);
859
860 void
861 mod(re::engine::Plugin self)
862 PREINIT:
863     U32 flags;
864     char mods[5 + 1];
865     int n = 0, i;
866 PPCODE:
867     flags = self->rx->intflags;
868     if (flags & PMf_FOLD)         /* /i */
869         mods[n++] = 'i';
870     if (flags & PMf_MULTILINE)    /* /m */
871         mods[n++] = 'm';
872     if (flags & PMf_SINGLELINE)   /* /s */
873         mods[n++] = 's';
874     if (flags & PMf_EXTENDED)     /* /x */
875         mods[n++] = 'x';
876     if (flags & RXf_PMf_KEEPCOPY) /* /p */
877         mods[n++] = 'p';
878     mods[n] = '\0';
879     EXTEND(SP, 2 * n);
880     for (i = 0; i < n; ++i) {
881         mPUSHp(mods + i, 1);
882         PUSHs(&PL_sv_yes);
883     }
884     XSRETURN(2 * n);
885
886 void
887 stash(re::engine::Plugin self, ...)
888 PPCODE:
889     if (items > 1) {
890         SvREFCNT_dec(self->stash);
891         self->stash = ST(1);
892         SvREFCNT_inc_simple_void(self->stash);
893         XSRETURN_EMPTY;
894     } else {
895         XPUSHs(self->stash);
896     }
897
898 void
899 minlen(re::engine::Plugin self, ...)
900 PPCODE:
901     if (items > 1) {
902         self->rx->minlen = (I32)SvIV(ST(1));
903         XSRETURN_EMPTY;
904     } else {
905         if (self->rx->minlen) {
906             XPUSHs(sv_2mortal(newSViv(self->rx->minlen)));
907         } else {
908             XPUSHs(sv_2mortal(&PL_sv_undef));
909         }
910     }
911
912 void
913 gofs(re::engine::Plugin self, ...)
914 PPCODE:
915     if (items > 1) {
916         self->rx->gofs = (U32)SvIV(ST(1));
917         XSRETURN_EMPTY;
918     } else {
919         if (self->rx->gofs) {
920             XPUSHs(sv_2mortal(newSVuv(self->rx->gofs)));
921         } else {
922             XPUSHs(sv_2mortal(&PL_sv_undef));
923         }
924     }
925
926 void
927 nparens(re::engine::Plugin self, ...)
928 PPCODE:
929     if (items > 1) {
930         self->rx->nparens = (U32)SvIV(ST(1));
931         XSRETURN_EMPTY;
932     } else {
933         if (self->rx->nparens) {
934             XPUSHs(sv_2mortal(newSVuv(self->rx->nparens)));
935         } else {
936             XPUSHs(sv_2mortal(&PL_sv_undef));
937         }
938     }
939
940 void
941 _exec(re::engine::Plugin self, ...)
942 PPCODE:
943     if (items > 1) {
944         SvREFCNT_dec(self->cb_exec);
945         self->cb_exec = ST(1);
946         SvREFCNT_inc_simple_void(self->cb_exec);
947     }
948
949 void
950 _num_capture_buff_FETCH(re::engine::Plugin self, ...)
951 PPCODE:
952     if (items > 1) {
953         SvREFCNT_dec(self->cb_num_capture_buff_FETCH);
954         self->cb_num_capture_buff_FETCH = ST(1);
955         SvREFCNT_inc_simple_void(self->cb_num_capture_buff_FETCH);
956     }
957
958 void
959 _num_capture_buff_STORE(re::engine::Plugin self, ...)
960 PPCODE:
961     if (items > 1) {
962         SvREFCNT_dec(self->cb_num_capture_buff_STORE);
963         self->cb_num_capture_buff_STORE = ST(1);
964         SvREFCNT_inc_simple_void(self->cb_num_capture_buff_STORE);
965     }
966
967 void
968 _num_capture_buff_LENGTH(re::engine::Plugin self, ...)
969 PPCODE:
970     if (items > 1) {
971         SvREFCNT_dec(self->cb_num_capture_buff_LENGTH);
972         self->cb_num_capture_buff_LENGTH = ST(1);
973         SvREFCNT_inc_simple_void(self->cb_num_capture_buff_LENGTH);
974     }
975
976 SV *
977 _tag(SV *comp, SV *exec)
978 CODE:
979     RETVAL = rep_tag(comp, exec);
980 OUTPUT:
981     RETVAL
982
983 void
984 ENGINE()
985 PPCODE:
986     XPUSHs(sv_2mortal(newSViv(PTR2IV(&engine_plugin))));