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