QLocale: make qstrnto(u)ll not have output arguments
That is, return everything in the return argument. On the SysV ABI, that means everything gets returned in registers, in both 32- and 64-bit platforms (unlike QtPrivate::ParsedNumber). There's a minor but perceptible performance improvement in parsing strings and byte arrays. Before: Parsed string "42" "1234" "-1548860221" Clock (ns) 16.673 18.878 25.517 CPU cycles 46.548 52.704 71.243 Instructions 201 233 331 After: Parsed string "42" "1234" "-1548860221" Clock (ns) 15.577 17.998 24.198 CPU cycles 43.491 49.942 67.552 Instructions 179 211 308 On my Core i7-1165G7 @ 2.80 GHz, the 22-23 instruction gain per iteration results in half the expected clock gain in runtime (22 / 2.8 GHz = 7.8 ns) because of a slightly lower instruction per cycle rate. That's acceptable because we need less speculative execution. Task-number: QTBUG-107788 Change-Id: I07ec23f3cb174fb197c3fffd17220fd64d473cc0 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io> (cherry picked from commit d50d34e5de7f5cf5e34243210e3df519974d7794) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
parent
1f92a4005c
commit
0ff4e7d4a3
@ -60,11 +60,9 @@ static bool parseIp4Internal(IPv4Address &address, const char *ptr, bool acceptL
|
||||
ptr[1] != '.' && ptr[1] != '\0')
|
||||
return false;
|
||||
|
||||
const char *endptr;
|
||||
bool ok;
|
||||
quint64 ll = qstrntoull(ptr, stop - ptr, &endptr, 0, &ok);
|
||||
auto [ll, endptr] = qstrntoull(ptr, stop - ptr, 0);
|
||||
quint32 x = ll;
|
||||
if (!ok || endptr == ptr || ll != x)
|
||||
if (!endptr || endptr == ptr || ll != x)
|
||||
return false;
|
||||
|
||||
if (*endptr == '.' || dotCount == 3) {
|
||||
@ -176,15 +174,13 @@ const QChar *parseIp6(IPv6Address &address, const QChar *begin, const QChar *end
|
||||
continue;
|
||||
}
|
||||
|
||||
const char *endptr;
|
||||
bool ok;
|
||||
quint64 ll = qstrntoull(ptr, stop - ptr, &endptr, 16, &ok);
|
||||
auto [ll, endptr] = qstrntoull(ptr, stop - ptr, 16);
|
||||
quint16 x = ll;
|
||||
|
||||
// Reject malformed fields:
|
||||
// - failed to parse
|
||||
// - too many hex digits
|
||||
if (!ok || endptr > ptr + 4)
|
||||
if (!endptr || endptr > ptr + 4)
|
||||
return begin + (ptr - buffer.data());
|
||||
|
||||
if (*endptr == '.') {
|
||||
|
@ -451,32 +451,35 @@ inline bool QStorageIterator::next()
|
||||
const char *const stop = ptr + len - 1;
|
||||
|
||||
// parse the line
|
||||
bool ok;
|
||||
mnt.mnt_freq = 0;
|
||||
mnt.mnt_passno = 0;
|
||||
|
||||
mnt.mount_id = qstrntoll(ptr, stop - ptr, const_cast<const char **>(&ptr), 10, &ok);
|
||||
if (!ok)
|
||||
auto r = qstrntoll(ptr, stop - ptr, 10);
|
||||
if (!r.ok())
|
||||
return false;
|
||||
mnt.mount_id = r.result;
|
||||
|
||||
int parent_id = qstrntoll(ptr, stop - ptr, const_cast<const char **>(&ptr), 10, &ok);
|
||||
r = qstrntoll(r.endptr, stop - r.endptr, 10);
|
||||
if (!r.ok())
|
||||
return false;
|
||||
int parent_id = r.result;
|
||||
Q_UNUSED(parent_id);
|
||||
if (!ok)
|
||||
return false;
|
||||
|
||||
int rdevmajor = qstrntoll(ptr, stop - ptr, const_cast<const char **>(&ptr), 10, &ok);
|
||||
if (!ok)
|
||||
return false;
|
||||
if (*ptr != ':')
|
||||
return false;
|
||||
int rdevminor = qstrntoll(ptr + 1, stop - ptr - 1, const_cast<const char **>(&ptr), 10, &ok);
|
||||
if (!ok)
|
||||
return false;
|
||||
mnt.rdev = makedev(rdevmajor, rdevminor);
|
||||
|
||||
if (*ptr != ' ')
|
||||
|
||||
r = qstrntoll(r.endptr, stop - r.endptr, 10);
|
||||
if (!r.ok())
|
||||
return false;
|
||||
if (*r.endptr != ':')
|
||||
return false;
|
||||
int rdevmajor = r.result;
|
||||
r = qstrntoll(r.endptr + 1, stop - r.endptr - 1, 10);
|
||||
if (!r.ok())
|
||||
return false;
|
||||
mnt.rdev = makedev(rdevmajor, r.result);
|
||||
|
||||
if (*r.endptr != ' ')
|
||||
return false;
|
||||
|
||||
ptr = const_cast<char *>(r.endptr);
|
||||
mnt.subvolume = ++ptr;
|
||||
ptr = parseMangledPath(ptr);
|
||||
if (!ptr)
|
||||
|
@ -4116,11 +4116,8 @@ qulonglong QLocaleData::stringToUnsLongLong(QStringView str, int base, bool *ok,
|
||||
|
||||
qlonglong QLocaleData::bytearrayToLongLong(QByteArrayView num, int base, bool *ok)
|
||||
{
|
||||
bool _ok;
|
||||
const char *endptr;
|
||||
const qlonglong l = qstrntoll(num.data(), num.size(), &endptr, base, &_ok);
|
||||
|
||||
if (!_ok || endptr == num.data()) {
|
||||
auto [l, endptr] = qstrntoll(num.data(), num.size(), base);
|
||||
if (!endptr) {
|
||||
if (ok != nullptr)
|
||||
*ok = false;
|
||||
return 0;
|
||||
@ -4146,11 +4143,8 @@ qlonglong QLocaleData::bytearrayToLongLong(QByteArrayView num, int base, bool *o
|
||||
|
||||
qulonglong QLocaleData::bytearrayToUnsLongLong(QByteArrayView num, int base, bool *ok)
|
||||
{
|
||||
bool _ok;
|
||||
const char *endptr;
|
||||
const qulonglong l = qstrntoull(num.data(), num.size(), &endptr, base, &_ok);
|
||||
|
||||
if (!_ok || endptr == num.data()) {
|
||||
auto [l, endptr] = qstrntoull(num.data(), num.size(), base);
|
||||
if (!endptr) {
|
||||
if (ok != nullptr)
|
||||
*ok = false;
|
||||
return 0;
|
||||
|
@ -191,11 +191,10 @@ void qt_doubleToAscii(double d, QLocaleData::DoubleForm form, int precision,
|
||||
// which case the missing digits are zeroes. In the 'e' case decptInTarget is always 1,
|
||||
// as variants of snprintf always generate numbers with one digit before the '.' then.
|
||||
// This is why the final decimal point is offset by 1, relative to the number after 'e'.
|
||||
bool ok;
|
||||
const char *endptr;
|
||||
decpt = qstrntoll(target.data() + eSign + 1, length - eSign - 1, &endptr, 10, &ok) + 1;
|
||||
Q_ASSERT(ok);
|
||||
Q_ASSERT(endptr - target.data() <= length);
|
||||
auto r = qstrntoll(target.data() + eSign + 1, length - eSign - 1, 10);
|
||||
decpt = r.result + 1;
|
||||
Q_ASSERT(r.ok());
|
||||
Q_ASSERT(r.endptr - target.data() <= length);
|
||||
} else {
|
||||
// No 'e' found, so it's the 'f' form. Variants of snprintf generate numbers with
|
||||
// potentially multiple digits before the '.', but without decimal exponent then. So we
|
||||
@ -423,36 +422,25 @@ static bool isDigitForBase(char d, int base)
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned long long
|
||||
qstrntoull(const char *begin, qsizetype size, const char **endptr, int base, bool *ok)
|
||||
QSimpleParsedNumber<qulonglong> qstrntoull(const char *begin, qsizetype size, int base)
|
||||
{
|
||||
const char *p = begin, *const stop = begin + size;
|
||||
while (p < stop && ascii_isspace(*p))
|
||||
++p;
|
||||
unsigned long long result = 0;
|
||||
if (p >= stop || *p == '-') {
|
||||
*ok = false;
|
||||
if (endptr)
|
||||
*endptr = begin;
|
||||
return result;
|
||||
}
|
||||
if (p >= stop || *p == '-')
|
||||
return { };
|
||||
const auto prefix = scanPrefix(*p == '+' ? p + 1 : p, stop, base);
|
||||
if (!prefix.base || prefix.next >= stop) {
|
||||
if (endptr)
|
||||
*endptr = begin;
|
||||
*ok = false;
|
||||
return 0;
|
||||
}
|
||||
if (!prefix.base || prefix.next >= stop)
|
||||
return { };
|
||||
|
||||
const auto res = std::from_chars(prefix.next, stop, result, prefix.base);
|
||||
*ok = res.ec == std::errc{};
|
||||
if (endptr)
|
||||
*endptr = res.ptr == prefix.next ? begin : res.ptr;
|
||||
return result;
|
||||
if (res.ec != std::errc{})
|
||||
return { };
|
||||
return { result, res.ptr == prefix.next ? begin : res.ptr };
|
||||
}
|
||||
|
||||
long long
|
||||
qstrntoll(const char *begin, qsizetype size, const char **endptr, int base, bool *ok)
|
||||
QSimpleParsedNumber<qlonglong> qstrntoll(const char *begin, qsizetype size, int base)
|
||||
{
|
||||
const char *p = begin, *const stop = begin + size;
|
||||
while (p < stop && ascii_isspace(*p))
|
||||
@ -467,30 +455,22 @@ qstrntoll(const char *begin, qsizetype size, const char **endptr, int base, bool
|
||||
const auto prefix = scanPrefix(p, stop, base);
|
||||
// Must check for digit, as from_chars() will accept a sign, which would be
|
||||
// a second sign, that we should reject.
|
||||
if (!prefix.base || prefix.next >= stop || !isDigitForBase(*prefix.next, prefix.base)) {
|
||||
if (endptr)
|
||||
*endptr = begin;
|
||||
*ok = false;
|
||||
return 0;
|
||||
}
|
||||
if (!prefix.base || prefix.next >= stop || !isDigitForBase(*prefix.next, prefix.base))
|
||||
return { };
|
||||
|
||||
long long result = 0;
|
||||
auto res = std::from_chars(prefix.next, stop, result, prefix.base);
|
||||
*ok = res.ec == std::errc{};
|
||||
if (negate && res.ec == std::errc::result_out_of_range) {
|
||||
// Maybe LLONG_MIN:
|
||||
unsigned long long check = 0;
|
||||
res = std::from_chars(prefix.next, stop, check, prefix.base);
|
||||
if (res.ec == std::errc{} && check + std::numeric_limits<long long>::min() == 0) {
|
||||
*ok = true;
|
||||
if (endptr)
|
||||
*endptr = res.ptr;
|
||||
return std::numeric_limits<long long>::min();
|
||||
}
|
||||
if (res.ec == std::errc{} && check + std::numeric_limits<long long>::min() == 0)
|
||||
return { std::numeric_limits<long long>::min(), res.ptr };
|
||||
return { };
|
||||
}
|
||||
if (endptr)
|
||||
*endptr = res.ptr == prefix.next ? begin : res.ptr;
|
||||
return negate && *ok ? -result : result;
|
||||
if (res.ec != std::errc{})
|
||||
return { };
|
||||
return { negate ? -result : result, res.ptr };
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
|
@ -26,6 +26,13 @@ enum StrayCharacterMode {
|
||||
WhitespacesAllowed
|
||||
};
|
||||
|
||||
template <typename T> struct QSimpleParsedNumber
|
||||
{
|
||||
T result;
|
||||
const char *endptr;
|
||||
bool ok() { return endptr; }
|
||||
};
|
||||
|
||||
// API note: this function can't process a number with more than 2.1 billion digits
|
||||
[[nodiscard]] double qt_asciiToDouble(const char *num, qsizetype numLen, bool &ok, int &processed,
|
||||
StrayCharacterMode strayCharMode = TrailingJunkProhibited);
|
||||
@ -81,10 +88,8 @@ template <typename UcsInt>
|
||||
return qstrntod(s00, len, se, ok);
|
||||
}
|
||||
|
||||
[[nodiscard]] qlonglong qstrntoll(const char *nptr, qsizetype size, const char **endptr,
|
||||
int base, bool *ok);
|
||||
[[nodiscard]] qulonglong qstrntoull(const char *nptr, qsizetype size, const char **endptr,
|
||||
int base, bool *ok);
|
||||
[[nodiscard]] QSimpleParsedNumber<qlonglong> qstrntoll(const char *nptr, qsizetype size, int base);
|
||||
[[nodiscard]] QSimpleParsedNumber<qulonglong> qstrntoull(const char *nptr, qsizetype size, int base);
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
|
@ -1071,11 +1071,9 @@ static QString winIso639LangName(LCID id)
|
||||
lang_code = QString::fromWCharArray(out);
|
||||
|
||||
if (!lang_code.isEmpty()) {
|
||||
const char *endptr;
|
||||
bool ok;
|
||||
const QByteArray latin1 = std::move(lang_code).toLatin1();
|
||||
const auto i = qstrntoull(latin1.data(), latin1.size(), &endptr, 16, &ok);
|
||||
if (ok && *endptr == '\0') {
|
||||
const auto [i, endptr] = qstrntoull(latin1.data(), latin1.size(), 16);
|
||||
if (endptr && *endptr == '\0') {
|
||||
switch (i) {
|
||||
case 0x814:
|
||||
result = u"nn"_s; // Nynorsk
|
||||
@ -1115,8 +1113,8 @@ static QByteArray getWinLocaleName(LCID id)
|
||||
result = langEnvVar;
|
||||
if (result == "C"
|
||||
|| (!result.isEmpty() && qt_splitLocaleName(QString::fromLocal8Bit(result)))) {
|
||||
bool ok = false; // See if we have a Windows locale code instead of a locale name:
|
||||
long id = qstrntoll(result.data(), result.size(), 0, 0, &ok);
|
||||
// See if we have a Windows locale code instead of a locale name:
|
||||
auto [id, ok] = qstrntoll(result.data(), result.size(), 0);
|
||||
if (!ok || id == 0 || id < INT_MIN || id > INT_MAX) // Assume real locale name
|
||||
return result;
|
||||
return winLangCodeToIsoName(int(id));
|
||||
|
@ -6767,14 +6767,14 @@ static int parse_field_width(const char *&c, qsizetype size)
|
||||
|
||||
// can't be negative - started with a digit
|
||||
// contains at least one digit
|
||||
const char *endp;
|
||||
bool ok;
|
||||
const qulonglong result = qstrntoull(c, size, &endp, 10, &ok);
|
||||
auto [result, endp] = qstrntoull(c, size, 10);
|
||||
c = endp;
|
||||
if (!endp)
|
||||
return false;
|
||||
// preserve Qt 5.5 behavior of consuming all digits, no matter how many
|
||||
while (c < stop && qIsDigit(*c))
|
||||
++c;
|
||||
return ok && result < qulonglong(std::numeric_limits<int>::max()) ? int(result) : 0;
|
||||
return result < qulonglong(std::numeric_limits<int>::max()) ? int(result) : 0;
|
||||
}
|
||||
|
||||
enum LengthMod { lm_none, lm_hh, lm_h, lm_l, lm_ll, lm_L, lm_j, lm_z, lm_t };
|
||||
|
@ -392,27 +392,28 @@ static int parsePosixTime(const char *begin, const char *end)
|
||||
int hour, min = 0, sec = 0;
|
||||
|
||||
const int maxHour = 137; // POSIX's extended range.
|
||||
bool ok = false;
|
||||
const char *cut = begin;
|
||||
hour = qstrntoll(begin, end - begin, &cut, 10, &ok);
|
||||
if (!ok || hour < -maxHour || hour > maxHour || cut > begin + 2)
|
||||
auto r = qstrntoll(begin, end - begin, 10);
|
||||
hour = r.result;
|
||||
if (!r.ok() || hour < -maxHour || hour > maxHour || r.endptr > begin + 2)
|
||||
return INT_MIN;
|
||||
begin = cut;
|
||||
begin = r.endptr;
|
||||
if (begin < end && *begin == ':') {
|
||||
// minutes
|
||||
++begin;
|
||||
min = qstrntoll(begin, end - begin, &cut, 10, &ok);
|
||||
if (!ok || min < 0 || min > 59 || cut > begin + 2)
|
||||
r = qstrntoll(begin, end - begin, 10);
|
||||
min = r.result;
|
||||
if (!r.ok() || min < 0 || min > 59 || r.endptr > begin + 2)
|
||||
return INT_MIN;
|
||||
|
||||
begin = cut;
|
||||
begin = r.endptr;
|
||||
if (begin < end && *begin == ':') {
|
||||
// seconds
|
||||
++begin;
|
||||
sec = qstrntoll(begin, end - begin, &cut, 10, &ok);
|
||||
if (!ok || sec < 0 || sec > 59 || cut > begin + 2)
|
||||
r = qstrntoll(begin, end - begin, 10);
|
||||
sec = r.result;
|
||||
if (!r.ok() || sec < 0 || sec > 59 || r.endptr > begin + 2)
|
||||
return INT_MIN;
|
||||
begin = cut;
|
||||
begin = r.endptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,22 +111,18 @@ private:
|
||||
#else
|
||||
// can't use qEnvironmentVariableIntValue (reentrancy)
|
||||
const char *seedstr = getenv("QT_HASH_SEED");
|
||||
const char *endptr = nullptr;
|
||||
bool ok = false;
|
||||
int seed = 0;
|
||||
if (seedstr)
|
||||
seed = qstrntoll(seedstr, strlen(seedstr), &endptr, 10, &ok);
|
||||
if (ok && endptr != seedstr + strlen(seedstr))
|
||||
ok = false;
|
||||
if (ok) {
|
||||
if (seed) {
|
||||
// can't use qWarning here (reentrancy)
|
||||
fprintf(stderr, "QT_HASH_SEED: forced seed value is not 0; ignored.\n");
|
||||
}
|
||||
if (seedstr) {
|
||||
auto r = qstrntoll(seedstr, strlen(seedstr), 10);
|
||||
if (r.endptr == seedstr + strlen(seedstr)) {
|
||||
if (r.result) {
|
||||
// can't use qWarning here (reentrancy)
|
||||
fprintf(stderr, "QT_HASH_SEED: forced seed value is not 0; ignored.\n");
|
||||
}
|
||||
|
||||
// we don't have to store to the seed, since it's pre-initialized by
|
||||
// the compiler to zero
|
||||
return result;
|
||||
// we don't have to store to the seed, since it's pre-initialized by
|
||||
// the compiler to zero
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// update the full seed
|
||||
|
@ -404,19 +404,18 @@ static QVersionNumber from_string(QLatin1StringView string, qsizetype *suffixInd
|
||||
QVarLengthArray<int, 32> seg;
|
||||
|
||||
const char *start = string.begin();
|
||||
const char *end = start;
|
||||
const char *lastGoodEnd = start;
|
||||
const char *endOfString = string.end();
|
||||
|
||||
do {
|
||||
bool ok = false;
|
||||
const qulonglong value = qstrntoull(start, endOfString - start, &end, 10, &ok);
|
||||
if (!ok || value > qulonglong(std::numeric_limits<int>::max()))
|
||||
// parsing as unsigned so a minus sign is rejected
|
||||
auto [value, end] = qstrntoull(start, endOfString - start, 10);
|
||||
if (!end || value > qulonglong(std::numeric_limits<int>::max()))
|
||||
break;
|
||||
seg.append(int(value));
|
||||
start = end + 1;
|
||||
lastGoodEnd = end;
|
||||
} while (start < endOfString && end < endOfString && *end == '.');
|
||||
} while (start < endOfString && *lastGoodEnd == '.');
|
||||
|
||||
if (suffixIndex)
|
||||
*suffixIndex = lastGoodEnd - string.begin();
|
||||
|
Loading…
x
Reference in New Issue
Block a user