/* This file is part of the re::engine::Plugin Perl module. * See http://search.cpan.org/dist/re-engine-Plugin/ */ #define PERL_NO_GET_CONTEXT #include "EXTERN.h" #include "perl.h" #include "XSUB.h" /* --- Helpers ------------------------------------------------------------- */ #define XSH_PACKAGE "re::engine::Plugin" #include "xsh/caps.h" #include "xsh/util.h" /* ... Lexical hints ....................................................... */ typedef struct { SV *comp; SV *exec; SV *free; } xsh_hints_user_t; static SV *rep_validate_callback(SV *code) { if (!SvROK(code)) return NULL; code = SvRV(code); if (SvTYPE(code) < SVt_PVCV) return NULL; return SvREFCNT_inc_simple_NN(code); } static void xsh_hints_user_init(pTHX_ xsh_hints_user_t *hv, xsh_hints_user_t *v) { hv->comp = rep_validate_callback(v->comp); hv->exec = rep_validate_callback(v->exec); hv->free = rep_validate_callback(v->free); return; } #if XSH_THREADSAFE static void xsh_hints_user_clone(pTHX_ xsh_hints_user_t *nv, xsh_hints_user_t *ov, CLONE_PARAMS *params) { nv->comp = xsh_dup_inc(ov->comp, params); nv->exec = xsh_dup_inc(ov->exec, params); nv->free = xsh_dup_inc(ov->free, params); return; } #endif /* XSH_THREADSAFE */ static void xsh_hints_user_deinit(pTHX_ xsh_hints_user_t *hv) { SvREFCNT_dec(hv->comp); SvREFCNT_dec(hv->exec); SvREFCNT_dec(hv->free); return; } #define rep_hint() xsh_hints_detag(xsh_hints_fetch()) #define XSH_HINTS_TYPE_USER 1 #define XSH_HINTS_ONLY_COMPILE_TIME 0 #include "xsh/hints.h" /* ... Thread-local storage ................................................ */ #define XSH_THREADS_USER_CONTEXT 0 #define XSH_THREADS_USER_LOCAL_SETUP 0 #define XSH_THREADS_USER_LOCAL_TEARDOWN 0 #define XSH_THREADS_USER_GLOBAL_TEARDOWN 0 #define XSH_THREADS_COMPILE_TIME_PROTECTION 0 #include "xsh/threads.h" /* --- Custom regexp engine ------------------------------------------------ */ /* re__engine__Plugin self; SELF_FROM_PPRIVATE(self,rx->pprivate) */ #define SELF_FROM_PPRIVATE(self, pprivate) \ if (sv_isobject(pprivate)) { \ SV *ref = SvRV((SV *) pprivate); \ IV tmp = SvIV((SV *) ref); \ self = INT2PTR(re__engine__Plugin, tmp); \ } else { \ Perl_croak(aTHX_ "Not an object"); \ } #if XSH_HAS_PERL(5, 19, 4) # define REP_ENG_EXEC_MINEND_TYPE SSize_t #else # define REP_ENG_EXEC_MINEND_TYPE I32 #endif START_EXTERN_C EXTERN_C const regexp_engine engine_plugin; #if XSH_HAS_PERL(5, 11, 0) EXTERN_C REGEXP * Plugin_comp(pTHX_ SV * const, U32); #else EXTERN_C REGEXP * Plugin_comp(pTHX_ const SV * const, const U32); #endif EXTERN_C I32 Plugin_exec(pTHX_ REGEXP * const, char *, char *, char *, REP_ENG_EXEC_MINEND_TYPE, SV *, void *, U32); #if XSH_HAS_PERL(5, 19, 1) EXTERN_C char * Plugin_intuit(pTHX_ REGEXP * const, SV *, const char * const, char *, char *, U32, re_scream_pos_data *); #else EXTERN_C char * Plugin_intuit(pTHX_ REGEXP * const, SV *, char *, char *, U32, re_scream_pos_data *); #endif EXTERN_C SV * Plugin_checkstr(pTHX_ REGEXP * const); EXTERN_C void Plugin_free(pTHX_ REGEXP * const); EXTERN_C void * Plugin_dupe(pTHX_ REGEXP * const, CLONE_PARAMS *); EXTERN_C void Plugin_numbered_buff_FETCH(pTHX_ REGEXP * const, const I32, SV * const); EXTERN_C void Plugin_numbered_buff_STORE(pTHX_ REGEXP * const, const I32, SV const * const); EXTERN_C I32 Plugin_numbered_buff_LENGTH(pTHX_ REGEXP * const, const SV * const, const I32); EXTERN_C SV * Plugin_named_buff(pTHX_ REGEXP * const, SV * const, SV * const, const U32); EXTERN_C SV * Plugin_named_buff_iter(pTHX_ REGEXP * const, const SV * const, const U32); EXTERN_C SV * Plugin_package(pTHX_ REGEXP * const); #ifdef USE_ITHREADS EXTERN_C void * Plugin_dupe(pTHX_ REGEXP * const, CLONE_PARAMS *); #endif EXTERN_C const regexp_engine engine_plugin; END_EXTERN_C #define RE_ENGINE_PLUGIN (&engine_plugin) const regexp_engine engine_plugin = { Plugin_comp, Plugin_exec, Plugin_intuit, Plugin_checkstr, Plugin_free, Plugin_numbered_buff_FETCH, Plugin_numbered_buff_STORE, Plugin_numbered_buff_LENGTH, Plugin_named_buff, Plugin_named_buff_iter, Plugin_package #if defined(USE_ITHREADS) , Plugin_dupe #endif #if XSH_HAS_PERL(5, 17, 0) , 0 #endif }; typedef struct replug { /* Pointer back to the containing regexp struct so that accessors * can modify nparens, gofs, etc... */ struct regexp *rx; /* A copy of the pattern given to comp, for ->pattern */ SV *pattern; /* A copy of the string being matched against, for ->str */ SV *str; /* The ->stash */ SV *stash; /* Callbacks */ SV *cb_exec; SV *cb_free; /* ->num_captures */ SV *cb_num_capture_buff_FETCH; SV *cb_num_capture_buff_STORE; SV *cb_num_capture_buff_LENGTH; } *re__engine__Plugin; #if XSH_HAS_PERL(5, 11, 0) # define rxREGEXP(RX) (SvANY(RX)) # define newREGEXP(RX) ((RX) = ((REGEXP *) newSV_type(SVt_REGEXP))) #else # define rxREGEXP(RX) (RX) # define newREGEXP(RX) (Newxz((RX), 1, struct regexp)) #endif REGEXP * #if XSH_HAS_PERL(5, 11, 0) Plugin_comp(pTHX_ SV * const pattern, U32 flags) #else Plugin_comp(pTHX_ const SV * const pattern, const U32 flags) #endif { dSP; struct regexp *rx; REGEXP *RX; re__engine__Plugin re; const xsh_hints_user_t *h; STRLEN plen; char *pbuf; SV *obj; h = rep_hint(); if (!h) /* This looks like a pragma leak. Apply the default behaviour */ return re_compile(pattern, flags); /* exp/xend version of the pattern & length */ pbuf = SvPV((SV *) pattern, plen); /* Our blessed object */ obj = newSV(0); Newxz(re, 1, struct replug); sv_setref_pv(obj, XSH_PACKAGE, (void *) re); newREGEXP(RX); rx = rxREGEXP(RX); re->rx = rx; /* Make the rx accessible from self->rx */ rx->intflags = flags; /* Flags for internal use */ rx->extflags = flags; /* Flags for perl to use */ rx->engine = RE_ENGINE_PLUGIN; /* Compile to use this engine */ #if !XSH_HAS_PERL(5, 11, 0) rx->refcnt = 1; /* Refcount so we won't be destroyed */ /* Precompiled pattern for pp_regcomp to use */ rx->prelen = plen; rx->precomp = savepvn(pbuf, rx->prelen); /* Set up qr// stringification to be equivalent to the supplied * pattern, this should be done via overload eventually */ rx->wraplen = rx->prelen; Newx(rx->wrapped, rx->wraplen, char); Copy(rx->precomp, rx->wrapped, rx->wraplen, char); #endif /* Store our private object */ rx->pprivate = obj; /* Store the pattern for ->pattern */ re->pattern = (SV *) pattern; SvREFCNT_inc_simple_void(re->pattern); /* If there's an exec callback, store it into the private object so that it * will be the one to be called, even if the engine changes in between */ if (h->exec) { re->cb_exec = h->exec; SvREFCNT_inc_simple_void_NN(h->exec); } /* Same goes for the free callback, if there's one. */ if (h->free) { re->cb_free = h->free; SvREFCNT_inc_simple_void_NN(h->free); } re->cb_num_capture_buff_FETCH = NULL; re->cb_num_capture_buff_STORE = NULL; re->cb_num_capture_buff_LENGTH = NULL; /* Call our callback function if one was defined, if not we've already set up * all the stuff we're going to to need for subsequent exec and other calls */ if (h->comp) { ENTER; SAVETMPS; PUSHMARK(SP); XPUSHs(obj); PUTBACK; call_sv(h->comp, G_DISCARD); FREETMPS; LEAVE; } /* If any of the comp-time accessors were called we'll have to * update the regexp struct with the new info */ Newxz(rx->offs, rx->nparens + 1, regexp_paren_pair); return RX; } I32 Plugin_exec(pTHX_ REGEXP * const RX, char *stringarg, char *strend, char *strbeg, REP_ENG_EXEC_MINEND_TYPE minend, SV *sv, void *data, U32 flags) { struct regexp *rx; re__engine__Plugin self; I32 matched; rx = rxREGEXP(RX); SELF_FROM_PPRIVATE(self, rx->pprivate); if (self->cb_exec) { SV *ret; dSP; /* Store the current str for ->str */ SvREFCNT_dec(self->str); self->str = sv; SvREFCNT_inc_simple_void(self->str); ENTER; SAVETMPS; PUSHMARK(SP); XPUSHs(rx->pprivate); XPUSHs(sv); PUTBACK; call_sv(self->cb_exec, G_SCALAR); SPAGAIN; ret = POPs; if (SvTRUE(ret)) matched = 1; else matched = 0; PUTBACK; FREETMPS; LEAVE; } else { matched = 0; } return matched; } char * #if XSH_HAS_PERL(5, 19, 1) Plugin_intuit(pTHX_ REGEXP * const RX, SV *sv, const char * const strbeg, char *strpos, char *strend, U32 flags, re_scream_pos_data *data) #else Plugin_intuit(pTHX_ REGEXP * const RX, SV *sv, char *strpos, char *strend, U32 flags, re_scream_pos_data *data) #endif { PERL_UNUSED_ARG(RX); PERL_UNUSED_ARG(sv); #if XSH_HAS_PERL(5, 19, 1) PERL_UNUSED_ARG(strbeg); #endif PERL_UNUSED_ARG(strpos); PERL_UNUSED_ARG(strend); PERL_UNUSED_ARG(flags); PERL_UNUSED_ARG(data); return NULL; } SV * Plugin_checkstr(pTHX_ REGEXP * const RX) { PERL_UNUSED_ARG(RX); return NULL; } void Plugin_free(pTHX_ REGEXP * const RX) { struct regexp *rx; re__engine__Plugin self; SV *callback; dSP; if (PL_dirty) return; rx = rxREGEXP(RX); SELF_FROM_PPRIVATE(self, rx->pprivate); callback = self->cb_free; if (callback) { ENTER; SAVETMPS; PUSHMARK(SP); XPUSHs(rx->pprivate); PUTBACK; call_sv(callback, G_DISCARD); PUTBACK; FREETMPS; LEAVE; } SvREFCNT_dec(self->pattern); SvREFCNT_dec(self->str); SvREFCNT_dec(self->stash); SvREFCNT_dec(self->cb_exec); SvREFCNT_dec(self->cb_num_capture_buff_FETCH); SvREFCNT_dec(self->cb_num_capture_buff_STORE); SvREFCNT_dec(self->cb_num_capture_buff_LENGTH); self->rx = NULL; Safefree(self); SvREFCNT_dec(rx->pprivate); return; } void * Plugin_dupe(pTHX_ REGEXP * const RX, CLONE_PARAMS *param) { struct regexp *rx = rxREGEXP(RX); Perl_croak(aTHX_ "dupe not supported yet"); return rx->pprivate; } void Plugin_numbered_buff_FETCH(pTHX_ REGEXP * const RX, const I32 paren, SV * const sv) { struct regexp *rx; re__engine__Plugin self; SV *callback; rx = rxREGEXP(RX); SELF_FROM_PPRIVATE(self, rx->pprivate); callback = self->cb_num_capture_buff_FETCH; if (callback) { I32 items; dSP; ENTER; SAVETMPS; PUSHMARK(SP); XPUSHs(rx->pprivate); XPUSHs(sv_2mortal(newSViv(paren))); PUTBACK; items = call_sv(callback, G_SCALAR); if (items == 1) { SV *ret; SPAGAIN; ret = POPs; sv_setsv(sv, ret); } else { sv_setsv(sv, &PL_sv_undef); } PUTBACK; FREETMPS; LEAVE; } else { sv_setsv(sv, &PL_sv_undef); } } void Plugin_numbered_buff_STORE(pTHX_ REGEXP * const RX, const I32 paren, SV const * const value) { struct regexp *rx; re__engine__Plugin self; SV *callback; rx = rxREGEXP(RX); SELF_FROM_PPRIVATE(self, rx->pprivate); callback = self->cb_num_capture_buff_STORE; if (callback) { dSP; ENTER; SAVETMPS; PUSHMARK(SP); XPUSHs(rx->pprivate); XPUSHs(sv_2mortal(newSViv(paren))); XPUSHs((SV *) value); PUTBACK; call_sv(callback, G_DISCARD); PUTBACK; FREETMPS; LEAVE; } } I32 Plugin_numbered_buff_LENGTH(pTHX_ REGEXP * const RX, const SV * const sv, const I32 paren) { struct regexp *rx; re__engine__Plugin self; SV *callback; rx = rxREGEXP(RX); SELF_FROM_PPRIVATE(self, rx->pprivate); callback = self->cb_num_capture_buff_LENGTH; if (callback) { IV ret; dSP; ENTER; SAVETMPS; PUSHMARK(SP); XPUSHs(rx->pprivate); XPUSHs(sv_2mortal(newSViv(paren))); PUTBACK; call_sv(callback, G_SCALAR); SPAGAIN; ret = POPi; PUTBACK; FREETMPS; LEAVE; return (I32) ret; } else { /* TODO: call FETCH and get the length on that value */ return 0; } } SV * Plugin_named_buff(pTHX_ REGEXP * const RX, SV * const key, SV * const value, const U32 flags) { return NULL; } SV * Plugin_named_buff_iter(pTHX_ REGEXP * const RX, const SV * const lastkey, const U32 flags) { return NULL; } SV * Plugin_package(pTHX_ REGEXP * const RX) { PERL_UNUSED_ARG(RX); return newSVpvs(XSH_PACKAGE); } static void xsh_user_global_setup(pTHX) { HV *stash; stash = gv_stashpvn(XSH_PACKAGE, XSH_PACKAGE_LEN, 1); newCONSTSUB(stash, "REP_THREADSAFE", newSVuv(XSH_THREADSAFE)); newCONSTSUB(stash, "REP_FORKSAFE", newSVuv(XSH_FORKSAFE)); return; } /* --- XS ------------------------------------------------------------------ */ MODULE = re::engine::Plugin PACKAGE = re::engine::Plugin PROTOTYPES: DISABLE BOOT: { xsh_setup(); } #if XSH_THREADSAFE void CLONE(...) PPCODE: xsh_clone(); XSRETURN(0); #endif /* XSH_THREADSAFE */ void pattern(re::engine::Plugin self, ...) PPCODE: XPUSHs(self->pattern); XSRETURN(1); void str(re::engine::Plugin self, ...) PPCODE: XPUSHs(self->str); XSRETURN(1); void mod(re::engine::Plugin self) PREINIT: U32 flags; char mods[5 + 1]; int n = 0, i; PPCODE: flags = self->rx->intflags; if (flags & PMf_FOLD) /* /i */ mods[n++] = 'i'; if (flags & PMf_MULTILINE) /* /m */ mods[n++] = 'm'; if (flags & PMf_SINGLELINE) /* /s */ mods[n++] = 's'; if (flags & PMf_EXTENDED) /* /x */ mods[n++] = 'x'; if (flags & RXf_PMf_KEEPCOPY) /* /p */ mods[n++] = 'p'; mods[n] = '\0'; EXTEND(SP, 2 * n); for (i = 0; i < n; ++i) { mPUSHp(mods + i, 1); PUSHs(&PL_sv_yes); } XSRETURN(2 * n); void stash(re::engine::Plugin self, ...) PPCODE: if (items > 1) { SvREFCNT_dec(self->stash); self->stash = ST(1); SvREFCNT_inc_simple_void(self->stash); XSRETURN_EMPTY; } else { XPUSHs(self->stash); XSRETURN(1); } void minlen(re::engine::Plugin self, ...) PPCODE: if (items > 1) { self->rx->minlen = (I32)SvIV(ST(1)); XSRETURN_EMPTY; } else { if (self->rx->minlen) { XPUSHs(sv_2mortal(newSViv(self->rx->minlen))); } else { XPUSHs(sv_2mortal(&PL_sv_undef)); } XSRETURN(1); } void gofs(re::engine::Plugin self, ...) PPCODE: if (items > 1) { self->rx->gofs = (U32)SvIV(ST(1)); XSRETURN_EMPTY; } else { if (self->rx->gofs) { XPUSHs(sv_2mortal(newSVuv(self->rx->gofs))); } else { XPUSHs(sv_2mortal(&PL_sv_undef)); } XSRETURN(1); } void nparens(re::engine::Plugin self, ...) PPCODE: if (items > 1) { self->rx->nparens = (U32)SvIV(ST(1)); XSRETURN_EMPTY; } else { if (self->rx->nparens) { XPUSHs(sv_2mortal(newSVuv(self->rx->nparens))); } else { XPUSHs(sv_2mortal(&PL_sv_undef)); } XSRETURN(1); } void _exec(re::engine::Plugin self, ...) PPCODE: if (items > 1) { SvREFCNT_dec(self->cb_exec); self->cb_exec = ST(1); SvREFCNT_inc_simple_void(self->cb_exec); } XSRETURN(0); void _free(re::engine::Plugin self, ...) PPCODE: if (items > 1) { SvREFCNT_dec(self->cb_free); self->cb_free = ST(1); SvREFCNT_inc_simple_void(self->cb_free); } XSRETURN(0); void _num_capture_buff_FETCH(re::engine::Plugin self, ...) PPCODE: if (items > 1) { SvREFCNT_dec(self->cb_num_capture_buff_FETCH); self->cb_num_capture_buff_FETCH = ST(1); SvREFCNT_inc_simple_void(self->cb_num_capture_buff_FETCH); } XSRETURN(0); void _num_capture_buff_STORE(re::engine::Plugin self, ...) PPCODE: if (items > 1) { SvREFCNT_dec(self->cb_num_capture_buff_STORE); self->cb_num_capture_buff_STORE = ST(1); SvREFCNT_inc_simple_void(self->cb_num_capture_buff_STORE); } XSRETURN(0); void _num_capture_buff_LENGTH(re::engine::Plugin self, ...) PPCODE: if (items > 1) { SvREFCNT_dec(self->cb_num_capture_buff_LENGTH); self->cb_num_capture_buff_LENGTH = ST(1); SvREFCNT_inc_simple_void(self->cb_num_capture_buff_LENGTH); } XSRETURN(0); SV * _tag(SV *comp, SV *exec, SV *free) PREINIT: xsh_hints_user_t arg; CODE: arg.comp = comp; arg.exec = exec; arg.free = free; RETVAL = xsh_hints_tag(&arg); OUTPUT: RETVAL void ENGINE() PPCODE: XPUSHs(sv_2mortal(newSViv(PTR2IV(&engine_plugin)))); XSRETURN(1);