Ancient

 view release on metacpan or  search on metacpan

xs/slot/slot.c  view on Meta::CPAN

static XOP slot_unwatch_one_xop;
static XOP slot_clear_xop;

/* Simple SV* registry - just store pointers, manage refcounts */
static SV **g_slots = NULL;
static IV g_slots_size = 0;
static IV g_slots_count = 0;
static char *g_has_watchers = NULL;  /* Quick flag per slot */

/* Mappings */
static HV *g_slot_index = NULL;
static HV *g_slot_names = NULL;
static HV *g_watchers = NULL;

/* Forward declaration */
static void fire_watchers(pTHX_ IV idx, SV *new_val);

/* ============================================
   Custom OP implementations - fastest path
   ============================================ */

static OP* pp_slot_get(pTHX) {
    dSP;
    IV idx = PL_op->op_targ;
#ifdef DEBUGGING
    EXTEND(SP, 1);  /* Ensure stack space - only needed for DEBUGGING builds */
#endif
    PUSHs(g_slots[idx]);
    RETURN;
}

static OP* pp_slot_set(pTHX) {
    dSP;
    IV idx = PL_op->op_targ;
    SV **slot = &g_slots[idx];
    SV *old = *slot;
    SV *new_val = TOPs;  /* Don't pop - just peek and replace */

    *slot = SvREFCNT_inc_simple_NN(new_val);
    SvREFCNT_dec_NN(old);

    if (g_has_watchers[idx]) fire_watchers(aTHX_ idx, new_val);

    /* Value already on stack as result */
    RETURN;
}

/* pp_slot_watch - register watcher, idx in op_targ, callback on stack */
static OP* pp_slot_watch(pTHX) {
    dSP;
    IV idx = PL_op->op_targ;
    SV *callback = POPs;
    char key[32];
    int klen = snprintf(key, sizeof(key), "%ld", (long)idx);
    SV **name_svp = hv_fetch(g_slot_names, key, klen, 0);
    
    if (name_svp) {
        STRLEN name_len;
        const char *name = SvPV(*name_svp, name_len);
        SV **existing = hv_fetch(g_watchers, name, name_len, 0);
        AV *callbacks;
        
        if (existing && SvROK(*existing)) {
            callbacks = (AV*)SvRV(*existing);
        } else {
            callbacks = newAV();
            hv_store(g_watchers, name, name_len, newRV_noinc((SV*)callbacks), 0);
        }
        av_push(callbacks, SvREFCNT_inc(callback));
        g_has_watchers[idx] = 1;
    }
    RETURN;
}

/* pp_slot_unwatch - unregister all watchers, idx in op_targ */
static OP* pp_slot_unwatch(pTHX) {
    IV idx = PL_op->op_targ;
    char key[32];
    int klen = snprintf(key, sizeof(key), "%ld", (long)idx);
    SV **name_svp = hv_fetch(g_slot_names, key, klen, 0);
    
    if (name_svp) {
        STRLEN name_len;
        const char *name = SvPV(*name_svp, name_len);
        hv_delete(g_watchers, name, name_len, G_DISCARD);
        g_has_watchers[idx] = 0;
    }
    return NORMAL;
}

/* pp_slot_unwatch_one - unregister specific watcher, idx in op_targ, callback on stack */
static OP* pp_slot_unwatch_one(pTHX) {
    dSP;
    IV idx = PL_op->op_targ;
    SV *callback = POPs;
    char key[32];
    int klen = snprintf(key, sizeof(key), "%ld", (long)idx);
    SV **name_svp = hv_fetch(g_slot_names, key, klen, 0);
    
    if (name_svp) {
        STRLEN name_len;
        const char *name = SvPV(*name_svp, name_len);
        SV **existing = hv_fetch(g_watchers, name, name_len, 0);
        
        if (existing && SvROK(*existing)) {
            AV *callbacks = (AV*)SvRV(*existing);
            SSize_t i, len = av_len(callbacks);
            for (i = len; i >= 0; i--) {
                SV **cb = av_fetch(callbacks, i, 0);
                if (cb && SvRV(*cb) == SvRV(callback)) {
                    av_delete(callbacks, i, G_DISCARD);
                }
            }
            if (av_len(callbacks) < 0) {
                g_has_watchers[idx] = 0;
            }
        }
    }
    RETURN;
}

/* pp_slot_clear - reset slot to undef and clear watchers, idx in op_targ */
static OP* pp_slot_clear(pTHX) {
    IV idx = PL_op->op_targ;
    SV *old = g_slots[idx];
    char key[32];
    int klen = snprintf(key, sizeof(key), "%ld", (long)idx);
    SV **name_svp = hv_fetch(g_slot_names, key, klen, 0);
    
    g_slots[idx] = &PL_sv_undef;
    SvREFCNT_dec(old);
    
    if (name_svp) {
        STRLEN name_len;
        const char *name = SvPV(*name_svp, name_len);
        hv_delete(g_watchers, name, name_len, G_DISCARD);
    }
    g_has_watchers[idx] = 0;
    
    return NORMAL;
}

