diff options
author | Thiago Macieira <thiago.macieira@intel.com> | 2022-10-27 15:55:42 -0700 |
---|---|---|
committer | Thiago Macieira <thiago.macieira@intel.com> | 2022-11-16 16:15:31 -0800 |
commit | d50d34e5de7f5cf5e34243210e3df519974d7794 (patch) | |
tree | ff86a644a6dc271da7817767e1f7668d7b82ed25 /src | |
parent | 649dccf57b18626f6a0790d46e5e619e4e603078 (diff) |
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.
Pick-to: 6.4
Task-number: QTBUG-107788
Change-Id: I07ec23f3cb174fb197c3fffd17220fd64d473cc0
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Diffstat (limited to 'src')
-rw-r--r-- | src/corelib/io/qipaddress.cpp | 12 | ||||
-rw-r--r-- | src/corelib/io/qstorageinfo_unix.cpp | 29 | ||||
-rw-r--r-- | src/corelib/text/qlocale.cpp | 14 | ||||
-rw-r--r-- | src/corelib/text/qlocale_tools.cpp | 62 | ||||
-rw-r--r-- | src/corelib/text/qlocale_tools_p.h | 13 | ||||
-rw-r--r-- | src/corelib/text/qlocale_win.cpp | 10 | ||||
-rw-r--r-- | src/corelib/text/qstring.cpp | 8 | ||||
-rw-r--r-- | src/corelib/time/qtimezoneprivate_tz.cpp | 23 | ||||
-rw-r--r-- | src/corelib/tools/qhash.cpp | 26 | ||||
-rw-r--r-- | src/corelib/tools/qversionnumber.cpp | 9 |
10 files changed, 89 insertions, 117 deletions
diff --git a/src/corelib/io/qipaddress.cpp b/src/corelib/io/qipaddress.cpp index eeb3d79b06..feed38bef6 100644 --- a/src/corelib/io/qipaddress.cpp +++ b/src/corelib/io/qipaddress.cpp @@ -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 == '.') { diff --git a/src/corelib/io/qstorageinfo_unix.cpp b/src/corelib/io/qstorageinfo_unix.cpp index 5f3fa32482..be3bf0252d 100644 --- a/src/corelib/io/qstorageinfo_unix.cpp +++ b/src/corelib/io/qstorageinfo_unix.cpp @@ -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); - Q_UNUSED(parent_id); - if (!ok) + r = qstrntoll(r.endptr, stop - r.endptr, 10); + if (!r.ok()) return false; + int parent_id = r.result; + Q_UNUSED(parent_id); - int rdevmajor = qstrntoll(ptr, stop - ptr, const_cast<const char **>(&ptr), 10, &ok); - if (!ok) + r = qstrntoll(r.endptr, stop - r.endptr, 10); + if (!r.ok()) return false; - if (*ptr != ':') + if (*r.endptr != ':') return false; - int rdevminor = qstrntoll(ptr + 1, stop - ptr - 1, const_cast<const char **>(&ptr), 10, &ok); - if (!ok) + int rdevmajor = r.result; + r = qstrntoll(r.endptr + 1, stop - r.endptr - 1, 10); + if (!r.ok()) return false; - mnt.rdev = makedev(rdevmajor, rdevminor); + mnt.rdev = makedev(rdevmajor, r.result); - if (*ptr != ' ') + if (*r.endptr != ' ') return false; + ptr = const_cast<char *>(r.endptr); mnt.subvolume = ++ptr; ptr = parseMangledPath(ptr); if (!ptr) diff --git a/src/corelib/text/qlocale.cpp b/src/corelib/text/qlocale.cpp index a6ba1931cb..16c80dd667 100644 --- a/src/corelib/text/qlocale.cpp +++ b/src/corelib/text/qlocale.cpp @@ -4147,11 +4147,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; @@ -4177,11 +4174,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; diff --git a/src/corelib/text/qlocale_tools.cpp b/src/corelib/text/qlocale_tools.cpp index f3e664ea41..76ea6c9f33 100644 --- a/src/corelib/text/qlocale_tools.cpp +++ b/src/corelib/text/qlocale_tools.cpp @@ -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> diff --git a/src/corelib/text/qlocale_tools_p.h b/src/corelib/text/qlocale_tools_p.h index 2f676eced5..b5eb4d344b 100644 --- a/src/corelib/text/qlocale_tools_p.h +++ b/src/corelib/text/qlocale_tools_p.h @@ -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 diff --git a/src/corelib/text/qlocale_win.cpp b/src/corelib/text/qlocale_win.cpp index 821369ff89..598de83b0e 100644 --- a/src/corelib/text/qlocale_win.cpp +++ b/src/corelib/text/qlocale_win.cpp @@ -1074,11 +1074,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 @@ -1118,8 +1116,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)); diff --git a/src/corelib/text/qstring.cpp b/src/corelib/text/qstring.cpp index 5984fa9c0a..4db2723be7 100644 --- a/src/corelib/text/qstring.cpp +++ b/src/corelib/text/qstring.cpp @@ -6887,14 +6887,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 }; diff --git a/src/corelib/time/qtimezoneprivate_tz.cpp b/src/corelib/time/qtimezoneprivate_tz.cpp index 099bccb071..d7c529ad10 100644 --- a/src/corelib/time/qtimezoneprivate_tz.cpp +++ b/src/corelib/time/qtimezoneprivate_tz.cpp @@ -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; } } diff --git a/src/corelib/tools/qhash.cpp b/src/corelib/tools/qhash.cpp index 7ff53fe67e..a97fc50f3a 100644 --- a/src/corelib/tools/qhash.cpp +++ b/src/corelib/tools/qhash.cpp @@ -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 diff --git a/src/corelib/tools/qversionnumber.cpp b/src/corelib/tools/qversionnumber.cpp index 7d6ddc9bcd..f44d5e4e8c 100644 --- a/src/corelib/tools/qversionnumber.cpp +++ b/src/corelib/tools/qversionnumber.cpp @@ -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(); |