Data-MinHash-Shared

 view release on metacpan or  search on metacpan

Shared.xs  view on Meta::CPAN

    { 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 )