/* Call checker - replaces calls with custom ops */
static OP* slot_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) {
    CV *cv = (CV*)ckobj;
    IV idx = CvXSUBANY(cv).any_iv;
    OP *pushop, *cvop, *argop;

    PERL_UNUSED_ARG(namegv);

    /* Get the argument list */
    pushop = cUNOPx(entersubop)->op_first;
    if (!OpHAS_SIBLING(pushop)) {
        pushop = cUNOPx(pushop)->op_first;
    }

    /* Find first real arg (skip pushmark) */
    argop = OpSIBLING(pushop);

    /* Find the cv op (last sibling) */
    cvop = argop;
    while (OpHAS_SIBLING(cvop)) {
        cvop = OpSIBLING(cvop);
    }

    if (argop == cvop) {
        /* No args - getter */
        OP *newop = newOP(OP_CUSTOM, 0);
        newop->op_ppaddr = pp_slot_get;
        newop->op_targ = idx;
        op_free(entersubop);
        return newop;
    } else if (OpSIBLING(argop) == cvop) {
        /* Single arg - setter */

xs/slot/slot.c  view on Meta::CPAN

    }

    /* Check if single constant string argument */
    if (argop != cvop && OpSIBLING(argop) == cvop && argop->op_type == OP_CONST) {
        SV *namesv = cSVOPx(argop)->op_sv;
        if (SvPOK(namesv)) {
            STRLEN name_len;
            const char *name = SvPV(namesv, name_len);
            SV **svp = hv_fetch(g_slot_index, name, name_len, 0);
            if (svp) {
                IV idx = SvIV(*svp);
                OP *newop = newOP(OP_CUSTOM, 0);
                newop->op_ppaddr = pp_slot_clear;
                newop->op_targ = idx;
                op_free(entersubop);
                return newop;
            }
        }
    }
    return entersubop;
}

/* ============================================
   XS Accessor - fallback for dynamic calls
   ============================================ */

static XS(xs_slot_accessor) {
    dXSARGS;
    IV idx = CvXSUBANY(cv).any_iv;
    SV **slot = &g_slots[idx];

    if (items) {
        SV *old = *slot;
        SV *new_val = ST(0);
        *slot = SvREFCNT_inc_simple_NN(new_val);
        SvREFCNT_dec_NN(old);
        if (g_has_watchers[idx]) fire_watchers(aTHX_ idx, new_val);
        ST(0) = new_val;
        XSRETURN(1);
    }
    ST(0) = *slot;
    XSRETURN(1);
}

/* ============================================
   Watchers
   ============================================ */

static void fire_watchers(pTHX_ IV idx, SV *new_val) {
    char key[32];
    int klen = snprintf(key, sizeof(key), "%ld", (long)idx);
    SV **name_sv = hv_fetch(g_slot_names, key, klen, 0);

    if (!name_sv || !SvOK(*name_sv)) return;

    STRLEN name_len;
    const char *name = SvPV(*name_sv, name_len);
    SV **svp = hv_fetch(g_watchers, name, name_len, 0);

    if (svp && SvROK(*svp) && SvTYPE(SvRV(*svp)) == SVt_PVAV) {
        AV *callbacks = (AV*)SvRV(*svp);
        SSize_t i, len = av_len(callbacks);
        for (i = 0; i <= len; i++) {
            SV **cb = av_fetch(callbacks, i, 0);
            if (cb && SvROK(*cb)) {
                dSP;
                ENTER; SAVETMPS;
                PUSHMARK(SP);
                mXPUSHs(newSVpvn(name, name_len));
                XPUSHs(new_val);
                PUTBACK;
                call_sv(*cb, G_DISCARD);
                FREETMPS; LEAVE;
            }
        }
    }
}

/* ============================================
   Slot management
   ============================================ */

static void ensure_slot_capacity(IV needed) {
    if (needed >= g_slots_size) {
        IV new_size = g_slots_size ? g_slots_size * 2 : 16;
        IV i;
        while (new_size <= needed) new_size *= 2;
        Renew(g_slots, new_size, SV*);
        Renew(g_has_watchers, new_size, char);
        for (i = g_slots_size; i < new_size; i++) {
            g_slots[i] = &PL_sv_undef;
            g_has_watchers[i] = 0;
        }
        g_slots_size = new_size;
    }
}

static IV create_slot(pTHX_ const char *name, STRLEN name_len) {
    IV idx = g_slots_count++;
    char key[32];
    int klen;

    ensure_slot_capacity(idx);
    hv_store(g_slot_index, name, name_len, newSViv(idx), 0);
    klen = snprintf(key, sizeof(key), "%ld", (long)idx);
    hv_store(g_slot_names, key, klen, newSVpvn(name, name_len), 0);

    return idx;
}

static char* get_caller(pTHX) {
    return HvNAME((HV*)CopSTASH(PL_curcop));
}

