Skip to content

Commit 256b0c4

Browse files
committed
Add checked transform variant of convert()
1 parent 4e18fe4 commit 256b0c4

1 file changed

Lines changed: 69 additions & 68 deletions

File tree

jsrc/conversions.cpp

Lines changed: 69 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <algorithm>
77
#include <limits>
88
#include <numeric>
9+
#include <optional>
910

1011
#include "array.hpp"
1112
extern "C" {
@@ -14,11 +15,12 @@ extern "C" {
1415

1516
#define CVCASE(a, b) (((a) << 3) + (b)) // The main cases fit in low 8 bits of mask
1617

17-
// FIEQ are used in bcvt, where FUZZ may be set to 0 to ensure only exact values are demoted to lower precision
18-
#define FIEQ(u, v, fuzz) \
19-
(ABS((u) - (v)) <= \
20-
fuzz * \
21-
ABS(v)) // used when v is known to be exact integer. It's close enough, maybe ULP too small on the high end
18+
// fuzzy_equal() is used in bcvt, where FUZZ may be set to 0 to ensure only exact values are demoted to lower precision
19+
// used when v is known to be exact integer. It's close enough, maybe ULP too small on the high end
20+
[[nodiscard]] static auto
21+
fuzzy_equal(double u, double v, double fuzz) -> bool {
22+
return std::abs(u - v) <= fuzz * std::abs(v);
23+
}
2224

2325
template <typename T, typename V>
2426
[[nodiscard]] constexpr auto
@@ -32,88 +34,84 @@ in_range() -> bool {
3234
return in_range<T>(std::numeric_limits<V>::min()) && in_range<T>(std::numeric_limits<V>::max());
3335
}
3436

35-
template <typename From, typename To>
37+
template <typename T>
38+
struct is_optional : std::false_type {};
39+
40+
template <typename T>
41+
struct is_optional<std::optional<T>> : std::true_type {};
42+
43+
template <typename T>
3644
[[nodiscard]] auto
37-
convert(J jt, array w, void *yv) -> bool {
45+
value_if(bool cond, T value) -> std::optional<T> {
46+
return cond ? std::optional(value) : std::nullopt;
47+
}
48+
49+
template <typename From, typename To, typename Transform>
50+
[[nodiscard]] auto
51+
convert(J jt, array w, void *yv, Transform t) -> bool {
3852
auto *v = pointer_to_values<From>(w);
39-
if constexpr (!in_range<To, From>()) {
40-
// TODO: replace with short circuiting solution
41-
auto *out = static_cast<To *>(yv);
42-
return out + AN(w) == std::copy_if(v, v + AN(w), out, [](auto v) { return in_range<To>(v); });
53+
auto *result = static_cast<To *>(yv);
54+
if constexpr (is_optional<decltype(t(*v))>::value) {
55+
for (int64_t i = 0; i < AN(w); ++i) {
56+
auto opt = t(v[i]);
57+
if (!opt) return false;
58+
result[i] = opt.value();
59+
}
60+
} else {
61+
std::transform(v, v + AN(w), result, t);
4362
}
44-
std::copy(v, v + AN(w), static_cast<To *>(yv));
4563
return true;
4664
}
4765

48-
template <typename From, typename To, typename Transform>
66+
template <typename From, typename To>
4967
[[nodiscard]] auto
50-
convert(J jt, array w, void *yv, Transform t) -> bool {
68+
convert(J jt, array w, void *yv) -> bool {
69+
if constexpr (!in_range<To, From>()) {
70+
return convert<From, To>(jt, w, yv, [](auto v) { return value_if(in_range<To>(v), v); });
71+
}
5172
auto *v = pointer_to_values<From>(w);
52-
std::transform(v, v + AN(w), static_cast<To *>(yv), t);
73+
std::copy(v, v + AN(w), static_cast<To *>(yv));
5374
return true;
5475
}
5576

5677
template <>
5778
[[nodiscard]] auto
5879
convert<D, bool>(J jt, array w, void *yv, D fuzz) -> bool {
59-
auto n = AN(w);
60-
auto *v = pointer_to_values<double>(w);
61-
auto *x = static_cast<B *>(yv);
62-
DQ(n, auto p = *v++; if (p < -2 || 2 < p) return false; // handle infinities
63-
I val = 2;
64-
val = (p == 0) ? 0 : val;
65-
val = FIEQ(p, 1.0, fuzz) ? 1 : val;
66-
if (val == 2) return false;
67-
*x++ = (B)val;)
68-
return true;
80+
auto const infinity = [](auto p) { return p < -2 || 2 < p; };
81+
return convert<D, bool>(jt, w, yv, [&](auto p) {
82+
return value_if(!infinity(p) && (p == 0.0 || fuzzy_equal(p, 1.0, fuzz)), p != 0.0);
83+
});
6984
}
7085

7186
template <>
7287
[[nodiscard]] auto
7388
convert<D, I>(J jt, array w, void *yv, D fuzz) -> bool {
74-
auto n = AN(w);
75-
auto *v = pointer_to_values<double>(w);
76-
auto *x = static_cast<I *>(yv);
77-
for (int64_t i = 0; i < n; ++i) {
78-
auto const p = v[i];
89+
return convert<D, I>(jt, w, yv, [&](auto p) -> std::optional<I> {
7990
auto const q = jround(p);
80-
I rq = static_cast<I>(q);
81-
if (!(p == q || FIEQ(p, q, fuzz))) {
82-
return 0; // must equal int, possibly out of range
91+
if (!(p == q || fuzzy_equal(p, q, fuzz))) {
92+
return std::nullopt; // must equal int, possibly out of range
8393
}
8494
// out-of-range values don't convert, handle separately
8595
if (p < static_cast<D> IMIN) {
86-
if (!(p >= IMIN * (1 + fuzz))) return false;
87-
rq = IMIN;
96+
return value_if(p >= IMIN * (1 + fuzz), IMIN);
8897
} // if tolerantly < IMIN, error; else take IMIN
8998
else if (p >= FLIMAX) {
90-
if (!(p <= -static_cast<D> IMIN * (1 + fuzz))) return false;
91-
rq = IMAX;
99+
return value_if(p <= -static_cast<D> IMIN * (1 + fuzz), IMAX);
92100
} // if tolerantly > IMAX, error; else take IMAX
93-
*x++ = rq;
94-
}
95-
return true;
101+
return q;
102+
});
96103
}
97104

98105
template <>
99106
[[nodiscard]] auto
100107
convert<Z, D>(J jt, array w, void *yv, D fuzz) -> bool {
101-
auto const n = AN(w);
102-
auto const *v = pointer_to_values<Z>(w);
103-
auto *x = static_cast<D *>(yv);
104-
if (fuzz != 0.0)
105-
DQ(
106-
n, auto d = std::abs(v->im); if (d != inf && d <= fuzz * std::abs(v->re)) {
107-
*x++ = v->re;
108-
v++;
109-
} else return false;)
110-
else
111-
DQ(
112-
n, if (!v->im) {
113-
*x++ = v->re;
114-
v++;
115-
} else return false;);
116-
return true;
108+
if (fuzz != 0.0) {
109+
return convert<Z, D>(jt, w, yv, [&](auto const &v) {
110+
auto const d = std::abs(v.im);
111+
return value_if(d != inf && d <= fuzz * std::abs(v.re), v.re);
112+
});
113+
}
114+
return convert<Z, D>(jt, w, yv, [](auto v) { return value_if(!v.im, v.re); });
117115
}
118116

119117
template <>
@@ -206,10 +204,10 @@ convert<D, X>(J jt, array w, void *yv, I mode) -> bool {
206204
template <>
207205
[[nodiscard]] auto
208206
convert<X, bool>(J jt, array w, void *yv) -> bool {
209-
auto *v = pointer_to_values<X>(w);
210-
auto *x = static_cast<B *>(yv);
211-
DO(AN(w), array q = v[i]; I e = pointer_to_values<int64_t>(q)[0]; if ((AN(q) ^ 1) | (e & -2)) return false; x[i] = (B)e;);
212-
return true;
207+
return convert<X, bool>(jt, w, yv, [](auto q) {
208+
auto const e = pointer_to_values<int64_t>(q)[0];
209+
return value_if(!((AN(q) ^ 1) | (e & -2)), e);
210+
});
213211
}
214212

215213
template <typename T>
@@ -316,7 +314,10 @@ convert<Q, D>(J jt, array w, void *yv) -> bool {
316314
auto const add_digits = [&](auto n, auto v) {
317315
auto f = 1.0;
318316
auto d = 0.0;
319-
DO(n, d += f * v[i]; f *= xb;);
317+
std::for_each(v, v + n, [&](auto i) {
318+
d += f * i;
319+
f *= xb;
320+
});
320321
return d;
321322
};
322323

@@ -357,10 +358,7 @@ convert<Q, D>(J jt, array w, void *yv) -> bool {
357358
template <>
358359
[[nodiscard]] auto
359360
convert<Q, X>(J jt, array w, void *yv) -> bool {
360-
auto *v = pointer_to_values<Q>(w);
361-
auto *x = static_cast<X *>(yv);
362-
DQ(AN(w), if (!(jtequ(jt, iv1, v->d))) return 0; *x++ = v->n; ++v;);
363-
return !jt->jerr;
361+
return convert<Q, X>(jt, w, yv, [&](auto v) { return value_if(jtequ(jt, iv1, v.d), v.n); }) && !jt->jerr;
364362
}
365363

366364
template <typename T>
@@ -641,14 +639,17 @@ jtxco1(J jt, array w) -> array {
641639
auto
642640
jtxco2(J jt, array a, array w) -> array {
643641
ASSERT(AT(w) & DENSE, EVNONCE);
644-
I j = 0;
645-
RE(j = jti0(jt, a));
642+
I j = jti0(jt, a);
643+
if (jt->jerr != 0) return nullptr;
646644
switch (j) {
647645
case -2: return jtaslash1(jt, CDIV, w);
648646
case -1: return jtbcvt(jt, 1, w);
649647
case 1: return jtxco1(jt, w);
650648
case 2:
651-
if ((AT(w) & RAT) == 0) RZ(w = jtcvt(jt, RAT, w));
649+
if ((AT(w) & RAT) == 0) {
650+
w = jtcvt(jt, RAT, w);
651+
if (!w) return nullptr;
652+
}
652653
{
653654
auto const n = AN(w);
654655
auto const r = AR(w);

0 commit comments

Comments
 (0)