Data-MinHash-Shared
view release on metacpan or search on metacpan
{ SV *ca = sv_2mortal(newSVsv(sig_a)); a = (const unsigned char *)SvPVbyte(ca, la); }
bp = (const unsigned char *)SvPVbyte(sig_b, lb);
{
STRLEN need = (STRLEN)(((uint64_t)k * (uint64_t)b + 7) / 8);
if (la < need || lb < need)
croak("Data::MinHash::Shared->bbit_similarity_of: signature too short for k=%llu b=%llu (need %llu bytes)",
(unsigned long long)k, (unsigned long long)b, (unsigned long long)need);
uint64_t agree = 0;
for (uint64_t i = 0; i < (uint64_t)k; i++)
if (mnh_bbit_get(a, i, (uint32_t)b) == mnh_bbit_get(bp, i, (uint32_t)b)) agree++;
double f = (double)agree / (double)k;
RETVAL = mnh_bbit_correct(f, (uint32_t)b);
}
OUTPUT:
RETVAL
void
merge(self, other)
SV *self
SV *other
PREINIT:
EXTRACT(self);
CODE:
if (!sv_isobject(other) || !sv_derived_from(other, "Data::MinHash::Shared"))
croak("Data::MinHash::Shared->merge: expected a Data::MinHash::Shared object");
MnhHandle *o = INT2PTR(MnhHandle*, SvIV(SvRV(other)));
if (!o) croak("Attempted to use a destroyed Data::MinHash::Shared object");
REEXTRACT(self);
/* k is immutable after creation -- compare lock-free, croak BEFORE allocating
* so a mismatch holds no lock and leaks no buffer. */
uint64_t ok = o->hdr->k;
if (ok != h->hdr->k)
croak("Data::MinHash::Shared->merge: register-count mismatch (k=%llu vs k=%llu)",
(unsigned long long)h->hdr->k, (unsigned long long)ok);
/* Snapshot the other's registers under its read lock into a temp buffer, then
* release before taking self's write lock. Copying to a temp avoids holding
* two locks at once (deadlock-free regardless of acquisition order between
* two processes merging each other). */
uint64_t k = ok;
uint64_t o_max = mnh_reg_max(o); /* Layer B: never read past o's real mapping */
if (k > o_max) k = o_max;
uint64_t *tmp;
Newx(tmp, (size_t)(k ? k : 1), uint64_t);
SAVEFREEPV(tmp); /* freed on normal return OR croak unwind */
mnh_rwlock_rdlock(o);
memcpy(tmp, mnh_registers(o), (size_t)k * sizeof(uint64_t));
mnh_rwlock_rdunlock(o);
mnh_rwlock_wrlock(h);
mnh_merge_locked(h, tmp, k);
__atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
mnh_rwlock_wrunlock(h);
void
registers(self)
SV *self
PREINIT:
EXTRACT(self);
PPCODE:
{
uint64_t k = h->hdr->k;
uint64_t kmax = mnh_reg_max(h); /* Layer B: never read past the real mapping */
if (k > kmax) k = kmax;
uint64_t *snap = NULL;
if (k) { Newx(snap, (size_t)k, uint64_t); SAVEFREEPV(snap); } /* alloc BEFORE the lock */
mnh_rwlock_rdlock(h);
if (k) memcpy(snap, mnh_registers(h), (size_t)k * sizeof(uint64_t));
mnh_rwlock_rdunlock(h);
EXTEND(SP, (SSize_t)k);
for (uint64_t j = 0; j < k; j++)
PUSHs(sv_2mortal(newSVuv((UV)snap[j]))); /* Perl alloc AFTER unlock */
}
UV
filled(self)
SV *self
PREINIT:
EXTRACT(self);
UV n;
CODE:
mnh_rwlock_rdlock(h);
n = (UV)mnh_filled_locked(h);
mnh_rwlock_rdunlock(h);
RETVAL = n;
OUTPUT:
RETVAL
void
clear(self)
SV *self
PREINIT:
EXTRACT(self);
CODE:
mnh_rwlock_wrlock(h);
mnh_clear_locked(h);
__atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
mnh_rwlock_wrunlock(h);
UV
size(self)
SV *self
PREINIT:
EXTRACT(self);
CODE:
RETVAL = (UV)h->hdr->k;
OUTPUT:
RETVAL
SV *
stats(self)
SV *self
PREINIT:
EXTRACT(self);
CODE:
{
uint64_t k, filled, ops;
/* Snapshot under the lock; do all (croak-capable) Perl allocation after
releasing it -- so an OOM in newHV/newSVuv can never strand the lock. */
mnh_rwlock_rdlock(h);
( run in 1.679 second using v1.01-cache-2.11-cpan-92ad3014f07 )