]> git.vpit.fr Git - perl/modules/re-engine-Plugin.git/blob - Plugin.xs
2229d48f3882b3ea8acb2ab1b4a64f83767b52aa
[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 /* --- Helpers ------------------------------------------------------------- */
10
11 #define XSH_PACKAGE "re::engine::Plugin"
12
13 #include "xsh/caps.h"
14 #include "xsh/util.h"
15
16 /* ... Lexical hints ....................................................... */
17
18 typedef struct {
19  SV *comp;
20  SV *exec;
21 } xsh_hints_user_t;
22
23 static SV *rep_validate_callback(SV *code) {
24  if (!SvROK(code))
25   return NULL;
26
27  code = SvRV(code);
28  if (SvTYPE(code) < SVt_PVCV)
29   return NULL;
30
31  return SvREFCNT_inc_simple_NN(code);
32 }
33
34 static void xsh_hints_user_init(pTHX_ xsh_hints_user_t *hv, xsh_hints_user_t *v) {
35  hv->comp = rep_validate_callback(v->comp);
36  hv->exec = rep_validate_callback(v->exec);
37
38  return;
39 }
40
41 #if XSH_THREADSAFE
42
43 static void xsh_hints_user_clone(pTHX_ xsh_hints_user_t *nv, xsh_hints_user_t *ov, CLONE_PARAMS *params) {
44  nv->comp = xsh_dup_inc(ov->comp, params);
45  nv->exec = xsh_dup_inc(ov->exec, params);
46
47  return;
48 }
49
50 #endif /* XSH_THREADSAFE */
51
52 static void xsh_hints_user_deinit(pTHX_ xsh_hints_user_t *hv) {
53  SvREFCNT_dec(hv->comp);
54  SvREFCNT_dec(hv->exec);
55
56  return;
57 }
58
59 #define rep_hint() xsh_hints_detag(xsh_hints_fetch())
60
61 #define XSH_HINTS_TYPE_USER         1
62 #define XSH_HINTS_ONLY_COMPILE_TIME 0
63
64 #include "xsh/hints.h"
65
66 /* ... Thread-local storage ................................................ */
67
68 #define XSH_THREADS_USER_CONTEXT            0
69 #define XSH_THREADS_USER_LOCAL_SETUP        0
70 #define XSH_THREADS_USER_LOCAL_TEARDOWN     0
71 #define XSH_THREADS_USER_GLOBAL_TEARDOWN    0
72 #define XSH_THREADS_COMPILE_TIME_PROTECTION 0
73
74 #include "xsh/threads.h"
75
76 /* --- Custom regexp engine ------------------------------------------------ */
77
78 #define GET_SELF_FROM_PPRIVATE(pprivate) \
79  re__engine__Plugin self;                \
80  SELF_FROM_PPRIVATE(self,pprivate);
81
82 /* re__engine__Plugin self; SELF_FROM_PPRIVATE(self,rx->pprivate) */
83 #define SELF_FROM_PPRIVATE(self, pprivate) \
84  if (sv_isobject(pprivate)) {              \
85   SV *ref = SvRV((SV *) pprivate);         \
86   IV  tmp = SvIV((SV *) ref);              \
87   self = INT2PTR(re__engine__Plugin, tmp); \
88  } else {                                  \
89   Perl_croak(aTHX_ "Not an object");       \
90  }
91
92 #if XSH_HAS_PERL(5, 19, 4)
93 # define REP_ENG_EXEC_MINEND_TYPE SSize_t
94 #else
95 # define REP_ENG_EXEC_MINEND_TYPE I32
96 #endif
97
98 START_EXTERN_C
99 EXTERN_C const regexp_engine engine_plugin;
100 #if XSH_HAS_PERL(5, 11, 0)
101 EXTERN_C REGEXP * Plugin_comp(pTHX_ SV * const, U32);
102 #else
103 EXTERN_C REGEXP * Plugin_comp(pTHX_ const SV * const, const U32);
104 #endif
105 EXTERN_C I32      Plugin_exec(pTHX_ REGEXP * const, char *, char *,
106                               char *, REP_ENG_EXEC_MINEND_TYPE, SV *, void *, U32);
107 #if XSH_HAS_PERL(5, 19, 1)
108 EXTERN_C char *   Plugin_intuit(pTHX_ REGEXP * const, SV *, const char * const,
109                                 char *, char *, U32, re_scream_pos_data *);
110 #else
111 EXTERN_C char *   Plugin_intuit(pTHX_ REGEXP * const, SV *, char *,
112                                 char *, U32, re_scream_pos_data *);
113 #endif
114 EXTERN_C SV *     Plugin_checkstr(pTHX_ REGEXP * const);
115 EXTERN_C void     Plugin_free(pTHX_ REGEXP * const);
116 EXTERN_C void *   Plugin_dupe(pTHX_ REGEXP * const, CLONE_PARAMS *);
117 EXTERN_C void     Plugin_numbered_buff_FETCH(pTHX_ REGEXP * const,
118                                              const I32, SV * const);
119 EXTERN_C void     Plugin_numbered_buff_STORE(pTHX_ REGEXP * const,
120                                              const I32, SV const * const);
121 EXTERN_C I32      Plugin_numbered_buff_LENGTH(pTHX_ REGEXP * const,
122                                               const SV * const, const I32);
123 EXTERN_C SV *     Plugin_named_buff(pTHX_ REGEXP * const, SV * const,
124                                     SV * const, const U32);
125 EXTERN_C SV *     Plugin_named_buff_iter(pTHX_ REGEXP * const, const SV * const,
126                                          const U32);
127 EXTERN_C SV *     Plugin_package(pTHX_ REGEXP * const);
128 #ifdef USE_ITHREADS
129 EXTERN_C void *   Plugin_dupe(pTHX_ REGEXP * const, CLONE_PARAMS *);
130 #endif
131
132 EXTERN_C const regexp_engine engine_plugin;
133 END_EXTERN_C
134
135 #define RE_ENGINE_PLUGIN (&engine_plugin)
136 const regexp_engine engine_plugin = {
137  Plugin_comp,
138  Plugin_exec,
139  Plugin_intuit,
140  Plugin_checkstr,
141  Plugin_free,
142  Plugin_numbered_buff_FETCH,
143  Plugin_numbered_buff_STORE,
144  Plugin_numbered_buff_LENGTH,
145  Plugin_named_buff,
146  Plugin_named_buff_iter,
147  Plugin_package
148 #if defined(USE_ITHREADS)
149  , Plugin_dupe
150 #endif
151 #if XSH_HAS_PERL(5, 17, 0)
152  , 0
153 #endif
154 };
155
156 typedef struct replug {
157  /* Pointer back to the containing regexp struct so that accessors
158   * can modify nparens, gofs etc. */
159  struct regexp *rx;
160
161  /* A copy of the pattern given to comp, for ->pattern */
162  SV *pattern;
163
164  /* A copy of the string being matched against, for ->str */
165  SV *str;
166
167  /* The ->stash */
168  SV *stash;
169
170  /* Callbacks */
171  SV *cb_exec;
172  SV *cb_free;
173
174  /* ->num_captures */
175  SV *cb_num_capture_buff_FETCH;
176  SV *cb_num_capture_buff_STORE;
177  SV *cb_num_capture_buff_LENGTH;
178 } *re__engine__Plugin;
179
180 #if XSH_HAS_PERL(5, 11, 0)
181 # define rxREGEXP(RX)  (SvANY(RX))
182 # define newREGEXP(RX) ((RX) = ((REGEXP *) newSV_type(SVt_REGEXP)))
183 #else
184 # define rxREGEXP(RX)  (RX)
185 # define newREGEXP(RX) (Newxz((RX), 1, struct regexp))
186 #endif
187
188 REGEXP *
189 #if XSH_HAS_PERL(5, 11, 0)
190 Plugin_comp(pTHX_ SV * const pattern, U32 flags)
191 #else
192 Plugin_comp(pTHX_ const SV * const pattern, const U32 flags)
193 #endif
194 {
195  dSP;
196  struct regexp *rx;
197  REGEXP *RX;
198
199  re__engine__Plugin re;
200  const xsh_hints_user_t *h;
201
202  STRLEN plen;
203  char *pbuf;
204
205  SV *obj;
206
207  h = rep_hint();
208  if (!h) /* This looks like a pragma leak. Apply the default behaviour */
209   return re_compile(pattern, flags);
210
211  /* exp/xend version of the pattern & length */
212  pbuf = SvPV((SV *) pattern, plen);
213
214  /* Our blessed object */
215  obj = newSV(0);
216  SvREFCNT_inc_simple_void_NN(obj);
217  Newxz(re, 1, struct replug);
218  sv_setref_pv(obj, "re::engine::Plugin", (void *) re);
219
220  newREGEXP(RX);
221  rx = rxREGEXP(RX);
222
223  re->rx       = rx;               /* Make the rx accessible from self->rx */
224  rx->intflags = flags;            /* Flags for internal use */
225  rx->extflags = flags;            /* Flags for perl to use */
226  rx->engine   = RE_ENGINE_PLUGIN; /* Compile to use this engine */
227
228 #if !XSH_HAS_PERL(5, 11, 0)
229  rx->refcnt   = 1;                /* Refcount so we won't be destroyed */
230
231  /* Precompiled pattern for pp_regcomp to use */
232  rx->prelen   = plen;
233  rx->precomp  = savepvn(pbuf, rx->prelen);
234
235  /* Set up qr// stringification to be equivalent to the supplied
236   * pattern, this should be done via overload eventually.
237   */ 
238  rx->wraplen  = rx->prelen;
239  Newx(rx->wrapped, rx->wraplen, char);
240  Copy(rx->precomp, rx->wrapped, rx->wraplen, char);
241 #endif
242
243  /* Store our private object */
244  rx->pprivate = obj;
245
246  /* Store the pattern for ->pattern */
247  re->pattern  = (SV *) pattern;
248  SvREFCNT_inc_simple_void(re->pattern);
249
250  /* If there's an exec callback, store it into the private object so
251   * that it will be the one to be called, even if the engine changes
252   * in between */
253  if (h->exec) {
254   re->cb_exec = h->exec;
255   SvREFCNT_inc_simple_void_NN(h->exec);
256  }
257
258  re->cb_num_capture_buff_FETCH  = NULL;
259  re->cb_num_capture_buff_STORE  = NULL;
260  re->cb_num_capture_buff_LENGTH = NULL;
261
262  /* Call our callback function if one was defined, if not we've
263   * already set up all the stuff we're going to to need for
264   * subsequent exec and other calls */
265  if (h->comp) {
266   ENTER;
267   SAVETMPS;
268
269   PUSHMARK(SP);
270   XPUSHs(obj);
271   PUTBACK;
272
273   call_sv(h->comp, G_DISCARD);
274
275   FREETMPS;
276   LEAVE;
277  }
278
279  /* If any of the comp-time accessors were called we'll have to
280   * update the regexp struct with the new info.
281   */
282  Newxz(rx->offs, rx->nparens + 1, regexp_paren_pair);
283
284  return RX;
285 }
286
287 I32
288 Plugin_exec(pTHX_ REGEXP * const RX, char *stringarg, char *strend,
289             char *strbeg, REP_ENG_EXEC_MINEND_TYPE minend,
290             SV *sv, void *data, U32 flags)
291 {
292  dSP;
293  I32 matched;
294  struct regexp *rx = rxREGEXP(RX);
295
296  GET_SELF_FROM_PPRIVATE(rx->pprivate);
297
298  if (self->cb_exec) {
299   SV *ret;
300
301   /* Store the current str for ->str */
302   SvREFCNT_dec(self->str);
303   self->str = sv;
304   SvREFCNT_inc_simple_void(self->str);
305
306   ENTER;
307   SAVETMPS;
308
309   PUSHMARK(SP);
310   XPUSHs(rx->pprivate);
311   XPUSHs(sv);
312   PUTBACK;
313
314   call_sv(self->cb_exec, G_SCALAR);
315
316   SPAGAIN;
317
318   ret = POPs;
319   if (SvTRUE(ret))
320    matched = 1;
321   else
322    matched = 0;
323
324   PUTBACK;
325   FREETMPS;
326   LEAVE;
327  } else {
328   matched = 0;
329  }
330
331  return matched;
332 }
333
334 char *
335 #if XSH_HAS_PERL(5, 19, 1)
336 Plugin_intuit(pTHX_ REGEXP * const RX, SV *sv, const char * const strbeg,
337               char *strpos, char *strend, U32 flags, re_scream_pos_data *data)
338 #else
339 Plugin_intuit(pTHX_ REGEXP * const RX, SV *sv, char *strpos,
340               char *strend, U32 flags, re_scream_pos_data *data)
341 #endif
342 {
343  PERL_UNUSED_ARG(RX);
344  PERL_UNUSED_ARG(sv);
345 #if XSH_HAS_PERL(5, 19, 1)
346  PERL_UNUSED_ARG(strbeg);
347 #endif
348  PERL_UNUSED_ARG(strpos);
349  PERL_UNUSED_ARG(strend);
350  PERL_UNUSED_ARG(flags);
351  PERL_UNUSED_ARG(data);
352
353  return NULL;
354 }
355
356 SV *
357 Plugin_checkstr(pTHX_ REGEXP * const RX)
358 {
359  PERL_UNUSED_ARG(RX);
360
361  return NULL;
362 }
363
364 void
365 Plugin_free(pTHX_ REGEXP * const RX)
366 {
367  struct regexp *rx;
368  re__engine__Plugin self;
369
370  if (PL_dirty)
371   return;
372
373  rx = rxREGEXP(RX);
374  SELF_FROM_PPRIVATE(self, rx->pprivate);
375
376  SvREFCNT_dec(self->pattern);
377  SvREFCNT_dec(self->str);
378
379  SvREFCNT_dec(self->cb_exec);
380
381  SvREFCNT_dec(self->cb_num_capture_buff_FETCH);
382  SvREFCNT_dec(self->cb_num_capture_buff_STORE);
383  SvREFCNT_dec(self->cb_num_capture_buff_LENGTH);
384
385  self->rx = NULL;
386  Safefree(self);
387
388 /*
389  dSP;
390  SV *callback;
391
392  callback = self->cb_free;
393
394  if (callback) {
395   ENTER;
396   SAVETMPS;
397
398   PUSHMARK(SP);
399   XPUSHs(rx->pprivate);
400   PUTBACK;
401
402   call_sv(callback, G_DISCARD);
403
404   PUTBACK;
405   FREETMPS;
406   LEAVE;
407  }
408  return;
409 */
410 }
411
412 void *
413 Plugin_dupe(pTHX_ REGEXP * const RX, CLONE_PARAMS *param)
414 {
415  struct regexp *rx = rxREGEXP(RX);
416
417  Perl_croak(aTHX_ "dupe not supported yet");
418
419  return rx->pprivate;
420 }
421
422
423 void
424 Plugin_numbered_buff_FETCH(pTHX_ REGEXP * const RX, const I32 paren,
425                            SV * const sv)
426 {
427  dSP;
428  I32 items;
429  SV *callback;
430  struct regexp *rx = rxREGEXP(RX);
431
432  GET_SELF_FROM_PPRIVATE(rx->pprivate);
433
434  callback = self->cb_num_capture_buff_FETCH;
435
436  if (callback) {
437   ENTER;
438   SAVETMPS;
439
440   PUSHMARK(SP);
441   XPUSHs(rx->pprivate);
442   XPUSHs(sv_2mortal(newSViv(paren)));
443   PUTBACK;
444
445   items = call_sv(callback, G_SCALAR);
446
447   if (items == 1) {
448    SV *ret;
449    SPAGAIN;
450    ret = POPs;
451    sv_setsv(sv, ret);
452   } else {
453    sv_setsv(sv, &PL_sv_undef);
454   }
455
456   PUTBACK;
457   FREETMPS;
458   LEAVE;
459  } else {
460   sv_setsv(sv, &PL_sv_undef);
461  }
462 }
463
464 void
465 Plugin_numbered_buff_STORE(pTHX_ REGEXP * const RX, const I32 paren,
466                            SV const * const value)
467 {
468  dSP;
469  SV *callback;
470  struct regexp *rx = rxREGEXP(RX);
471
472  GET_SELF_FROM_PPRIVATE(rx->pprivate);
473
474  callback = self->cb_num_capture_buff_STORE;
475
476  if (callback) {
477   ENTER;
478   SAVETMPS;
479
480   PUSHMARK(SP);
481   XPUSHs(rx->pprivate);
482   XPUSHs(sv_2mortal(newSViv(paren)));
483   XPUSHs((SV *) value);
484   PUTBACK;
485
486   call_sv(callback, G_DISCARD);
487
488   PUTBACK;
489   FREETMPS;
490   LEAVE;
491  }
492 }
493
494 I32
495 Plugin_numbered_buff_LENGTH(pTHX_ REGEXP * const RX, const SV * const sv,
496                             const I32 paren)
497 {
498  dSP;
499  SV *callback;
500  struct regexp *rx = rxREGEXP(RX);
501
502  GET_SELF_FROM_PPRIVATE(rx->pprivate);
503
504  callback = self->cb_num_capture_buff_LENGTH;
505
506  if (callback) {
507   IV ret;
508
509   ENTER;
510   SAVETMPS;
511
512   PUSHMARK(SP);
513   XPUSHs(rx->pprivate);
514   XPUSHs(sv_2mortal(newSViv(paren)));
515   PUTBACK;
516
517   call_sv(callback, G_SCALAR);
518
519   SPAGAIN;
520
521   ret = POPi;
522
523   PUTBACK;
524   FREETMPS;
525   LEAVE;
526
527   return (I32) ret;
528  } else {
529   /* TODO: call FETCH and get the length on that value */
530   return 0;
531  }
532 }
533
534 SV *
535 Plugin_named_buff(pTHX_ REGEXP * const RX, SV * const key, SV * const value,
536                   const U32 flags)
537 {
538  return NULL;
539 }
540
541 SV *
542 Plugin_named_buff_iter(pTHX_ REGEXP * const RX, const SV * const lastkey,
543                        const U32 flags)
544 {
545  return NULL;
546 }
547
548 SV *
549 Plugin_package(pTHX_ REGEXP * const RX)
550 {
551  PERL_UNUSED_ARG(RX);
552
553  return newSVpvs("re::engine::Plugin");
554 }
555
556 static void xsh_user_global_setup(pTHX) {
557  HV *stash;
558
559  stash = gv_stashpvn(XSH_PACKAGE, XSH_PACKAGE_LEN, 1);
560  newCONSTSUB(stash, "REP_THREADSAFE", newSVuv(XSH_THREADSAFE));
561  newCONSTSUB(stash, "REP_FORKSAFE",   newSVuv(XSH_FORKSAFE));
562
563  return;
564 }
565
566 /* --- XS ------------------------------------------------------------------ */
567
568 MODULE = re::engine::Plugin       PACKAGE = re::engine::Plugin
569
570 PROTOTYPES: DISABLE
571
572 BOOT:
573 {
574  xsh_setup();
575 }
576
577 #if XSH_THREADSAFE
578
579 void
580 CLONE(...)
581 PPCODE:
582  xsh_clone();
583  XSRETURN(0);
584
585 #endif /* XSH_THREADSAFE */
586
587 void
588 pattern(re::engine::Plugin self, ...)
589 PPCODE:
590  XPUSHs(self->pattern);
591
592 void
593 str(re::engine::Plugin self, ...)
594 PPCODE:
595  XPUSHs(self->str);
596
597 void
598 mod(re::engine::Plugin self)
599 PREINIT:
600  U32 flags;
601  char mods[5 + 1];
602  int n = 0, i;
603 PPCODE:
604  flags = self->rx->intflags;
605  if (flags & PMf_FOLD)         /* /i */
606   mods[n++] = 'i';
607  if (flags & PMf_MULTILINE)    /* /m */
608   mods[n++] = 'm';
609  if (flags & PMf_SINGLELINE)   /* /s */
610   mods[n++] = 's';
611  if (flags & PMf_EXTENDED)     /* /x */
612   mods[n++] = 'x';
613  if (flags & RXf_PMf_KEEPCOPY) /* /p */
614   mods[n++] = 'p';
615  mods[n] = '\0';
616  EXTEND(SP, 2 * n);
617  for (i = 0; i < n; ++i) {
618   mPUSHp(mods + i, 1);
619   PUSHs(&PL_sv_yes);
620  }
621  XSRETURN(2 * n);
622
623 void
624 stash(re::engine::Plugin self, ...)
625 PPCODE:
626  if (items > 1) {
627   SvREFCNT_dec(self->stash);
628   self->stash = ST(1);
629   SvREFCNT_inc_simple_void(self->stash);
630   XSRETURN_EMPTY;
631  } else {
632   XPUSHs(self->stash);
633  }
634
635 void
636 minlen(re::engine::Plugin self, ...)
637 PPCODE:
638  if (items > 1) {
639   self->rx->minlen = (I32)SvIV(ST(1));
640   XSRETURN_EMPTY;
641  } else {
642   if (self->rx->minlen) {
643    XPUSHs(sv_2mortal(newSViv(self->rx->minlen)));
644   } else {
645    XPUSHs(sv_2mortal(&PL_sv_undef));
646   }
647  }
648
649 void
650 gofs(re::engine::Plugin self, ...)
651 PPCODE:
652  if (items > 1) {
653   self->rx->gofs = (U32)SvIV(ST(1));
654   XSRETURN_EMPTY;
655  } else {
656   if (self->rx->gofs) {
657    XPUSHs(sv_2mortal(newSVuv(self->rx->gofs)));
658   } else {
659    XPUSHs(sv_2mortal(&PL_sv_undef));
660   }
661  }
662
663 void
664 nparens(re::engine::Plugin self, ...)
665 PPCODE:
666  if (items > 1) {
667   self->rx->nparens = (U32)SvIV(ST(1));
668   XSRETURN_EMPTY;
669  } else {
670   if (self->rx->nparens) {
671    XPUSHs(sv_2mortal(newSVuv(self->rx->nparens)));
672   } else {
673    XPUSHs(sv_2mortal(&PL_sv_undef));
674   }
675  }
676
677 void
678 _exec(re::engine::Plugin self, ...)
679 PPCODE:
680  if (items > 1) {
681   SvREFCNT_dec(self->cb_exec);
682   self->cb_exec = ST(1);
683   SvREFCNT_inc_simple_void(self->cb_exec);
684  }
685
686 void
687 _num_capture_buff_FETCH(re::engine::Plugin self, ...)
688 PPCODE:
689  if (items > 1) {
690   SvREFCNT_dec(self->cb_num_capture_buff_FETCH);
691   self->cb_num_capture_buff_FETCH = ST(1);
692   SvREFCNT_inc_simple_void(self->cb_num_capture_buff_FETCH);
693  }
694
695 void
696 _num_capture_buff_STORE(re::engine::Plugin self, ...)
697 PPCODE:
698  if (items > 1) {
699   SvREFCNT_dec(self->cb_num_capture_buff_STORE);
700   self->cb_num_capture_buff_STORE = ST(1);
701   SvREFCNT_inc_simple_void(self->cb_num_capture_buff_STORE);
702  }
703
704 void
705 _num_capture_buff_LENGTH(re::engine::Plugin self, ...)
706 PPCODE:
707  if (items > 1) {
708   SvREFCNT_dec(self->cb_num_capture_buff_LENGTH);
709   self->cb_num_capture_buff_LENGTH = ST(1);
710   SvREFCNT_inc_simple_void(self->cb_num_capture_buff_LENGTH);
711  }
712
713 SV *
714 _tag(SV *comp, SV *exec)
715 PREINIT:
716  xsh_hints_user_t arg;
717 CODE:
718  arg.comp = comp;
719  arg.exec = exec;
720  RETVAL = xsh_hints_tag(&arg);
721 OUTPUT:
722  RETVAL
723
724 void
725 ENGINE()
726 PPCODE:
727  XPUSHs(sv_2mortal(newSViv(PTR2IV(&engine_plugin))));