Data-SortedSet-Shared
view release on metacpan or search on metacpan
if (nr > 0) {
Newx(mem, nr, int64_t); SAVEFREEPV(mem); /* freed on return OR a longjmp from SvNV/SvIV magic */
Newx(sco, nr, double); SAVEFREEPV(sco);
for (SSize_t i = 0; i < nr; i++) {
SV **rv = av_fetch(av, i, 0);
if (!rv) continue;
SV *rsv = *rv;
SvREFCNT_inc(rsv); sv_2mortal(rsv); /* FETCH magic can drop the element from av */
SvGETMAGIC(rsv); /* a tied-array element is a deferred-magic PVLV */
if (!SvROK(rsv) || SvTYPE(SvRV(rsv)) != SVt_PVAV) continue; /* skip malformed */
AV *row = (AV *)SvRV(rsv);
SvREFCNT_inc((SV *)row); sv_2mortal((SV *)row); /* element magic below can free the row AV */
if (av_len(row) + 1 < 2) continue;
SV **ms = av_fetch(row, 0, 0), **sv = av_fetch(row, 1, 0);
if (!ms || !sv) continue;
/* Pin both element SVs by value BEFORE any magic runs: the
* score's SvNV can free or reassign the row AV, dangling the
* other's raw SV** slot. */
SV *memsv = *ms, *scosv = *sv;
SvREFCNT_inc(memsv); sv_2mortal(memsv);
SvREFCNT_inc(scosv); sv_2mortal(scosv);
double score = SvNV(scosv);
if (score != score) continue; /* skip NaN */
mem[n] = (int64_t)SvIV(memsv);
sco[n] = score;
n++;
}
}
REEXTRACT(self);
ss_rwlock_wrlock(h);
if (h->hdr->sealed) { ss_rwlock_wrunlock(h); croak("Data::SortedSet::Shared->add_many: sorted set is frozen (read-only)"); }
for (SSize_t i = 0; i < n; i++) {
int rc = ss_add_locked(h, mem[i], sco[i]);
if (rc == 1) added++;
else if (rc == -1) break; /* pool full */
}
__atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
ss_rwlock_wrunlock(h);
/* mem/sco freed by SAVEFREEPV at scope exit (croak-safe) */
}
RETVAL = added;
OUTPUT:
RETVAL
SV *
stats(self)
SV *self
PREINIT:
EXTRACT(self);
CODE:
{
uint32_t count, max_entries, height, node_capacity, index_slots, nodes_used;
uint64_t ops;
/* Snapshot the header fields under the lock; do all (croak-capable) Perl
allocation after releasing it -- an OOM in newHV/newSVuv must never
strand the read lock. */
if (!h->readonly) ss_rwlock_rdlock(h); /* frozen: immutable, no lock */
SsHeader *hd = h->hdr;
uint32_t nfree = 0, f = hd->node_free_head;
/* node_free_head / parent free-links are file-stored: bound the index and
cap iterations so a crafted out-of-range or cyclic free-list can't OOB
or spin (never taken for a valid free-list of length <= node_capacity) */
while (f != SS_NONE && ss_node_ok(h, f) && nfree <= h->node_capacity) { nfree++; f = h->nodes[f].parent; } /* cached cap: trusted anti-spin bound */
count = hd->count; /* backward-shift delete leaves no tombstones: occupied slots == count */
max_entries = hd->max_entries;
height = hd->height;
node_capacity = hd->node_capacity;
index_slots = hd->index_slots;
nodes_used = hd->node_capacity - nfree;
ops = hd->stat_ops;
if (!h->readonly) ss_rwlock_rdunlock(h);
HV *hv = newHV();
hv_stores(hv, "count", newSVuv(count));
hv_stores(hv, "max_entries", newSVuv(max_entries));
hv_stores(hv, "height", newSVuv(height));
hv_stores(hv, "node_capacity", newSVuv(node_capacity));
hv_stores(hv, "nodes_used", newSVuv(nodes_used));
hv_stores(hv, "index_slots", newSVuv(index_slots));
hv_stores(hv, "index_load", newSVnv((double)count / (double)index_slots));
hv_stores(hv, "ops", newSVuv(ops));
hv_stores(hv, "mmap_size", newSVuv((UV)h->mmap_size));
hv_stores(hv, "frozen", newSVuv(h->hdr->sealed ? 1 : 0));
hv_stores(hv, "readonly", newSVuv(h->readonly ? 1 : 0));
RETVAL = newRV_noinc((SV *)hv);
}
OUTPUT:
RETVAL
( run in 1.194 second using v1.01-cache-2.11-cpan-4ab04211f4c )