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 )