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