diff options
author | Edward Welbourne <edward.welbourne@qt.io> | 2021-08-27 14:58:57 +0200 |
---|---|---|
committer | Edward Welbourne <edward.welbourne@qt.io> | 2021-08-30 17:46:00 +0200 |
commit | 5644af6f8a800a1516360a42ba4c1a8dc61fc516 (patch) | |
tree | 0fb11871c40c7f389f95bc227699fdcda8b7889a /src/corelib/text | |
parent | 522ca997d3baab1b88f454bbeea9f357d3969dff (diff) |
Replace FreeBSD's strtou?ll() with std::from_chars()-based strntou?ll()
Remove third-party code in favor of STL. Implement (for now)
strtou?ll() as inlines on strntou?ll() calling strlen() for the size
parameter. (This is not entirely safe, as a string lacking
'\0'-termination but with at least some non-matching text after the
numeric portion would formerly be parsed just fine, but would now
produce a crash. However, strtou?ll() are internal and callers should
be ensuring '\0'-termination.)
Task-number: QTBUG-74286
Change-Id: I0c8ca7d4f6110367e93b4c0164854a82c5a545e1
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: MÃ¥rten Nordheim <marten.nordheim@qt.io>
Diffstat (limited to 'src/corelib/text')
-rw-r--r-- | src/corelib/text/qlocale_tools.cpp | 117 | ||||
-rw-r--r-- | src/corelib/text/qlocale_tools_p.h | 11 |
2 files changed, 88 insertions, 40 deletions
diff --git a/src/corelib/text/qlocale_tools.cpp b/src/corelib/text/qlocale_tools.cpp index 15933c3644..c144dba938 100644 --- a/src/corelib/text/qlocale_tools.cpp +++ b/src/corelib/text/qlocale_tools.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2021 The Qt Company Ltd. ** Copyright (C) 2016 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** @@ -54,6 +54,7 @@ #include <time.h> #include <limits> +#include <charconv> #if defined(Q_OS_LINUX) && !defined(__UCLIBC__) # include <fenv.h> @@ -72,13 +73,6 @@ QT_BEGIN_NAMESPACE -QT_WARNING_PUSH - /* "unary minus operator applied to unsigned type, result still unsigned" */ -QT_WARNING_DISABLE_MSVC(4146) -#include "../../3rdparty/freebsd/strtoull.c" -#include "../../3rdparty/freebsd/strtoll.c" -QT_WARNING_POP - QT_CLOCALE_HOLDER void qt_doubleToAscii(double d, QLocaleData::DoubleForm form, int precision, char *buf, int bufSize, @@ -416,50 +410,99 @@ double qt_asciiToDouble(const char *num, qsizetype numLen, bool &ok, int &proces return d; } +/* Detect base if 0 and, if base is hex, skip over 0x prefix */ +static auto scanPrefix(const char *p, const char *stop, int base) +{ + if (p < stop && *p >= '0' && *p <= '9') { + if (*p == '0') { + const char *x = p + 1; + if (x < stop && (*x == 'x' || *x == 'X')) { + if (base == 0) + base = 16; + if (base == 16) + p += 2; + } else if (base == 0) { + base = 8; + } + } else if (base == 0) { + base = 10; + } + Q_ASSERT(base); + } + struct R + { + const char *next; + int base; + }; + return R{p, base}; +} + unsigned long long -qstrtoull(const char * nptr, const char **endptr, int base, bool *ok) +qstrntoull(const char *begin, qsizetype size, const char **endptr, int base, bool *ok) { - // strtoull accepts negative numbers. We don't. - // Use a different variable so we pass the original nptr to strtoul - // (we need that so endptr may be nptr in case of failure) - const char *begin = nptr; - while (ascii_isspace(*begin)) - ++begin; - if (*begin == '-') { + 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; - return 0; + if (endptr) + *endptr = begin; + return result; } - - *ok = true; - errno = 0; - char *endptr2 = nullptr; - unsigned long long result = qt_strtoull(nptr, &endptr2, base); - if (endptr) - *endptr = endptr2; - if ((result == 0 || result == std::numeric_limits<unsigned long long>::max()) - && (errno || endptr2 == nptr)) { + const auto prefix = scanPrefix(*p == '+' ? p + 1 : p, stop, base); + if (!prefix.base || prefix.next >= stop) { + if (endptr) + *endptr = begin; *ok = false; return 0; } + + 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; } long long -qstrtoll(const char * nptr, const char **endptr, int base, bool *ok) +qstrntoll(const char *begin, qsizetype size, const char **endptr, int base, bool *ok) { - *ok = true; - errno = 0; - char *endptr2 = nullptr; - long long result = qt_strtoll(nptr, &endptr2, base); - if (endptr) - *endptr = endptr2; - if ((result == 0 || result == std::numeric_limits<long long>::min() - || result == std::numeric_limits<long long>::max()) - && (errno || nptr == endptr2)) { + const char *p = begin, *const stop = begin + size; + while (p < stop && ascii_isspace(*p)) + ++p; + // Frustratingly, std::from_chars() doesn't cope with a 0x prefix that might + // be between the sign and digits, so we have to handle that for it, which + // means we can't use its ability to read LLONG_MIN directly; see below. + const bool negate = p < stop && *p == '-'; + if (negate || (p < stop && *p == '+')) + ++p; + + const auto prefix = scanPrefix(p, stop, base); + if (!prefix.base || prefix.next >= stop) { + if (endptr) + *endptr = begin; *ok = false; return 0; } - return result; + + 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 (endptr) + *endptr = res.ptr == prefix.next ? begin : res.ptr; + return negate && *ok ? -result : result; } template <typename Char> diff --git a/src/corelib/text/qlocale_tools_p.h b/src/corelib/text/qlocale_tools_p.h index b6ffff8224..27d849ea7a 100644 --- a/src/corelib/text/qlocale_tools_p.h +++ b/src/corelib/text/qlocale_tools_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2020 The Qt Company Ltd. +** Copyright (C) 2021 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -117,8 +117,13 @@ inline double qstrtod(const char *s00, char const **se, bool *ok) return qstrntod(s00, len, se, ok); } -qlonglong qstrtoll(const char *nptr, const char **endptr, int base, bool *ok); -qulonglong qstrtoull(const char *nptr, const char **endptr, int base, bool *ok); +qlonglong qstrntoll(const char *nptr, qsizetype size, const char **endptr, int base, bool *ok); +qulonglong qstrntoull(const char *nptr, qsizetype size, const char **endptr, int base, bool *ok); + +inline qlonglong qstrtoll(const char *nptr, const char **endptr, int base, bool *ok) +{ return qstrntoll(nptr, strlen(nptr), endptr, base, ok); } +inline qulonglong qstrtoull(const char *nptr, const char **endptr, int base, bool *ok) +{ return qstrntoull(nptr, strlen(nptr), endptr, base, ok); } QT_END_NAMESPACE |