summaryrefslogtreecommitdiff
path: root/src/string.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/string.c')
-rw-r--r--src/string.c68
1 files changed, 58 insertions, 10 deletions
diff --git a/src/string.c b/src/string.c
index 4abd694..06066ce 100644
--- a/src/string.c
+++ b/src/string.c
@@ -980,6 +980,7 @@ EXPORT_SYMBOL double HX_strtod_unit(const char *s, char **out_end, unsigned int
if (end == s) {
if (out_end != nullptr)
*out_end = end;
+ errno = 0;
return q;
}
while (HX_isspace(*end))
@@ -988,10 +989,12 @@ EXPORT_SYMBOL double HX_strtod_unit(const char *s, char **out_end, unsigned int
if (pwr == 0) {
if (out_end != nullptr)
*out_end = const_cast(char *, end);
+ errno = 0;
return q;
}
if (out_end != nullptr)
*out_end = const_cast(char *, end + 1);
+ errno = 0;
return q * pow(exponent, pwr);
}
@@ -1001,17 +1004,45 @@ EXPORT_SYMBOL unsigned long long HX_strtoull_unit(const char *s,
char *end;
unsigned long long ipart;
unsigned int pwr = 0;
+ bool neg = false;
while (HX_isspace(*s))
++s;
+ if (*s == '-') {
+ /*
+ * "-5k": While (-5ULL) * 1000 is the same as (-5000ULL) under
+ * modulo arithmetic, the expression `ipart >= ULLONG_MAX /
+ * exponent` depends on seeing the true value (5 rather than
+ * (-5ULL).)
+ */
+ neg = true;
+ ++s;
+ }
+ errno = 0;
ipart = strtoull(s, &end, 10);
+ if (ipart == ULLONG_MAX && errno == ERANGE)
+ return ipart;
if (*end == '.') {
double q = HX_strtod_unit(s, out_end, exponent);
bool lo_ok = q >= nextafter(-static_cast(double, ULLONG_MAX), 0);
bool hi_ok = q <= nextafter(static_cast(double, ULLONG_MAX), 0);
- if (!hi_ok || !lo_ok)
+ if (!hi_ok || !lo_ok) {
+ errno = ERANGE;
return ULLONG_MAX;
- return q;
+ }
+ /*
+ * https://eel.is/c++draft/conv.fpint: values unrepresentable
+ * in the target type (such as forcing -5.2f into a uint) is
+ * UB. Thus check for range and apply the negation after the
+ * conversion to ULL.
+ */
+ if (q > ULLONG_MAX) {
+ errno = ERANGE;
+ return ULLONG_MAX;
+ }
+ unsigned long long r = q;
+ errno = 0;
+ return neg ? -r : r;
}
if (exponent == 0)
exponent = 1000;
@@ -1021,7 +1052,8 @@ EXPORT_SYMBOL unsigned long long HX_strtoull_unit(const char *s,
if (pwr == 0) {
if (out_end != nullptr)
*out_end = end;
- return ipart;
+ errno = 0;
+ return neg ? -ipart: ipart;
}
if (out_end != nullptr)
*out_end = const_cast(char *, end + 1);
@@ -1032,11 +1064,13 @@ EXPORT_SYMBOL unsigned long long HX_strtoull_unit(const char *s,
}
ipart *= exponent;
}
- return ipart;
+ errno = 0;
+ return neg ? -ipart : ipart;
}
-#define SECONDS_PER_YEAR 31557600
-#define SECONDS_PER_MONTH 2629800
+/* Numbers also used by systemd — the focus is on longterm averages */
+#define SECONDS_PER_YEAR 31557600 /* 365.25 days */
+#define SECONDS_PER_MONTH 2629800 /* 1/12th of that year = 30.4375 days */
#define NSEC_PER_SECOND 1000000000ULL
static const struct {
@@ -1071,7 +1105,6 @@ static const struct {
{"µs", 3, 0, 1000},
{"nsec", 4, 0, 1},
{"ns", 2, 0, 1},
- {"", 0, 1, NSEC_PER_SECOND},
};
static unsigned long long HX_strtoull_time(const char *s, char **out_end, bool nsec)
@@ -1081,10 +1114,13 @@ static unsigned long long HX_strtoull_time(const char *s, char **out_end, bool n
while (*s != '\0') {
while (HX_isspace(*s))
++s;
+ const char *numbegin = s;
if (*s == '-')
break;
char *end = nullptr;
unsigned long long num = strtoull(s, &end, 10);
+ if (num == ULLONG_MAX && errno == ERANGE)
+ return num;
double frac = 0;
bool have_frac = *end == '.';
if (have_frac)
@@ -1100,13 +1136,25 @@ static unsigned long long HX_strtoull_time(const char *s, char **out_end, bool n
time_multiplier[i].len) == 0 &&
!HX_isalpha(s[time_multiplier[i].len]))
break;
- if (i == ARRAY_SIZE(time_multiplier))
+ if (i == ARRAY_SIZE(time_multiplier)) {
+ if ((!have_frac && num == 0) || (have_frac && frac == 0))
+ /* 0 is the same no matter what unit, take it */
+ continue;
+ s = numbegin;
break;
+ }
unsigned long long mult = nsec ? time_multiplier[i].ns_mult : time_multiplier[i].s_mult;
- if (have_frac)
+ if (have_frac) {
quant += frac * mult;
- else
+ } else {
+ if (mult > 0 && num >= ULLONG_MAX / mult) {
+ if (out_end != nullptr)
+ *out_end = const_cast(char *, numbegin);
+ errno = ERANGE;
+ return ULLONG_MAX;
+ }
quant += num * mult;
+ }
s += time_multiplier[i].len;
}
if (out_end != nullptr)