Skip to content

Commit d93cb6e

Browse files
committed
Replace all uintptr_t's with JsFakeInts for consistency.
All other integer inputs are accepted as JsFakeInts and are safely cast within the function body, so we do the same with the uintptr_t's (which are still represented as Javascript numbers from _malloc and in the $$.ptrs). Also fixed a few other things while we're at it, such as making sure all integers are passed as JsFakeInts, and namespacing the fixed-width types. In particular, cleaned up the NeighborResults constructor.
1 parent cebfbfd commit d93cb6e

34 files changed

Lines changed: 459 additions & 268 deletions

src/NeighborIndex.cpp

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,28 +40,31 @@ std::unique_ptr<knncolle::Builder<std::int32_t, double, double, knncolle::Simple
4040
}
4141
}
4242

43-
NeighborIndex build_neighbor_index(std::uintptr_t mat, JsFakeInt nr_raw, JsFakeInt nc_raw, bool approximate) {
43+
NeighborIndex build_neighbor_index(JsFakeInt mat_raw, JsFakeInt nr_raw, JsFakeInt nc_raw, bool approximate) {
4444
auto builder = create_builder(approximate);
4545
NeighborIndex output;
46-
const double* ptr = reinterpret_cast<const double*>(mat);
47-
output.index = builder->build_unique(knncolle::SimpleMatrix<std::int32_t, double>(js2int<std::size_t>(nr_raw), js2int<std::int32_t>(nc_raw), ptr));
46+
const auto nr = js2int<std::size_t>(nr_raw);
47+
const auto nc = js2int<std::int32_t>(nc_raw);
48+
const double* ptr = reinterpret_cast<const double*>(js2int<std::uintptr_t>(mat_raw));
49+
output.index = builder->build_unique(knncolle::SimpleMatrix<std::int32_t, double>(nr, nc, ptr));
4850
return output;
4951
}
5052

5153
NeighborResults find_nearest_neighbors(const NeighborIndex& index, JsFakeInt k_raw, JsFakeInt nthreads_raw) {
52-
NeighborResults output;
53-
output.neighbors = knncolle::find_nearest_neighbors(*(index.index), js2int<int>(k_raw), js2int<int>(nthreads_raw));
54-
return output;
54+
return NeighborResults(knncolle::find_nearest_neighbors(*(index.index), js2int<int>(k_raw), js2int<int>(nthreads_raw)));
5555
}
5656

57-
NeighborResults truncate_nearest_neighbors(const NeighborResults& original, JsFakeInt k_raw) {
57+
NeighborResults truncate_nearest_neighbors(const NeighborResults& input, JsFakeInt k_raw) {
5858
NeighborResults output;
59-
const auto nobs = original.neighbors.size();
60-
output.neighbors.resize(nobs);
59+
const auto nobs = input.neighbors().size();
60+
auto& out_neighbors = output.neighbors();
61+
sanisizer::resize(out_neighbors, nobs);
62+
63+
auto& in_neighbors = input.neighbors();
6164
const auto desired = js2int<int>(k_raw);
6265
for (I<decltype(nobs)> i = 0; i < nobs; ++i) {
63-
const auto& current = original.neighbors[i];
64-
auto& curout = output.neighbors[i];
66+
const auto& current = in_neighbors[i];
67+
auto& curout = out_neighbors[i];
6568
const auto size = sanisizer::min(current.size(), desired);
6669
curout.insert(curout.end(), current.begin(), current.begin() + size);
6770
}
@@ -80,7 +83,7 @@ EMSCRIPTEN_BINDINGS(build_neighbor_index) {
8083
.function("num_dim", &NeighborIndex::num_dim, emscripten::return_value_policy::take_ownership());
8184

8285
emscripten::class_<NeighborResults>("NeighborResults")
83-
.constructor<size_t, uintptr_t, uintptr_t, uintptr_t>()
86+
.constructor<JsFakeInt, JsFakeInt, JsFakeInt, JsFakeInt>()
8487
.function("num_obs", &NeighborResults::num_obs, emscripten::return_value_policy::take_ownership())
8588
.function("num_neighbors", &NeighborResults::num_neighbors, emscripten::return_value_policy::take_ownership())
8689
.function("size", &NeighborResults::size, emscripten::return_value_policy::take_ownership())

src/NeighborIndex.h

Lines changed: 72 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -23,63 +23,105 @@ struct NeighborIndex {
2323

2424
std::unique_ptr<knncolle::Builder<std::int32_t, double, double, knncolle::SimpleMatrix<std::int32_t, double> > > create_builder(bool);
2525

26-
struct NeighborResults {
27-
typedef std::vector<std::vector<std::pair<int32_t, double> > > Neighbors;
26+
class NeighborResults {
27+
typedef std::vector<std::vector<std::pair<std::int32_t, double> > > Neighbors;
2828

29-
Neighbors neighbors;
29+
Neighbors my_neighbors;
3030

3131
public:
32-
NeighborResults(size_t n = 0) : neighbors(n) {}
32+
NeighborResults() = default;
3333

34-
NeighborResults(size_t n, uintptr_t runs, uintptr_t indices, uintptr_t distances) : neighbors(n) {
35-
auto rptr = reinterpret_cast<const int32_t*>(runs);
36-
auto iptr = reinterpret_cast<const int32_t*>(indices);
34+
NeighborResults(Neighbors neighbors) : my_neighbors(std::move(neighbors)) {}
35+
36+
NeighborResults(JsFakeInt n_raw, JsFakeInt runs_raw, JsFakeInt indices_raw, JsFakeInt distances_raw) :
37+
my_neighbors(js2int<I<decltype(my_neighbors.size())> >(n_raw))
38+
{
39+
const auto runs = js2int<std::uintptr_t>(runs_raw);
40+
auto rptr = reinterpret_cast<const std::int32_t*>(runs);
41+
42+
const auto indices = js2int<std::uintptr_t>(indices_raw);
43+
auto iptr = reinterpret_cast<const std::int32_t*>(indices);
44+
45+
const auto distances = js2int<std::uintptr_t>(distances_raw);
3746
auto dptr = reinterpret_cast<const double*>(distances);
3847

39-
for (size_t i = 0; i < n; ++i) {
40-
neighbors[i].reserve(rptr[i]);
41-
for (int32_t j = 0; j < rptr[i]; ++j, ++iptr, ++dptr) {
42-
neighbors[i].emplace_back(*iptr, *dptr);
48+
const auto n = my_neighbors.size();
49+
for (I<decltype(n)> i = 0; i < n; ++i) {
50+
const auto run = rptr[i];
51+
auto& nn = my_neighbors[i];
52+
nn.reserve(run);
53+
54+
for (I<decltype(run)> j = 0; j < run; ++j) {
55+
nn.emplace_back(iptr[j], dptr[j]);
4356
}
57+
58+
iptr += run;
59+
dptr += run;
4460
}
4561
}
4662

4763
public:
48-
double size(int32_t truncate) const {
49-
size_t out = 0;
50-
size_t long_truncate = truncate;
51-
for (const auto& current : neighbors) {
52-
out += std::min(long_truncate, current.size());
64+
Neighbors& neighbors() {
65+
return my_neighbors;
66+
}
67+
68+
const Neighbors& neighbors() const {
69+
return my_neighbors;
70+
}
71+
72+
public:
73+
JsFakeInt size(JsFakeInt truncate_raw) const {
74+
std::size_t out = 0;
75+
if (truncate_raw < 0) {
76+
for (const auto& current : my_neighbors) {
77+
out = sanisizer::sum<std::size_t>(out, current.size());
78+
}
79+
} else {
80+
const auto truncate = js2int<std::size_t>(truncate_raw);
81+
for (const auto& current : my_neighbors) {
82+
out = sanisizer::sum<std::size_t>(out, sanisizer::min(truncate, current.size()));
83+
}
5384
}
54-
return static_cast<double>(out);
85+
return int2js(out);
5586
}
5687

57-
double num_obs() const {
58-
return static_cast<double>(neighbors.size());
88+
JsFakeInt num_obs() const {
89+
return int2js(my_neighbors.size());
5990
}
6091

61-
double num_neighbors() const {
62-
return (neighbors.empty() ? 0 : neighbors.front().size());
92+
JsFakeInt num_neighbors() const {
93+
return int2js(my_neighbors.empty() ? 0 : my_neighbors.front().size());
6394
}
6495

65-
void serialize(uintptr_t runs, uintptr_t indices, uintptr_t distances, int32_t truncate) const {
96+
void serialize(JsFakeInt runs_raw, JsFakeInt indices_raw, JsFakeInt distances_raw, JsFakeInt truncate_raw) const {
97+
const auto runs = js2int<std::uintptr_t>(runs_raw);
6698
auto rptr = reinterpret_cast<int32_t*>(runs);
99+
100+
const auto indices = js2int<std::uintptr_t>(indices_raw);
67101
auto iptr = reinterpret_cast<int32_t*>(indices);
102+
103+
const auto distances = js2int<std::uintptr_t>(distances_raw);
68104
auto dptr = reinterpret_cast<double*>(distances);
69105

70-
size_t long_truncate = truncate;
71-
for (const auto& current : neighbors) {
72-
size_t nkeep = std::min(long_truncate, current.size());
106+
const bool do_truncate = truncate_raw >= 0;
107+
const auto truncate = (do_truncate ? js2int<std::size_t>(truncate_raw) : 0);
108+
109+
for (const auto& current : my_neighbors) {
110+
auto nkeep = current.size();
111+
if (do_truncate && truncate < nkeep) {
112+
nkeep = truncate;
113+
}
73114
*rptr = nkeep;
74115
++rptr;
75116

76-
for (int32_t i = 0; i < nkeep; ++i) {
117+
for (I<decltype(nkeep)> i = 0; i < nkeep; ++i) {
77118
const auto& x = current[i];
78-
*iptr = x.first;
79-
*dptr = x.second;
80-
++iptr;
81-
++dptr;
119+
iptr[i] = x.first;
120+
dptr[i] = x.second;
82121
}
122+
123+
iptr += nkeep;
124+
dptr += nkeep;
83125
}
84126
}
85127
};

src/NumericMatrix.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ JsFakeInt NumericMatrix::ncol_js() const {
4343
return int2js(my_ptr->ncol());
4444
}
4545

46-
void NumericMatrix::row(JsFakeInt r_raw, std::uintptr_t values) {
46+
void NumericMatrix::row(JsFakeInt r_raw, JsFakeInt values_raw) {
47+
const auto values = js2int<std::uintptr_t>(values_raw);
4748
MatrixValue* buffer = reinterpret_cast<MatrixValue*>(values);
4849
if (!my_by_row) {
4950
my_by_row = my_ptr->dense_row();
@@ -53,7 +54,8 @@ void NumericMatrix::row(JsFakeInt r_raw, std::uintptr_t values) {
5354
return;
5455
}
5556

56-
void NumericMatrix::column(JsFakeInt c_raw, std::uintptr_t values) {
57+
void NumericMatrix::column(JsFakeInt c_raw, JsFakeInt values_raw) {
58+
const auto values = js2int<std::uintptr_t>(values_raw);
5759
MatrixValue* buffer = reinterpret_cast<MatrixValue*>(values);
5860
if (!my_by_column) {
5961
my_by_column = my_ptr->dense_column();

src/NumericMatrix.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ class NumericMatrix {
3232
// Not thread-safe! by_row and by_column are initialized
3333
// on demand when particular rows and columns are requested
3434
// in Javascript. Don't use these functions from C++.
35-
void row(JsFakeInt r, std::uintptr_t values);
35+
void row(JsFakeInt r, JsFakeInt values_raw);
3636

37-
void column(JsFakeInt c, std::uintptr_t values);
37+
void column(JsFakeInt c, JsFakeInt values_raw);
3838

3939
public:
4040
const std::shared_ptr<const tatami::Matrix<MatrixValue, MatrixIndex> >& ptr() const;

src/aggregate_across_cells.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ class AggregateAcrossCellsResults {
3030
return emscripten::val(emscripten::typed_memory_view(my_ngenes, my_store.sums[i].data()));
3131
}
3232

33-
void all_sums(std::uintptr_t output) const {
34-
auto optr = reinterpret_cast<double*>(output);
33+
void all_sums(JsFakeInt output_raw) const {
34+
auto optr = reinterpret_cast<double*>(js2int<std::uintptr_t>(output_raw));
3535
for (const auto& ss : my_store.sums) {
3636
std::copy_n(ss.begin(), my_ngenes, optr);
3737
optr += my_ngenes;
@@ -43,19 +43,19 @@ class AggregateAcrossCellsResults {
4343
return emscripten::val(emscripten::typed_memory_view(my_ngenes, my_store.detected[i].data()));
4444
}
4545

46-
void all_detected(std::uintptr_t output) const {
47-
auto optr = reinterpret_cast<double*>(output);
46+
void all_detected(JsFakeInt output_raw) const {
47+
auto optr = reinterpret_cast<double*>(js2int<std::uintptr_t>(output_raw));
4848
for (const auto& ds : my_store.detected) {
4949
std::copy_n(ds.begin(), my_ngenes, optr);
5050
optr += my_ngenes;
5151
}
5252
}
5353
};
5454

55-
AggregateAcrossCellsResults aggregate_across_cells(const NumericMatrix& mat, std::uintptr_t factor, bool average, JsFakeInt nthreads_raw) {
55+
AggregateAcrossCellsResults aggregate_across_cells(const NumericMatrix& mat, JsFakeInt factor_raw, bool average, JsFakeInt nthreads_raw) {
5656
scran_aggregate::AggregateAcrossCellsOptions aopt;
5757
aopt.num_threads = js2int<int>(nthreads_raw);
58-
auto fptr = reinterpret_cast<const std::int32_t*>(factor);
58+
auto fptr = reinterpret_cast<const std::int32_t*>(js2int<std::uintptr_t>(factor_raw));
5959
auto store = scran_aggregate::aggregate_across_cells<double, double>(*mat, fptr, aopt);
6060

6161
if (average) {

src/build_snn_graph.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
#include "NeighborIndex.h"
77
#include "build_snn_graph.h"
88

9-
BuildSnnGraphResult build_snn_graph(const NeighborResults& neighbors, std::string scheme, int32_t nthreads) {
9+
BuildSnnGraphResult build_snn_graph(const NeighborResults& neighbors, std::string scheme, JsFakeInt nthreads_raw) {
1010
scran_graph_cluster::BuildSnnGraphOptions opt;
11-
opt.num_threads = nthreads;
11+
opt.num_threads = js2int<int>(nthreads_raw);
1212

1313
if (scheme == "rank") {
1414
opt.weighting_scheme = scran_graph_cluster::SnnWeightScheme::RANKED;
@@ -20,7 +20,7 @@ BuildSnnGraphResult build_snn_graph(const NeighborResults& neighbors, std::strin
2020
throw std::runtime_error("no known weighting scheme '" + scheme + "'");
2121
}
2222

23-
return BuildSnnGraphResult(scran_graph_cluster::build_snn_graph(neighbors.neighbors, opt));
23+
return BuildSnnGraphResult(scran_graph_cluster::build_snn_graph(neighbors.neighbors(), opt));
2424
}
2525

2626
EMSCRIPTEN_BINDINGS(build_snn_graph) {

src/cbind.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@
99

1010
#include "tatami/tatami.hpp"
1111

12-
NumericMatrix cbind(JsFakeInt n_raw, std::uintptr_t mats) {
13-
const auto n = js2int<std::size_t>(n_raw);
12+
NumericMatrix cbind(JsFakeInt n_raw, JsFakeInt mats_raw) {
13+
const auto mat_ptrs = convert_array_of_offsets<const NumericMatrix*>(n_raw, mats_raw);
14+
const auto n = mat_ptrs.size();
1415
if (n == 0) {
1516
throw std::runtime_error("need at least one matrix to cbind");
1617
}
1718

18-
const auto mat_ptrs = convert_array_of_offsets<const NumericMatrix*>(n, mats);
1919
std::vector<std::shared_ptr<const tatami::Matrix<double, std::int32_t> > > collected;
2020
collected.reserve(mat_ptrs.size());
2121

@@ -36,13 +36,13 @@ NumericMatrix cbind(JsFakeInt n_raw, std::uintptr_t mats) {
3636
);
3737
}
3838

39-
NumericMatrix rbind(JsFakeInt n_raw, std::uintptr_t mats) {
40-
const auto n = js2int<std::size_t>(n_raw);
39+
NumericMatrix rbind(JsFakeInt n_raw, JsFakeInt mats_raw) {
40+
const auto mat_ptrs = convert_array_of_offsets<const NumericMatrix*>(n_raw, mats_raw);
41+
const auto n = mat_ptrs.size();
4142
if (n == 0) {
4243
throw std::runtime_error("need at least one matrix to rbind");
4344
}
4445

45-
const auto mat_ptrs = convert_array_of_offsets<const NumericMatrix*>(n, mats);
4646
std::vector<std::shared_ptr<const tatami::Matrix<double, std::int32_t> > > collected;
4747
collected.reserve(mat_ptrs.size());
4848

src/cluster_kmeans.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class ClusterKmeansResult {
5454
};
5555

5656
ClusterKmeansResult cluster_kmeans(
57-
std::uintptr_t mat,
57+
JsFakeInt mat_raw,
5858
JsFakeInt nr_raw,
5959
JsFakeInt nc_raw,
6060
JsFakeInt k_raw,
@@ -67,7 +67,11 @@ ClusterKmeansResult cluster_kmeans(
6767
JsFakeInt refine_hw_iterations_raw,
6868
JsFakeInt nthreads_raw
6969
) {
70-
kmeans::SimpleMatrix<std::int32_t, double> smat(js2int<std::size_t>(nr_raw), js2int<std::int32_t>(nc_raw), reinterpret_cast<const double*>(mat));
70+
kmeans::SimpleMatrix<std::int32_t, double> smat(
71+
js2int<std::size_t>(nr_raw),
72+
js2int<std::int32_t>(nc_raw),
73+
reinterpret_cast<const double*>(js2int<std::uintptr_t>(mat_raw))
74+
);
7175

7276
std::unique_ptr<kmeans::Initialize<std::int32_t, double, std::int32_t, double, I<decltype(smat)> > > iptr;
7377
if (init_method == "random") {

src/compute_clrm1_factors.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
#include "NumericMatrix.h"
44
#include "clrm1.hpp"
55

6-
void compute_clrm1_factors(const NumericMatrix& mat, std::uintptr_t output, JsFakeInt nthreads_raw) {
6+
void compute_clrm1_factors(const NumericMatrix& mat, JsFakeInt output_raw, JsFakeInt nthreads_raw) {
77
clrm1::Options opt;
88
opt.num_threads = js2int<int>(nthreads_raw);
9+
const auto output = js2int<std::uintptr_t>(output_raw);
910
clrm1::compute(*mat, opt, reinterpret_cast<double*>(output));
1011
}
1112

src/delayed.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,14 @@ void delayed_arithmetic_scalar(NumericMatrix& x, std::string op, bool right, dou
3838
x.reset_ptr(std::make_shared<tatami::DelayedUnaryIsometricOperation<double, double, std::int32_t> >(std::move(x.ptr()), std::move(operation)));
3939
}
4040

41-
void delayed_arithmetic_vector(NumericMatrix& x, std::string op, bool right, JsFakeInt margin_raw, std::uintptr_t ptr, JsFakeInt n_raw) {
41+
void delayed_arithmetic_vector(NumericMatrix& x, std::string op, bool right, JsFakeInt margin_raw, JsFakeInt ptr_raw, JsFakeInt n_raw) {
4242
const auto margin = js2int<int>(margin_raw);
4343
const auto n = js2int<std::size_t>(n_raw);
4444
if (!sanisizer::is_equal(n, margin == 0 ? x.nrow() : x.ncol())) {
4545
throw std::runtime_error("inappropriate length of vector for delayed arithmetic");
4646
}
47+
48+
const auto ptr = js2int<std::uintptr_t>(ptr_raw);
4749
auto input = reinterpret_cast<const double*>(ptr);
4850
std::vector<double> store(input, input + n);
4951

0 commit comments

Comments
 (0)