Data-SpatialHash-Shared
view release on metacpan or search on metacpan
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");
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);
OUTPUT:
RETVAL
bool
move(self, handle, x, y, ...)
SV *self
UV handle
NV x
NV y
PREINIT:
EXTRACT(self);
double z;
CODE:
z = 0;
if (items == 5) z = (double)SvNV(ST(4));
else if (items != 4) croak("move: expected (handle,x,y) or (handle,x,y,z)");
sph_rwlock_wrlock(h);
RETVAL = sph_move_locked(h, (uint32_t)handle, x, y, z);
__atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
sph_rwlock_wrunlock(h);
OUTPUT:
RETVAL
bool
remove(self, handle)
SV *self
UV handle
PREINIT:
EXTRACT(self);
CODE:
sph_rwlock_wrlock(h);
RETVAL = sph_remove_locked(h, (uint32_t)handle);
__atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
sph_rwlock_wrunlock(h);
OUTPUT:
RETVAL
bool
has(self, handle)
SV *self
UV handle
PREINIT:
EXTRACT(self);
CODE:
sph_rwlock_rdlock(h);
RETVAL = sph_is_live(h, (uint32_t)handle);
sph_rwlock_rdunlock(h);
OUTPUT:
RETVAL
IV
value(self, handle)
SV *self
UV handle
PREINIT:
EXTRACT(self);
CODE:
sph_rwlock_rdlock(h);
REQUIRE_LIVE_RD(h, (uint32_t)handle);
RETVAL = (IV)h->entries[(uint32_t)handle].value;
sph_rwlock_rdunlock(h);
OUTPUT:
RETVAL
void
set_value(self, handle, v)
SV *self
UV handle
IV v
PREINIT:
EXTRACT(self);
CODE:
sph_rwlock_wrlock(h);
REQUIRE_LIVE(h, (uint32_t)handle);
h->entries[(uint32_t)handle].value = (int64_t)v;
__atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
sph_rwlock_wrunlock(h);
void
set_radius(self, handle, radius)
SV *self
UV handle
NV radius
PREINIT:
EXTRACT(self);
CODE:
if (radius < 0 || !isfinite(radius)) croak("set_radius: radius must be a finite number >= 0");
sph_rwlock_wrlock(h);
REQUIRE_LIVE(h, (uint32_t)handle);
h->entries[(uint32_t)handle].radius = (double)radius;
__atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
sph_rwlock_wrunlock(h);
NV
get_radius(self, handle)
SV *self
UV handle
PREINIT:
EXTRACT(self);
CODE:
sph_rwlock_rdlock(h);
REQUIRE_LIVE_RD(h, (uint32_t)handle);
RETVAL = h->entries[(uint32_t)handle].radius;
sph_rwlock_rdunlock(h);
OUTPUT:
RETVAL
IV
move_many(self, rows)
SV *self
SV *rows
PREINIT:
EXTRACT(self);
IV moved;
CODE:
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;
moved = 0;
sph_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;
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;
if (sph_move_locked(h, (uint32_t)SvUV(*hp), SvNV(*xp), SvNV(*yp), zp ? SvNV(*zp) : 0.0)) 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:
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;
EXTEND(SP, nr);
sph_rwlock_wrlock(h);
for (SSize_t i = 0; i < nr; i++) {
uint32_t idx = SPH_NONE;
SV **rv = av_fetch(av, i, 0);
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;
/* skip a row with a bad radius (-> undef handle), like other
malformed rows; can't croak here -- we hold the write lock */
if (rad >= 0 && isfinite(rad))
idx = sph_insert_locked(h, SvNV(*xp), SvNV(*yp), 0.0,
(int64_t)SvIV(*vp), rad);
}
}
}
PUSHs(idx == SPH_NONE ? &PL_sv_undef : sv_2mortal(newSVuv(idx)));
}
if (nr > 0) __atomic_fetch_add(&h->hdr->stat_ops, (uint64_t)nr, __ATOMIC_RELAXED);
sph_rwlock_wrunlock(h);
}
void
position(self, handle)
SV *self
UV handle
PREINIT:
EXTRACT(self);
double px, py, pz;
PPCODE:
sph_rwlock_rdlock(h);
REQUIRE_LIVE_RD(h, (uint32_t)handle);
px = h->entries[(uint32_t)handle].pos[0];
py = h->entries[(uint32_t)handle].pos[1];
pz = h->entries[(uint32_t)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 };
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)");
EMIT_QUERY(sph_query_aabb(h, lo, hi, dims, &col));
void
query_radius(self, ...)
SV *self
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 */
sph_rwlock_rdlock(h); /* one lock for the whole batch */
for (SSize_t i = 0; i < nq && !err; i++) {
AV *res = newAV();
SV **qp = av_fetch(qav, i, 0);
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)) {
sph_collect_t col = { NULL, 0, 0 };
int rc = sph_query_radius(h, c, r, dims, &col);
if (rc == SPH_Q_OOM) { free(col.vals); err = 1; }
else if (rc == SPH_Q_TOOBIG) { free(col.vals); err = 2; }
else {
if (col.n) av_extend(res, (SSize_t)col.n - 1);
for (size_t k = 0; k < col.n; k++) av_push(res, newSViv((IV)col.vals[k]));
free(col.vals);
}
}
/* else: malformed query -> empty res (cannot croak under the lock) */
}
av_push(out, newRV_noinc((SV *)res)); /* out owns res, even on the error path */
}
sph_rwlock_rdunlock(h);
if (err) {
SvREFCNT_dec((SV *)out); /* frees out + every res pushed so far */
if (err == 1) croak("query_radius_many: out of memory");
croak(SPH_TOOBIG_MSG, (unsigned)SPH_MAX_QUERY_CELLS);
}
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, (uint32_t)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, (uint32_t)handle);
p[0] = h->entries[(uint32_t)handle].pos[0];
p[1] = h->entries[(uint32_t)handle].pos[1];
p[2] = h->entries[(uint32_t)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
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)");
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 };
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:
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");
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:
if (!SvROK(cb) || SvTYPE(SvRV(cb)) != SVt_PVCV) croak("each_colliding_pair: arg must be a coderef");
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 0.332 second using v1.01-cache-2.11-cpan-bbe5e583499 )