Data-SortedSet-Shared

 view release on metacpan or  search on metacpan

Shared.xs  view on Meta::CPAN

    char errbuf[SS_ERR_BUFLEN];
  CODE:
    if (max_entries > UINT32_MAX) croak("Data::SortedSet::Shared->new_memfd: max_entries exceeds 2^32");
    SsHandle *h = ss_create_memfd(name, (uint32_t)max_entries, errbuf);
    if (!h) croak("Data::SortedSet::Shared->new_memfd: %s", errbuf);
    MAKE_OBJ(class, h);
  OUTPUT:
    RETVAL

SV *
new_from_fd(class, fd)
    const char *class
    int fd
  PREINIT:
    char errbuf[SS_ERR_BUFLEN];
  CODE:
    SsHandle *h = ss_open_fd(fd, errbuf);
    if (!h) croak("Data::SortedSet::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::SortedSet::Shared")) {
        SsHandle *h = INT2PTR(SsHandle*, SvIV(SvRV(self)));
        if (h) { sv_setiv(SvRV(self), 0); ss_destroy(h); }   /* null first: activates EXTRACT's use-after-destroy croak + makes a double DESTROY a no-op */
    }

UV
count(self)
    SV *self
  PREINIT:
    EXTRACT(self);
  CODE:
    ss_rwlock_rdlock(h);
    RETVAL = h->hdr->count;
    ss_rwlock_rdunlock(h);
  OUTPUT:
    RETVAL

UV
max_entries(self)
    SV *self
  PREINIT:
    EXTRACT(self);
  CODE:
    RETVAL = h->hdr->max_entries;
  OUTPUT:
    RETVAL

void
clear(self)
    SV *self
  PREINIT:
    EXTRACT(self);
  CODE:
    ss_rwlock_wrlock(h);
    ss_clear_locked(h);
    ss_rwlock_wrunlock(h);

SV *
add(self, member, score)
    SV *self
    IV member
    NV score
  PREINIT:
    EXTRACT(self);
    int rc;
  CODE:
    if (score != score) croak("add: score must not be NaN");
    ss_rwlock_wrlock(h);
    rc = ss_add_locked(h, (int64_t)member, (double)score);
    __atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
    ss_rwlock_wrunlock(h);
    RETVAL = (rc < 0) ? &PL_sv_undef : newSViv(rc);
  OUTPUT:
    RETVAL

SV *
score(self, member)
    SV *self
    IV member
  PREINIT:
    EXTRACT(self);
    double sc;
  CODE:
    ss_rwlock_rdlock(h);
    int found = ss_idx_get(h, (int64_t)member, &sc);
    ss_rwlock_rdunlock(h);
    RETVAL = found ? newSVnv(sc) : &PL_sv_undef;
  OUTPUT:
    RETVAL

bool
exists(self, member)
    SV *self
    IV member
  PREINIT:
    EXTRACT(self);
    double sc;
  CODE:
    ss_rwlock_rdlock(h);
    RETVAL = ss_idx_get(h, (int64_t)member, &sc);
    ss_rwlock_rdunlock(h);
  OUTPUT:
    RETVAL

bool
remove(self, member)
    SV *self
    IV member
  PREINIT:
    EXTRACT(self);
  CODE:
    ss_rwlock_wrlock(h);
    RETVAL = ss_remove_locked(h, (int64_t)member);
    __atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
    ss_rwlock_wrunlock(h);
  OUTPUT:
    RETVAL

NV
incr(self, member, delta)
    SV *self
    IV member
    NV delta
  PREINIT:
    EXTRACT(self);
    double out;
    int rc;
  CODE:
    if (delta != delta) croak("incr: delta must not be NaN");
    ss_rwlock_wrlock(h);
    rc = ss_incr_locked(h, (int64_t)member, (double)delta, &out);
    __atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
    ss_rwlock_wrunlock(h);
    if (rc == -1) croak("incr: max_entries exhausted");
    if (rc == -2) croak("incr: result is NaN");
    RETVAL = out;
  OUTPUT:
    RETVAL

void
pop_min(self)
    SV *self
  PREINIT:
    EXTRACT(self);
  PPCODE:
    {
        int64_t m; double s; int ok;
        ss_rwlock_wrlock(h);
        ok = ss_pop_locked(h, 0, &m, &s);
        if (ok) __atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
        ss_rwlock_wrunlock(h);
        if (ok) { EXTEND(SP, 2); PUSHs(sv_2mortal(newSViv((IV)m))); PUSHs(sv_2mortal(newSVnv(s))); }
    }

void
pop_max(self)
    SV *self
  PREINIT:
    EXTRACT(self);
  PPCODE:
    {
        int64_t m; double s; int ok;
        ss_rwlock_wrlock(h);
        ok = ss_pop_locked(h, 1, &m, &s);
        if (ok) __atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
        ss_rwlock_wrunlock(h);
        if (ok) { EXTEND(SP, 2); PUSHs(sv_2mortal(newSViv((IV)m))); PUSHs(sv_2mortal(newSVnv(s))); }
    }

