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