Data-SpatialHash-Shared
view release on metacpan or search on metacpan
RETVAL = h->notify_fd;
OUTPUT:
RETVAL
bool
notify(self)
SV *self
PREINIT:
EXTRACT(self);
CODE:
RETVAL = sph_notify(h);
OUTPUT:
RETVAL
SV *
eventfd_consume(self)
SV *self
PREINIT:
EXTRACT(self);
CODE:
int64_t n = sph_eventfd_consume(h);
RETVAL = (n >= 0) ? newSViv((IV)n) : &PL_sv_undef;
OUTPUT:
RETVAL
UV
max_entries(self)
SV *self
PREINIT:
EXTRACT(self);
CODE:
RETVAL = h->hdr->max_entries;
OUTPUT:
RETVAL
UV
num_buckets(self)
SV *self
PREINIT:
EXTRACT(self);
CODE:
RETVAL = h->hdr->num_buckets;
OUTPUT:
RETVAL
NV
cell_size(self)
SV *self
PREINIT:
EXTRACT(self);
CODE:
RETVAL = h->hdr->cell_size;
OUTPUT:
RETVAL
void
world(self)
SV *self
PREINIT:
EXTRACT(self);
PPCODE:
if (h->wrap) {
int dims = (h->hdr->world[2] > 0.0) ? 3 : 2;
EXTEND(SP, dims);
for (int i = 0; i < dims; i++) PUSHs(sv_2mortal(newSVnv(h->hdr->world[i])));
}
NV
sphere(self)
SV *self
PREINIT:
EXTRACT(self);
CODE:
RETVAL = h->hdr->sphere_radius;
OUTPUT:
RETVAL
UV
count(self)
SV *self
PREINIT:
EXTRACT(self);
CODE:
RETVAL = __atomic_load_n(&h->hdr->count, __ATOMIC_ACQUIRE);
OUTPUT:
RETVAL
SV *
path(self)
SV *self
PREINIT:
EXTRACT(self);
CODE:
RETVAL = h->path ? newSVpv(h->path, 0) : &PL_sv_undef;
OUTPUT:
RETVAL
SV *
insert(self, x, y, ...)
SV *self
NV x
NV y
PREINIT:
EXTRACT(self);
double z, radius;
int64_t val;
uint32_t idx;
CODE:
/* (x,y,value)=4 2D ; (x,y,z,value)=5 3D ; (x,y,z,value,radius)=6 3D+radius */
z = 0; radius = 0;
if (items == 4) { val = (int64_t)SvIV(ST(3)); }
else if (items == 5) { z = (double)SvNV(ST(3)); val = (int64_t)SvIV(ST(4)); }
else if (items == 6) { z = (double)SvNV(ST(3)); val = (int64_t)SvIV(ST(4)); radius = (double)SvNV(ST(5)); }
else croak("insert: expected (x,y,value), (x,y,z,value), or (x,y,z,value,radius)");
if (radius < 0 || !isfinite(radius)) croak("insert: radius must be a finite number >= 0");
REEXTRACT(self);
sph_rwlock_wrlock(h);
idx = sph_insert_locked(h, x, y, z, val, radius);
__atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
sph_rwlock_wrunlock(h);
RETVAL = (idx == SPH_NONE) ? &PL_sv_undef : newSVuv(idx);
sph_rwlock_rdunlock(h);
OUTPUT:
RETVAL
IV
move_many(self, rows)
SV *self
SV *rows
PREINIT:
EXTRACT(self);
IV moved;
CODE:
SvGETMAGIC(rows);
if (!SvROK(rows) || SvTYPE(SvRV(rows)) != SVt_PVAV)
croak("move_many: expected an arrayref of [handle,x,y] or [handle,x,y,z]");
{
AV *av = (AV *)SvRV(rows);
SSize_t nr = av_len(av) + 1;
struct mm_row { uint32_t handle; double x, y, z; int valid; } *R = NULL;
moved = 0;
if (nr > 0) { Newxz(R, nr, struct mm_row); SAVEFREEPV(R); }
/* Phase 1: resolve all user-controlled conversions with NO lock held.
SvUV/SvNV run get-magic/overload = arbitrary Perl that may die; doing it
here (lock-free) means a longjmp can't strand the write lock. */
for (SSize_t i = 0; i < nr; i++) {
SV **rv = av_fetch(av, i, 0);
if (rv) SvGETMAGIC(*rv); /* a tied-array element is a deferred-magic PVLV */
if (!rv || !SvROK(*rv) || SvTYPE(SvRV(*rv)) != SVt_PVAV) continue;
AV *row = (AV *)SvRV(*rv);
SSize_t rl = av_len(row) + 1;
if (rl != 3 && rl != 4) continue;
SV **hp = av_fetch(row, 0, 0), **xp = av_fetch(row, 1, 0), **yp = av_fetch(row, 2, 0);
SV **zp = (rl == 4) ? av_fetch(row, 3, 0) : NULL;
if (!hp || !xp || !yp) continue;
R[i].handle = sph_idx(SvUV(*hp));
R[i].x = SvNV(*xp);
R[i].y = SvNV(*yp);
R[i].z = zp ? SvNV(*zp) : 0.0;
R[i].valid = 1;
}
/* Phase 2: pure C under the write lock, using pre-resolved values only. */
REEXTRACT(self);
sph_rwlock_wrlock(h);
for (SSize_t i = 0; i < nr; i++) {
if (!R[i].valid) continue;
if (sph_move_locked(h, R[i].handle, R[i].x, R[i].y, R[i].z)) moved++;
}
if (nr > 0) __atomic_fetch_add(&h->hdr->stat_ops, (uint64_t)nr, __ATOMIC_RELAXED);
sph_rwlock_wrunlock(h);
}
RETVAL = moved;
OUTPUT:
RETVAL
void
insert_many(self, rows)
SV *self
SV *rows
PREINIT:
EXTRACT(self);
PPCODE:
SvGETMAGIC(rows);
if (!SvROK(rows) || SvTYPE(SvRV(rows)) != SVt_PVAV)
croak("insert_many: expected an arrayref of [x,y,value] or [x,y,value,radius]");
{
AV *av = (AV *)SvRV(rows);
SSize_t nr = av_len(av) + 1;
struct im_row { double x, y, rad; int64_t val; int valid; } *R = NULL;
uint32_t *ids = NULL;
EXTEND(SP, nr);
if (nr > 0) {
Newxz(R, nr, struct im_row); SAVEFREEPV(R);
Newx(ids, nr, uint32_t); SAVEFREEPV(ids);
}
/* Phase 1: resolve all user-controlled conversions with NO lock held.
SvNV/SvIV run get-magic/overload = arbitrary Perl that may die; doing it
here (lock-free) means a longjmp can't strand the write lock. */
for (SSize_t i = 0; i < nr; i++) {
SV **rv = av_fetch(av, i, 0);
if (rv) SvGETMAGIC(*rv); /* a tied-array element is a deferred-magic PVLV */
if (rv && SvROK(*rv) && SvTYPE(SvRV(*rv)) == SVt_PVAV) {
AV *row = (AV *)SvRV(*rv);
SSize_t rl = av_len(row) + 1;
if (rl == 3 || rl == 4) {
SV **xp = av_fetch(row, 0, 0), **yp = av_fetch(row, 1, 0), **vp = av_fetch(row, 2, 0);
SV **rp = (rl == 4) ? av_fetch(row, 3, 0) : NULL;
if (xp && yp && vp) {
double rad = rp ? SvNV(*rp) : 0.0;
/* a row with a bad radius yields an undef handle, like other
malformed rows */
if (rad >= 0 && isfinite(rad)) {
R[i].x = SvNV(*xp);
R[i].y = SvNV(*yp);
R[i].val = (int64_t)SvIV(*vp);
R[i].rad = rad;
R[i].valid = 1;
}
}
}
}
}
/* Phase 2: pure C under the write lock into a C result buffer. */
REEXTRACT(self);
sph_rwlock_wrlock(h);
for (SSize_t i = 0; i < nr; i++)
ids[i] = R[i].valid
? sph_insert_locked(h, R[i].x, R[i].y, 0.0, R[i].val, R[i].rad)
: SPH_NONE;
if (nr > 0) __atomic_fetch_add(&h->hdr->stat_ops, (uint64_t)nr, __ATOMIC_RELAXED);
sph_rwlock_wrunlock(h);
/* Phase 3: build result SVs after the lock is released. */
for (SSize_t i = 0; i < nr; i++)
PUSHs(ids[i] == SPH_NONE ? &PL_sv_undef : sv_2mortal(newSVuv(ids[i])));
}
void
position(self, handle)
SV *self
UV handle
PREINIT:
EXTRACT(self);
double px, py, pz;
PPCODE:
sph_rwlock_rdlock(h);
REQUIRE_LIVE_RD(h, sph_idx(handle));
px = h->entries[sph_idx(handle)].pos[0];
py = h->entries[sph_idx(handle)].pos[1];
pz = h->entries[sph_idx(handle)].pos[2];
sph_rwlock_rdunlock(h);
EXTEND(SP, 3);
PUSHs(sv_2mortal(newSVnv(px)));
PUSHs(sv_2mortal(newSVnv(py)));
PUSHs(sv_2mortal(newSVnv(pz)));
void
query_cell(self, x, y, ...)
SV *self
NV x
NV y
PREINIT:
EXTRACT(self);
PPCODE:
if (items != 3 && items != 4) croak("query_cell: (x,y) or (x,y,z)");
int dims = (items == 4) ? 3 : 2;
double p[3] = { x, y, dims==3 ? (double)SvNV(ST(3)) : 0 };
REEXTRACT(self);
EMIT_QUERY(sph_query_cell(h, p, dims, &col));
void
query_aabb(self, ...)
SV *self
PREINIT:
EXTRACT(self);
PPCODE:
double lo[3], hi[3]; int dims;
if (items == 5) { dims = 2;
lo[0]=SvNV(ST(1)); lo[1]=SvNV(ST(2)); hi[0]=SvNV(ST(3)); hi[1]=SvNV(ST(4)); lo[2]=hi[2]=0;
} else if (items == 7) { dims = 3;
lo[0]=SvNV(ST(1)); lo[1]=SvNV(ST(2)); lo[2]=SvNV(ST(3));
hi[0]=SvNV(ST(4)); hi[1]=SvNV(ST(5)); hi[2]=SvNV(ST(6));
} else croak("query_aabb: (x0,y0,x1,y1) or (x0,y0,z0,x1,y1,z1)");
REEXTRACT(self);
EMIT_QUERY(sph_query_aabb(h, lo, hi, dims, &col));
void
query_radius(self, ...)
SV *self
PREINIT:
EXTRACT(self);
PPCODE:
double c[3] = {0,0,0}, r; int dims;
if (items == 4) { dims = 2; c[0]=SvNV(ST(1)); c[1]=SvNV(ST(2)); r=SvNV(ST(3)); }
else if (items == 5) { dims = 3; c[0]=SvNV(ST(1)); c[1]=SvNV(ST(2)); c[2]=SvNV(ST(3)); r=SvNV(ST(4)); }
else croak("query_radius: (x,y,r) or (x,y,z,r)");
if (r < 0 || !isfinite(r)) croak("query_radius: r must be a finite number >= 0");
REEXTRACT(self);
EMIT_QUERY(sph_query_radius(h, c, r, dims, &col));
# Batched broad-phase: N radius queries under ONE read lock. Returns an arrayref of
# id-list arrayrefs, one per query in input order (each == [query_radius(@$q)]). A
# malformed row (not a 3/4-elem arrayref, or a negative/non-finite r) yields an empty
# list for that slot -- can't croak under the lock, mirrors insert_many. OOM/TOOBIG
# from any query croak after freeing the partial result tree.
SV *
query_radius_many(self, queries)
SV *self
SV *queries
PREINIT:
EXTRACT(self);
CODE:
SvGETMAGIC(queries);
if (!SvROK(queries) || SvTYPE(SvRV(queries)) != SVt_PVAV)
croak("query_radius_many: expected an arrayref of [x,y,r] or [x,y,z,r]");
{
AV *qav = (AV *)SvRV(queries);
SSize_t nq = av_len(qav) + 1;
AV *out = newAV();
if (nq > 0) av_extend(out, nq - 1);
int err = 0; /* 0 ok, 1 OOM, 2 TOOBIG */
struct qr_row { double c[3]; double r; int dims; } *Q = NULL;
if (nq > 0) { Newxz(Q, nq, struct qr_row); SAVEFREEPV(Q); }
/* Phase 1: resolve all user-controlled conversions with NO lock held.
SvNV runs get-magic/overload = arbitrary Perl that may die; doing it here
(lock-free) means a longjmp can't strand the read lock. dims stays 0 for a
malformed query -> an empty result slot. */
for (SSize_t i = 0; i < nq; i++) {
SV **qp = av_fetch(qav, i, 0);
if (qp) SvGETMAGIC(*qp); /* a tied-array element is a deferred-magic PVLV */
if (qp && SvROK(*qp) && SvTYPE(SvRV(*qp)) == SVt_PVAV) {
AV *q = (AV *)SvRV(*qp);
SSize_t ql = av_len(q) + 1;
double c[3] = {0,0,0}, r = 0; int dims = 0;
if (ql == 3) {
SV **x=av_fetch(q,0,0), **y=av_fetch(q,1,0), **rr=av_fetch(q,2,0);
if (x && y && rr) { dims=2; c[0]=SvNV(*x); c[1]=SvNV(*y); r=SvNV(*rr); }
} else if (ql == 4) {
SV **x=av_fetch(q,0,0), **y=av_fetch(q,1,0), **z=av_fetch(q,2,0), **rr=av_fetch(q,3,0);
if (x && y && z && rr) { dims=3; c[0]=SvNV(*x); c[1]=SvNV(*y); c[2]=SvNV(*z); r=SvNV(*rr); }
}
if (dims && r >= 0 && isfinite(r)) {
Q[i].c[0]=c[0]; Q[i].c[1]=c[1]; Q[i].c[2]=c[2]; Q[i].r=r; Q[i].dims=dims;
}
}
}
/* Phase 2: run the queries under one read lock, collecting raw ids into C
buffers only. Building the Perl result tree here (newAV/newSViv/av_push)
could hit an allocation failure -> Perl longjmps out with the read lock
still held, permanently stranding this process's rdepth (a later writer
then hangs forever draining it). So we defer ALL SV building to Phase 3,
after rdunlock -- matching EMIT_QUERY and every sibling query method. */
av_extend(res, (SSize_t)R[i].n - 1);
for (size_t k = 0; k < R[i].n; k++) av_push(res, newSViv((IV)R[i].vals[k]));
}
free(R[i].vals); /* malloc'd by sph_query_radius */
av_push(out, newRV_noinc((SV *)res)); /* out owns res */
}
RETVAL = newRV_noinc((SV *)out);
}
OUTPUT:
RETVAL
SV *
insert_geo(self, lat, lon, alt, value)
SV *self
NV lat
NV lon
NV alt
IV value
PREINIT:
EXTRACT(self);
double xyz[3];
uint32_t idx;
CODE:
if (!(h->hdr->sphere_radius > 0.0)) croak("insert_geo: map was not created with sphere => R");
sph_geo_to_xyz(h->hdr->sphere_radius, lat, lon, alt, xyz);
sph_rwlock_wrlock(h);
idx = sph_insert_locked(h, xyz[0], xyz[1], xyz[2], (int64_t)value, 0.0);
__atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
sph_rwlock_wrunlock(h);
RETVAL = (idx == SPH_NONE) ? &PL_sv_undef : newSVuv(idx);
OUTPUT:
RETVAL
bool
move_geo(self, handle, lat, lon, alt)
SV *self
UV handle
NV lat
NV lon
NV alt
PREINIT:
EXTRACT(self);
double xyz[3];
CODE:
if (!(h->hdr->sphere_radius > 0.0)) croak("move_geo: map was not created with sphere => R");
sph_geo_to_xyz(h->hdr->sphere_radius, lat, lon, alt, xyz);
sph_rwlock_wrlock(h);
RETVAL = sph_move_locked(h, sph_idx(handle), xyz[0], xyz[1], xyz[2]);
__atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
sph_rwlock_wrunlock(h);
OUTPUT:
RETVAL
void
position_geo(self, handle)
SV *self
UV handle
PREINIT:
EXTRACT(self);
double p[3], lat, lon, alt;
PPCODE:
if (!(h->hdr->sphere_radius > 0.0)) croak("position_geo: map was not created with sphere => R");
sph_rwlock_rdlock(h);
REQUIRE_LIVE_RD(h, sph_idx(handle));
p[0] = h->entries[sph_idx(handle)].pos[0];
p[1] = h->entries[sph_idx(handle)].pos[1];
p[2] = h->entries[sph_idx(handle)].pos[2];
sph_rwlock_rdunlock(h);
sph_geo_of_xyz(h->hdr->sphere_radius, p, &lat, &lon, &alt);
EXTEND(SP, 3);
PUSHs(sv_2mortal(newSVnv(lat)));
PUSHs(sv_2mortal(newSVnv(lon)));
PUSHs(sv_2mortal(newSVnv(alt)));
void
query_geo_radius(self, lat, lon, alt, dist)
SV *self
NV lat
NV lon
NV alt
NV dist
PREINIT:
EXTRACT(self);
double c[3];
PPCODE:
if (!(h->hdr->sphere_radius > 0.0)) croak("query_geo_radius: map was not created with sphere => R");
if (dist < 0 || !isfinite(dist)) croak("query_geo_radius: dist must be a finite number >= 0");
sph_geo_to_xyz(h->hdr->sphere_radius, lat, lon, alt, c);
EMIT_QUERY(sph_query_radius(h, c, (double)dist, 3, &col));
UV
cube_cell(self, x, y, z, level)
SV *self
NV x
NV y
NV z
IV level
PREINIT:
EXTRACT(self);
double dir[3];
CODE:
(void)h;
if (level < 0 || level > SPH_CUBE_MAX_LEVEL) croak("cube_cell: level must be in 0..%d", SPH_CUBE_MAX_LEVEL);
dir[0] = x; dir[1] = y; dir[2] = z;
RETVAL = (UV)sph_cube_cell(dir, (int)level);
OUTPUT:
RETVAL
UV
cube_cell_geo(self, lat, lon, level)
SV *self
NV lat
NV lon
IV level
PREINIT:
EXTRACT(self);
double dir[3];
CODE:
(void)h;
if (level < 0 || level > SPH_CUBE_MAX_LEVEL) croak("cube_cell_geo: level must be in 0..%d", SPH_CUBE_MAX_LEVEL);
sph_geo_to_xyz(1.0, lat, lon, 0.0, dir); /* unit direction */
RETVAL = (UV)sph_cube_cell(dir, (int)level);
OUTPUT:
RETVAL
IV
cube_level(self, cell)
SV *self
UV cell
PREINIT:
EXTRACT(self);
CODE:
(void)h;
if (!sph_cube_valid((uint64_t)cell)) croak("cube_level: not a valid cube cell id");
RETVAL = (IV)sph_cube_level((uint64_t)cell);
OUTPUT:
RETVAL
void
cube_center(self, cell)
SV *self
UV cell
PREINIT:
EXTRACT(self);
double d[3];
PPCODE:
(void)h;
if (!sph_cube_valid((uint64_t)cell)) croak("cube_center: not a valid cube cell id");
sph_cube_center((uint64_t)cell, d);
EXTEND(SP, 3);
PUSHs(sv_2mortal(newSVnv(d[0])));
PUSHs(sv_2mortal(newSVnv(d[1])));
PUSHs(sv_2mortal(newSVnv(d[2])));
void
cube_center_geo(self, cell)
SV *self
UV cell
PREINIT:
EXTRACT(self);
double d[3], lat, lon, alt;
PPCODE:
(void)h;
if (!sph_cube_valid((uint64_t)cell)) croak("cube_center_geo: not a valid cube cell id");
sph_cube_center((uint64_t)cell, d);
sph_geo_of_xyz(1.0, d, &lat, &lon, &alt);
EXTEND(SP, 2);
PUSHs(sv_2mortal(newSVnv(lat)));
PUSHs(sv_2mortal(newSVnv(lon)));
SV *
cube_parent(self, cell)
SV *self
UV cell
PREINIT:
EXTRACT(self);
uint64_t p;
CODE:
(void)h;
if (!sph_cube_valid((uint64_t)cell)) croak("cube_parent: not a valid cube cell id");
RETVAL = sph_cube_parent((uint64_t)cell, &p) ? newSVuv((UV)p) : &PL_sv_undef;
OUTPUT:
RETVAL
void
cube_children(self, cell)
SV *self
UV cell
PREINIT:
EXTRACT(self);
uint64_t kids[4];
PPCODE:
(void)h;
if (!sph_cube_valid((uint64_t)cell)) croak("cube_children: not a valid cube cell id");
if (sph_cube_children((uint64_t)cell, kids)) {
EXTEND(SP, 4);
for (int k = 0; k < 4; k++) PUSHs(sv_2mortal(newSVuv((UV)kids[k])));
}
void
cube_neighbors(self, cell)
SV *self
UV cell
PREINIT:
EXTRACT(self);
uint64_t nb[4];
PPCODE:
(void)h;
if (!sph_cube_valid((uint64_t)cell)) croak("cube_neighbors: not a valid cube cell id");
sph_cube_neighbors((uint64_t)cell, nb);
EXTEND(SP, 4);
for (int k = 0; k < 4; k++) PUSHs(sv_2mortal(newSVuv((UV)nb[k])));
void query_knn(self, ...)
SV *self
PREINIT:
EXTRACT(self);
PPCODE:
double c[3] = {0,0,0}; uint32_t k; int dims; IV kiv;
if (items == 4) { dims = 2; c[0]=SvNV(ST(1)); c[1]=SvNV(ST(2)); kiv=SvIV(ST(3)); }
else if (items == 5) { dims = 3; c[0]=SvNV(ST(1)); c[1]=SvNV(ST(2)); c[2]=SvNV(ST(3)); kiv=SvIV(ST(4)); }
else croak("query_knn: (x,y,k) or (x,y,z,k)");
if (kiv < 1) croak("query_knn: k must be >= 1"); /* SvIV so a negative k is caught (SvUV would wrap it to a huge k) */
k = kiv > (IV)UINT32_MAX ? UINT32_MAX : (uint32_t)kiv; /* clamp an absurd k; knn caps at count anyway */
REEXTRACT(self);
EMIT_QUERY(sph_query_knn(h, c, k, dims, &col));
void each_in_radius(self, ...)
SV *self
PREINIT:
EXTRACT(self);
PPCODE:
/* items: self,x,y,r,cb (5)=2D ; self,x,y,z,r,cb (6)=3D. cb is last. */
double c[3] = {0,0,0}, r; int dims; SV *cb;
if (items == 5) { dims=2; c[0]=SvNV(ST(1)); c[1]=SvNV(ST(2)); r=SvNV(ST(3)); cb=ST(4); }
else if (items == 6) { dims=3; c[0]=SvNV(ST(1)); c[1]=SvNV(ST(2)); c[2]=SvNV(ST(3)); r=SvNV(ST(4)); cb=ST(5); }
else croak("each_in_radius: (x,y,r,cb) or (x,y,z,r,cb)");
SvGETMAGIC(cb); /* a tied/overloaded scalar may FETCH to a coderef */
if (!SvROK(cb) || SvTYPE(SvRV(cb)) != SVt_PVCV) croak("each_in_radius: last arg must be a coderef");
if (r < 0 || !isfinite(r)) croak("each_in_radius: r must be a finite number >= 0");
/* snapshot under lock */
sph_collect_t col = { NULL, 0, 0 };
REEXTRACT(self);
sph_rwlock_rdlock(h);
int rc = sph_query_radius(h, c, r, dims, &col);
sph_rwlock_rdunlock(h);
if (rc == SPH_Q_OOM) { free(col.vals); croak("each_in_radius: out of memory"); }
if (rc == SPH_Q_TOOBIG) { free(col.vals); croak(SPH_TOOBIG_MSG, (unsigned)SPH_MAX_QUERY_CELLS); }
/* invoke callback per value AFTER releasing the lock; G_EVAL so a die in
* the callback does not skip free(col.vals) -- re-throw after cleanup. */
for (size_t i = 0; i < col.n; i++) {
dSP; ENTER; SAVETMPS; PUSHMARK(SP);
XPUSHs(sv_2mortal(newSViv((IV)col.vals[i])));
PUTBACK;
call_sv(cb, G_VOID|G_DISCARD|G_EVAL);
FREETMPS; LEAVE;
if (SvTRUE(ERRSV)) { free(col.vals); croak_sv(ERRSV); }
}
free(col.vals);
void
each_pair_within(self, max_r, cb)
SV *self
NV max_r
SV *cb
PREINIT:
EXTRACT(self);
PPCODE:
SvGETMAGIC(cb); /* a tied/overloaded scalar may FETCH to a coderef */
if (!SvROK(cb) || SvTYPE(SvRV(cb)) != SVt_PVCV) croak("each_pair_within: last arg must be a coderef");
if (max_r < 0 || !isfinite(max_r)) croak("each_pair_within: max_r must be a finite number >= 0");
REEXTRACT(self);
EMIT_PAIRS(sph_pairs(h, (double)max_r, sph_pair_to_collect, &col));
void
each_colliding_pair(self, cb)
SV *self
SV *cb
PREINIT:
EXTRACT(self);
PPCODE:
SvGETMAGIC(cb); /* a tied/overloaded scalar may FETCH to a coderef */
if (!SvROK(cb) || SvTYPE(SvRV(cb)) != SVt_PVCV) croak("each_colliding_pair: arg must be a coderef");
REEXTRACT(self);
EMIT_PAIRS(sph_pairs(h, -1.0, sph_pair_to_collect, &col));
void clear(self)
SV *self
PREINIT:
EXTRACT(self);
CODE:
sph_rwlock_wrlock(h);
sph_clear_locked(h);
__atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
sph_rwlock_wrunlock(h);
SV *stats(self)
SV *self
PREINIT:
EXTRACT(self);
CODE:
sph_rwlock_rdlock(h);
uint32_t occ, mx, mxcell; sph_chain_stats(h, &occ, &mx, &mxcell);
uint32_t cnt = h->hdr->count, me = h->hdr->max_entries, nb = h->hdr->num_buckets;
sph_rwlock_rdunlock(h);
HV *hv = newHV();
hv_store(hv, "count", 5, newSVuv(cnt), 0);
hv_store(hv, "max_entries", 11, newSVuv(me), 0);
hv_store(hv, "num_buckets", 11, newSVuv(nb), 0);
hv_store(hv, "cell_size", 9, newSVnv(h->hdr->cell_size), 0);
hv_store(hv, "free_slots", 10, newSVuv(me - cnt), 0);
hv_store(hv, "occupied_buckets", 16, newSVuv(occ), 0);
hv_store(hv, "max_chain", 9, newSVuv(mx), 0);
hv_store(hv, "max_cell", 8, newSVuv(mxcell), 0);
hv_store(hv, "load_factor", 11, newSVnv(nb ? (double)cnt / nb : 0), 0);
hv_store(hv, "ops", 3, newSVuv((UV)__atomic_load_n(&h->hdr->stat_ops, __ATOMIC_RELAXED)), 0);
hv_store(hv, "mmap_size", 9, newSVuv((UV)h->mmap_size), 0);
RETVAL = newRV_noinc((SV *)hv);
OUTPUT: RETVAL
( run in 1.507 second using v1.01-cache-2.11-cpan-92ad3014f07 )