bool
_validate(self)
    SV *self
  PREINIT:
    EXTRACT(self);
  CODE:
    ss_rwlock_rdlock(h);
    RETVAL = ss_validate_tree(h);
    ss_rwlock_rdunlock(h);
  OUTPUT:
    RETVAL

SV *
rank(self, member)
    SV *self
    IV member
  PREINIT:
    EXTRACT(self);
    double sc;
  CODE:
    ss_rwlock_rdlock(h);
    RETVAL = ss_idx_get(h, (int64_t)member, &sc)
        ? newSVuv(ss_rank_of(h, sc, (int64_t)member)) : &PL_sv_undef;
    ss_rwlock_rdunlock(h);
  OUTPUT:
    RETVAL

SV *
rev_rank(self, member)
    SV *self
    IV member
  PREINIT:
    EXTRACT(self);
    double sc;
  CODE:
    ss_rwlock_rdlock(h);
    RETVAL = ss_idx_get(h, (int64_t)member, &sc)
        ? newSVuv(h->hdr->count - 1 - ss_rank_of(h, sc, (int64_t)member)) : &PL_sv_undef;
    ss_rwlock_rdunlock(h);
  OUTPUT:
    RETVAL

SV *
at_rank(self, rank)
    SV *self
    IV rank
  PREINIT:
    EXTRACT(self);
  CODE:
    ss_rwlock_rdlock(h);
    {
        uint32_t cnt = h->hdr->count;
        IV r = rank; if (r < 0) r += (IV)cnt;
        if (r >= 0 && (uint64_t)r < cnt) {          /* compare in 64-bit; large r must not truncate to an in-range index */
            int pos; uint32_t leaf = ss_at_rank(h, (uint32_t)r, &pos);

Shared.xs  view on Meta::CPAN

    if (RETVAL < 0) croak("eventfd: %s", strerror(errno));
  OUTPUT:
    RETVAL

int
fileno(self)
    SV *self
  PREINIT:
    EXTRACT(self);
  CODE:
    RETVAL = h->notify_fd;
  OUTPUT:
    RETVAL

bool
notify(self)
    SV *self
  PREINIT:
    EXTRACT(self);
  CODE:
    RETVAL = ss_notify(h);
  OUTPUT:
    RETVAL

SV *
eventfd_consume(self)
    SV *self
  PREINIT:
    EXTRACT(self);
  CODE:
    {
        int64_t v = ss_eventfd_consume(h);
        RETVAL = (v < 0) ? &PL_sv_undef : newSVuv((UV)v);
    }
  OUTPUT:
    RETVAL

IV
add_many(self, rows)
    SV *self
    SV *rows
  PREINIT:
    EXTRACT(self);
    int added = 0;
  CODE:
    if (!SvROK(rows) || SvTYPE(SvRV(rows)) != SVt_PVAV)
        croak("add_many: expected an arrayref of [member, score] rows");
    {
        AV *av = (AV *)SvRV(rows);
        SSize_t nr = av_len(av) + 1;
        ss_rwlock_wrlock(h);
        for (SSize_t i = 0; i < nr; i++) {
            SV **rv = av_fetch(av, i, 0);
            if (!rv || !SvROK(*rv) || SvTYPE(SvRV(*rv)) != SVt_PVAV) continue;   /* skip malformed */
            AV *row = (AV *)SvRV(*rv);
            if (av_len(row) + 1 < 2) continue;
            SV **ms = av_fetch(row, 0, 0), **sv = av_fetch(row, 1, 0);
            if (!ms || !sv) continue;
            double score = SvNV(*sv);
            if (score != score) continue;                                       /* skip NaN */
            int rc = ss_add_locked(h, (int64_t)SvIV(*ms), score);
            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);
    }
    RETVAL = added;
  OUTPUT:
    RETVAL

SV *
stats(self)
    SV *self
  PREINIT:
    EXTRACT(self);
  CODE:
    {
        HV *hv = newHV();
        ss_rwlock_rdlock(h);
        SsHeader *hd = h->hdr;
        uint32_t nfree = 0, f = hd->node_free_head;
        while (f != SS_NONE) { nfree++; f = h->nodes[f].parent; }
        uint32_t iload = hd->count;   /* backward-shift delete leaves no tombstones: occupied slots == count */
        hv_stores(hv, "count",         newSVuv(hd->count));
        hv_stores(hv, "max_entries",   newSVuv(hd->max_entries));
        hv_stores(hv, "height",        newSVuv(hd->height));
        hv_stores(hv, "node_capacity", newSVuv(hd->node_capacity));
        hv_stores(hv, "nodes_used",    newSVuv(hd->node_capacity - nfree));
        hv_stores(hv, "index_slots",   newSVuv(hd->index_slots));
        hv_stores(hv, "index_load",    newSVnv((double)iload / (double)hd->index_slots));
        hv_stores(hv, "ops",           newSVuv(hd->stat_ops));
        hv_stores(hv, "mmap_size",     newSVuv((UV)h->mmap_size));
        ss_rwlock_rdunlock(h);
        RETVAL = newRV_noinc((SV *)hv);
    }
  OUTPUT:
    RETVAL



( run in 0.750 second using v1.01-cache-2.11-cpan-bbe5e583499 )