Data-Reservoir-Shared
view release on metacpan or search on metacpan
croak("Data::Reservoir::Shared: weight must be a finite number > 0");
}
s = SvPVbyte(item, n); /* may croak (wide char) -- BEFORE the lock, AFTER weight magic */
/* SvNV(weight) above and SvPVbyte(item) just now both run magic = arbitrary
* Perl that can have destroyed self -- re-check before the first use of h. */
REEXTRACT(self);
rsv_rwlock_wrlock(h);
RETVAL = (h->mode == RSV_MODE_WEIGHTED)
? rsv_add_weighted_locked(h, s, (uint64_t)n, w)
: rsv_add_locked(h, s, (uint64_t)n);
__atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
rsv_rwlock_wrunlock(h);
OUTPUT:
RETVAL
UV
add_many(self, items)
SV *self
SV *items
PREINIT:
EXTRACT(self);
AV *av;
IV top;
UV stored = 0;
CODE:
SvGETMAGIC(items);
if (!SvROK(items) || SvTYPE(SvRV(items)) != SVt_PVAV)
croak("Data::Reservoir::Shared->add_many: expected an array reference");
av = (AV *)SvRV(items);
/* Pin the array for the whole loop: an element's magic can drop the caller's
* last reference (undef $aref), freeing av and dangling later av_fetch calls. */
SvREFCNT_inc((SV *)av); sv_2mortal((SV *)av);
top = av_len(av);
/* SvGETMAGIC(items) above, and av_len on a tied array, both run Perl that
* can have destroyed self -- re-check before the first use of h below. */
REEXTRACT(self);
{
STRLEN cnt = (top >= 0) ? (STRLEN)(top + 1) : 0, i;
const char **ps = NULL; STRLEN *ls = NULL; double *ws = NULL;
int weighted = (h->mode == RSV_MODE_WEIGHTED);
if (cnt) {
Newx(ps, cnt, const char *); SAVEFREEPV(ps);
Newx(ls, cnt, STRLEN); SAVEFREEPV(ls);
if (weighted) { Newx(ws, cnt, double); SAVEFREEPV(ws); }
for (i = 0; i < cnt; i++) {
SV **el = av_fetch(av, (SSize_t)i, 0);
if (weighted) {
/* weighted mode: each element is a [item, weight] pair,
* validated here (before the lock; no croak while locked) */
if (el && *el) SvGETMAGIC(*el); /* a tied-array element is a deferred-magic PVLV */
if (!el || !*el || !SvROK(*el) || SvTYPE(SvRV(*el)) != SVt_PVAV)
croak("Data::Reservoir::Shared->add_many: weighted reservoir expects [item, weight] pairs");
AV *pair = (AV *)SvRV(*el);
SV **iv = av_fetch(pair, 0, 0);
SV **wv = av_fetch(pair, 1, 0);
if (!iv || !*iv || !wv || !*wv)
croak("Data::Reservoir::Shared->add_many: each element must be [item, weight]");
/* Pin both element SVs by value BEFORE any magic runs: the item's
* SvPVbyte (or the weight's get-magic) can free or reassign the
* pair AV, dangling the other's raw SV** slot. The mortal INC
* keeps both alive for this branch even if the pair AV is freed. */
SV *isv = *iv, *wsv = *wv;
SvREFCNT_inc(isv); sv_2mortal(isv);
SvREFCNT_inc(wsv); sv_2mortal(wsv);
{
STRLEN len;
const char *src = SvPVbyte(isv, len); /* may run overload/tie/get-magic = arbitrary Perl */
/* Copy bytes into a private mortal SV NOW: a LATER element SvPVbyte can
* grow/free THIS element PV, dangling src before the locked loop uses it. */
SV *copy = sv_2mortal(newSVpvn(src, len));
ps[i] = SvPVX_const(copy);
ls[i] = len;
}
SvGETMAGIC(wsv);
{ double w = SvOK(wsv) ? SvNV(wsv) : 0.0; /* undef -> 0 -> clean croak below, no warning */
if (!(w > 0.0) || !isfinite(w))
croak("Data::Reservoir::Shared->add_many: weight must be a finite number > 0");
ws[i] = w; }
} else if (el && *el) {
STRLEN len;
const char *src = SvPVbyte(*el, len); /* may run overload/tie/get-magic = arbitrary Perl */
/* Copy bytes into a private mortal SV NOW: a LATER element SvPVbyte can
* grow/free THIS element PV, dangling src before the locked loop uses it. */
SV *copy = sv_2mortal(newSVpvn(src, len));
ps[i] = SvPVX_const(copy);
ls[i] = len;
} else { ps[i] = ""; ls[i] = 0; }
}
}
/* Element get-magic in the resolve loop above (SvPVbyte / SvGETMAGIC on
* the weight) can also have destroyed self. Outside the `if (cnt)`
* block: an empty or tied size-0 array skips the loop but still
* reaches this lock. */
REEXTRACT(self);
rsv_rwlock_wrlock(h);
if (weighted)
for (i = 0; i < cnt; i++) stored += (UV)rsv_add_weighted_locked(h, ps[i], (uint64_t)ls[i], ws[i]);
else
for (i = 0; i < cnt; i++) stored += (UV)rsv_add_locked(h, ps[i], (uint64_t)ls[i]);
__atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
rsv_rwlock_wrunlock(h);
}
RETVAL = stored;
OUTPUT:
RETVAL
# ---- reading the sample ----
SV *
get(self, i)
SV *self
UV i
PREINIT:
EXTRACT(self);
const uint8_t *ptr; uint64_t len; int ok;
uint8_t *tmp = NULL;
CODE:
/* copy the item out under the read lock, then build the SV (no croak-capable
* Perl allocation while the lock is held) */
Newx(tmp, (size_t)h->item_size, uint8_t); SAVEFREEPV(tmp); /* cached: matches rsv_get_locked's clamp */
rsv_rwlock_rdlock(h);
( run in 4.095 seconds using v1.01-cache-2.11-cpan-14f38c9f855 )