Data-DisjointSet-Shared
view release on metacpan or search on metacpan
MAKE_OBJ(class, h);
OUTPUT:
RETVAL
SV *
new_from_fd(class, fd)
const char *class
int fd
PREINIT:
char errbuf[DSU_ERR_BUFLEN];
CODE:
DsuHandle *h = dsu_open_fd(fd, errbuf);
if (!h) croak("Data::DisjointSet::Shared->new_from_fd: %s", errbuf);
MAKE_OBJ(class, h);
OUTPUT:
RETVAL
void
DESTROY(self)
SV *self
CODE:
if (sv_isobject(self) && sv_derived_from(self, "Data::DisjointSet::Shared")) {
DsuHandle *h = INT2PTR(DsuHandle*, SvIV(SvRV(self)));
if (h) { sv_setiv(SvRV(self), 0); dsu_destroy(h); } /* null first: activates EXTRACT's use-after-destroy croak + makes a double DESTROY a no-op */
}
UV
find(self, x)
SV *self
UV x
PREINIT:
EXTRACT(self);
CODE:
/* Range-check BEFORE locking so a croak holds no lock. */
if (x >= h->hdr->n)
croak("Data::DisjointSet::Shared->find: index %" UVuf " out of range (n=%u)",
x, h->hdr->n);
/* find performs path compression -> it MUTATES -> take the write lock. */
dsu_rwlock_wrlock(h);
RETVAL = (UV)dsu_find(h, (uint32_t)x);
__atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
dsu_rwlock_wrunlock(h);
OUTPUT:
RETVAL
IV
union(self, a, b)
SV *self
UV a
UV b
PREINIT:
EXTRACT(self);
CODE:
if (a >= h->hdr->n)
croak("Data::DisjointSet::Shared->union: index %" UVuf " out of range (n=%u)",
a, h->hdr->n);
if (b >= h->hdr->n)
croak("Data::DisjointSet::Shared->union: index %" UVuf " out of range (n=%u)",
b, h->hdr->n);
dsu_rwlock_wrlock(h);
RETVAL = (IV)dsu_union_locked(h, (uint32_t)a, (uint32_t)b); /* 1 = newly merged, 0 = already together */
__atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
dsu_rwlock_wrunlock(h);
OUTPUT:
RETVAL
bool
connected(self, a, b)
SV *self
UV a
UV b
PREINIT:
EXTRACT(self);
CODE:
if (a >= h->hdr->n)
croak("Data::DisjointSet::Shared->connected: index %" UVuf " out of range (n=%u)",
a, h->hdr->n);
if (b >= h->hdr->n)
croak("Data::DisjointSet::Shared->connected: index %" UVuf " out of range (n=%u)",
b, h->hdr->n);
/* connected compresses paths via dsu_find -> it MUTATES -> write lock. */
dsu_rwlock_wrlock(h);
RETVAL = dsu_connected_locked(h, (uint32_t)a, (uint32_t)b);
__atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
dsu_rwlock_wrunlock(h);
OUTPUT:
RETVAL
UV
union_many(self, pairs)
SV *self
SV *pairs
PREINIT:
EXTRACT(self);
AV *av;
IV top;
CODE:
if (!SvROK(pairs) || SvTYPE(SvRV(pairs)) != SVt_PVAV)
croak("Data::DisjointSet::Shared->union_many: expected an array reference");
av = (AV *)SvRV(pairs);
top = av_len(av); /* last index, -1 if empty */
{
STRLEN cnt = (top >= 0) ? (STRLEN)(top + 1) : 0, i;
STRLEN npairs;
uint32_t *vals = NULL;
UV merged = 0;
uint32_t n = h->hdr->n;
if (cnt & 1)
croak("Data::DisjointSet::Shared->union_many: expected an even number of elements (flat [a0,b0,a1,b1,...]), got %" UVuf,
(UV)cnt);
npairs = cnt / 2;
if (cnt) { /* resolve + range-check ALL before locking */
Newx(vals, cnt, uint32_t); SAVEFREEPV(vals); /* freed on return OR unwind */
for (i = 0; i < cnt; i++) { /* a croak here holds NO lock; SAVEFREEPV cleans up */
SV **el = av_fetch(av, (SSize_t)i, 0);
UV v = (el && *el) ? SvUV(*el) : 0;
if (v >= n)
croak("Data::DisjointSet::Shared->union_many: index %" UVuf " out of range (n=%u)",
v, n);
vals[i] = (uint32_t)v;
}
}
dsu_rwlock_wrlock(h); /* locked region: NO croak-capable calls */
for (i = 0; i < npairs; i++)
merged += (UV)dsu_union_locked(h, vals[2*i], vals[2*i + 1]);
__atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED); /* a call always counts, even an empty batch */
dsu_rwlock_wrunlock(h);
RETVAL = merged;
}
OUTPUT:
RETVAL
UV
set_size(self, x)
SV *self
UV x
PREINIT:
EXTRACT(self);
CODE:
if (x >= h->hdr->n)
croak("Data::DisjointSet::Shared->set_size: index %" UVuf " out of range (n=%u)",
x, h->hdr->n);
/* set_size compresses paths via dsu_find -> it MUTATES -> write lock. */
dsu_rwlock_wrlock(h);
RETVAL = (UV)dsu_set_size_locked(h, (uint32_t)x);
__atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
dsu_rwlock_wrunlock(h);
OUTPUT:
RETVAL
UV
num_sets(self)
SV *self
PREINIT:
EXTRACT(self);
CODE:
dsu_rwlock_rdlock(h);
RETVAL = (UV)h->hdr->num_sets;
dsu_rwlock_rdunlock(h);
OUTPUT:
RETVAL
UV
capacity(self)
SV *self
PREINIT:
EXTRACT(self);
CODE:
RETVAL = (UV)h->hdr->n; /* immutable after creation -- lock-free */
OUTPUT:
RETVAL
void
reset(self)
SV *self
PREINIT:
EXTRACT(self);
CODE:
dsu_rwlock_wrlock(h);
dsu_reset_locked(h);
__atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
dsu_rwlock_wrunlock(h);
SV *
stats(self)
SV *self
PREINIT:
EXTRACT(self);
CODE:
{
uint32_t n, num_sets;
uint64_t ops;
/* Snapshot under the lock; do all (croak-capable) Perl allocation after
releasing it -- so an OOM in newHV/newSV* can never strand the lock. */
dsu_rwlock_rdlock(h);
n = h->hdr->n;
num_sets = h->hdr->num_sets;
ops = h->hdr->stat_ops;
dsu_rwlock_rdunlock(h);
HV *hv = newHV();
hv_stores(hv, "capacity", newSVuv((UV)n));
hv_stores(hv, "sets", newSVuv((UV)num_sets));
hv_stores(hv, "ops", newSVuv((UV)ops));
hv_stores(hv, "mmap_size", newSVuv((UV)h->mmap_size));
RETVAL = newRV_noinc((SV *)hv);
}
OUTPUT:
RETVAL
SV *
path(self)
SV *self
PREINIT:
EXTRACT(self);
CODE:
RETVAL = h->path ? newSVpv(h->path, 0) : &PL_sv_undef;
OUTPUT:
RETVAL
int
memfd(self)
SV *self
PREINIT:
EXTRACT(self);
CODE:
RETVAL = h->backing_fd;
OUTPUT:
RETVAL
void
sync(self)
SV *self
PREINIT:
EXTRACT(self);
CODE:
if (dsu_msync(h) != 0) croak("sync: %s", strerror(errno));
void
unlink(self, ...)
( run in 0.433 second using v1.01-cache-2.11-cpan-bbe5e583499 )