static void install_accessor(pTHX_ const char *pkg, const char *name, IV idx) {
    char full[512];
    CV *cv;

    snprintf(full, sizeof(full), "%s::%s", pkg, name);
    cv = newXS(full, xs_slot_accessor, __FILE__);
    CvXSUBANY(cv).any_iv = idx;

    /* Install call checker for custom op optimization */
    cv_set_call_checker(cv, slot_call_checker, (SV*)cv);
}

static XS(xs_import) {
    dXSARGS;
    char *pkg = get_caller(aTHX);
    int i;

    for (i = 1; i < items; i++) {
        char *name = SvPV_nolen(ST(i));
        STRLEN name_len = SvCUR(ST(i));
        IV idx;
        SV **existing;

        existing = hv_fetch(g_slot_index, name, name_len, 0);
        if (existing) {
            idx = SvIV(*existing);
        } else {
            idx = create_slot(aTHX_ name, name_len);
        }
        install_accessor(aTHX_ pkg, name, idx);
    }
    XSRETURN_EMPTY;
}

/* ============================================
   Watch/unwatch API
   ============================================ */

static XS(xs_watch) {
    dXSARGS;
    if (items < 2) croak("Usage: slot::watch($name, $callback)");

    char *name = SvPV_nolen(ST(0));
    STRLEN name_len = SvCUR(ST(0));
    SV *callback = ST(1);
    SV **existing;
    AV *callbacks;
    SV **idx_sv;

    existing = hv_fetch(g_watchers, name, name_len, 0);
    if (existing && SvROK(*existing)) {
        callbacks = (AV*)SvRV(*existing);
    } else {
        callbacks = newAV();
        hv_store(g_watchers, name, name_len, newRV_noinc((SV*)callbacks), 0);
    }
    av_push(callbacks, SvREFCNT_inc(callback));

    /* Set the has_watchers flag for fast path */
    idx_sv = hv_fetch(g_slot_index, name, name_len, 0);
    if (idx_sv) {
        g_has_watchers[SvIV(*idx_sv)] = 1;
    }

    XSRETURN_EMPTY;
}

static XS(xs_unwatch) {
    dXSARGS;
    if (items < 1) croak("Usage: slot::unwatch($name, [$callback])");

    char *name = SvPV_nolen(ST(0));
    STRLEN name_len = SvCUR(ST(0));
    SV **idx_sv;
    int clear_flag = 0;

    if (items == 1) {
        hv_delete(g_watchers, name, name_len, G_DISCARD);
        clear_flag = 1;
    } else {
        SV *callback = ST(1);
        SV **existing = hv_fetch(g_watchers, name, name_len, 0);
        if (existing && SvROK(*existing)) {
            AV *callbacks = (AV*)SvRV(*existing);
            SSize_t i, len = av_len(callbacks);
            for (i = len; i >= 0; i--) {
                SV **cb = av_fetch(callbacks, i, 0);
                if (cb && SvRV(*cb) == SvRV(callback)) {
                    av_delete(callbacks, i, G_DISCARD);
                }
            }
            /* Check if all watchers removed */
            if (av_len(callbacks) < 0) {
                clear_flag = 1;
            }
        }
    }

    /* Clear the has_watchers flag if no watchers left */
    if (clear_flag) {
        idx_sv = hv_fetch(g_slot_index, name, name_len, 0);
        if (idx_sv) {
            g_has_watchers[SvIV(*idx_sv)] = 0;
        }
    }

    XSRETURN_EMPTY;
}

/* slot::index - get numeric index for a slot (for fast access) */
static XS(xs_index) {
    dXSARGS;
    if (items < 1) XSRETURN_UNDEF;
    STRLEN name_len;
    const char *name = SvPV(ST(0), name_len);
    SV **svp = hv_fetch(g_slot_index, name, name_len, 0);
    if (svp) {
        ST(0) = *svp;
        XSRETURN(1);
    }
    XSRETURN_UNDEF;
}

/* slot::get_by_idx - O(1) direct array access, no hash lookup */
static XS(xs_get_by_idx) {
    dXSARGS;
    if (items < 1) XSRETURN_UNDEF;
    IV idx = SvIV(ST(0));
    if (idx >= 0 && idx < g_slots_count) {
        ST(0) = g_slots[idx];
        XSRETURN(1);
    }
    XSRETURN_UNDEF;
}

/* slot::set_by_idx - O(1) direct array access, no hash lookup */
static XS(xs_set_by_idx) {
    dXSARGS;
    if (items < 2) XSRETURN_EMPTY;
    IV idx = SvIV(ST(0));
    if (idx >= 0 && idx < g_slots_count) {
        SV *old = g_slots[idx];
        g_slots[idx] = SvREFCNT_inc(ST(1));
        SvREFCNT_dec(old);
        if (g_has_watchers[idx]) fire_watchers(aTHX_ idx, g_slots[idx]);
        ST(0) = g_slots[idx];
        XSRETURN(1);
    }
    XSRETURN_EMPTY;
}

static XS(xs_get) {
    dXSARGS;



( run in 2.033 seconds using v1.01-cache-2.11-cpan-364913b4093 )