+/* --- Private API --------------------------------------------------------- */
+
+typedef struct {
+ size_t count;
+ const reh_config *cbs;
+ U32 refcount;
+} reh_private;
+
+STATIC void reh_private_free(pTHX_ reh_private *priv) {
+#define reh_private_free(P) reh_private_free(aTHX_ (P))
+ if (priv->refcount <= 1) {
+ PerlMemShared_free((void *) priv->cbs);
+ PerlMemShared_free(priv);
+ } else {
+ --priv->refcount;
+ }
+}
+
+#define PTABLE_NAME ptable_private
+#define PTABLE_VAL_FREE(V) reh_private_free(V)
+
+#define pPTBL pTHX
+#define pPTBL_ pTHX_
+#define aPTBL aTHX
+#define aPTBL_ aTHX_
+
+#include "ptable.h"
+
+#define ptable_private_store(T, K, V) ptable_private_store(aTHX_ (T), (K), (V))
+#define ptable_private_delete(T, K) ptable_private_delete(aTHX_ (T), (K))
+#define ptable_private_clear(T) ptable_private_clear(aTHX_ (T))
+#define ptable_private_free(T) ptable_private_free(aTHX_ (T))
+
+STATIC ptable *reh_private_map;
+
+#ifdef USE_ITHREADS
+
+STATIC perl_mutex reh_private_map_mutex;
+
+#endif /* USE_ITHREADS */
+
+#define REH_PRIVATE_MAP_FOREACH(C) STMT_START { \
+ reh_private *priv; \
+ REH_LOCK(&reh_private_map_mutex); \
+ priv = ptable_fetch(reh_private_map, rx->pprivate); \
+ if (priv) { \
+ const reh_config *cbs = priv->cbs; \
+ if (cbs) { \
+ const reh_config *end = cbs + priv->count; \
+ for (; cbs < end; ++cbs) { \
+ (C); \
+ } \
+ } \
+ } \
+ REH_UNLOCK(&reh_private_map_mutex); \
+} STMT_END
+
+STATIC reh_private *reh_private_map_store(pTHX_ void *ri, reh_private *priv) {
+#define reh_private_map_store(R, P) reh_private_map_store(aTHX_ (R), (P))
+ REH_LOCK(&reh_private_map_mutex);
+ ptable_private_store(reh_private_map, ri, priv);
+ REH_UNLOCK(&reh_private_map_mutex);
+
+ return;
+}
+
+STATIC void reh_private_map_copy(pTHX_ void *ri_from, void *ri_to) {
+#define reh_private_map_copy(F, T) reh_private_map_copy(aTHX_ (F), (T))
+ reh_private *priv;
+
+ REH_LOCK(&reh_private_map_mutex);
+ priv = ptable_fetch(reh_private_map, ri_from);
+ if (priv) {
+ ++priv->refcount;
+ ptable_private_store(reh_private_map, ri_to, priv);
+ }
+ REH_UNLOCK(&reh_private_map_mutex);
+}
+
+STATIC void reh_private_map_delete(pTHX_ void *ri) {
+#define reh_private_map_delete(R) reh_private_map_delete(aTHX_ (R))
+ REH_LOCK(&reh_private_map_mutex);
+ ptable_private_delete(reh_private_map, ri);
+ REH_UNLOCK(&reh_private_map_mutex);
+
+ return;
+}
+
+void reh_call_comp_begin_hook(pTHX_ regexp *rx) {
+ SV *hint = reh_hint();
+
+ if (hint && SvPOK(hint)) {
+ STRLEN len;
+ const char *keys = SvPV_const(hint, len);
+ size_t count = 0;
+
+ reh_private *priv;
+ reh_config *cbs = NULL;
+ reh_action *a, *root;
+
+ REH_LOCK(&reh_action_list_mutex);
+ root = reh_action_list;
+ REH_UNLOCK(&reh_action_list_mutex);
+
+ for (a = root; a; a = a->next) {
+ char *p = strstr(keys, a->key);
+
+ if (p && (p + a->klen <= keys + len) && p[a->klen] == ' ')
+ ++count;
+ }
+
+ if (count) {
+ size_t i = 0;
+
+ cbs = PerlMemShared_malloc(count * sizeof *cbs);
+
+ for (a = root; a; a = a->next) {
+ char *p = strstr(keys, a->key);
+
+ if (p && (p + a->klen <= keys + len) && p[a->klen] == ' ')
+ cbs[i++] = a->cbs;
+ }
+ }
+
+ priv = PerlMemShared_malloc(sizeof *priv);
+ priv->count = count;
+ priv->cbs = cbs;
+ priv->refcount = 1;
+
+ rx->engine = &reh_regexp_engine;
+
+ reh_private_map_store(rx->pprivate, priv);
+ }
+}
+
+void reh_call_comp_node_hook(pTHX_ regexp *rx, regnode *node) {
+ REH_PRIVATE_MAP_FOREACH(cbs->comp_node(aTHX_ rx, node));
+}
+
+void reh_call_exec_node_hook(pTHX_ regexp *rx, regnode *node, regmatch_info *reginfo, regmatch_state *st) {
+ REH_PRIVATE_MAP_FOREACH(cbs->exec_node(aTHX_ rx, node, reginfo, st));
+}
+
+void reh_re_free(pTHX_ REGEXP * const RX) {
+ regexp *rx = rxREGEXP(RX);
+
+ reh_private_map_delete(rx->pprivate);
+
+ reh_regfree(aTHX_ RX);
+}
+
+#ifdef USE_ITHREADS
+
+void *reh_re_dupe(pTHX_ REGEXP * const RX, CLONE_PARAMS *param) {
+ regexp *rx = rxREGEXP(RX);
+ void *new_ri;
+
+ new_ri = reh_regdupe(aTHX_ RX, param);
+
+ reh_private_map_copy(rx->pprivate, new_ri);
+
+ return new_ri;
+}
+
+#endif
+
+STATIC void reh_teardown(pTHX_ void *root) {
+#if REH_MULTIPLICITY
+ if (aTHX != root)
+ return;
+#endif
+
+ ptable_private_free(reh_private_map);
+}
+