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>
This commit is contained in:
Edward Welbourne 2021-08-27 14:58:57 +02:00
parent 522ca997d3
commit 5644af6f8a
10 changed files with 345 additions and 512 deletions

View File

@ -1,158 +0,0 @@
From 81a2d1a38becdeed2cd8b963e190aedf197e39c6 Mon Sep 17 00:00:00 2001
From: Thiago Macieira <thiago.macieira@intel.com>
Date: Thu, 2 Oct 2014 22:03:19 -0700
Subject: [PATCH 1/1] Patch the FreeBSD strto(u)ll functions to work inside
QtCore
Changes:
- remove the #includes and the SCCSID
- rename from strtoxx_l to qt_strtoxx (merging the two functions)
- remove __restrict
- remove the locale_t parameter and use ascii_isspace instead of isspace_l
- fix compilation with -Wcast-qual (requires C++)
src/3rdparty/freebsd/strtoll.c | 27 +++------------------------
src/3rdparty/freebsd/strtoull.c | 27 +++------------------------
2 files changed, 6 insertions(+), 48 deletions(-)
diff --git a/src/3rdparty/freebsd/strtoll.c b/src/3rdparty/freebsd/strtoll.c
index c87aefb1cd..89da83425d 100644
--- a/src/3rdparty/freebsd/strtoll.c
+++ b/src/3rdparty/freebsd/strtoll.c
@@ -1,6 +1,4 @@
/*-
- * SPDX-License-Identifier: BSD-3-Clause
- *
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
@@ -34,18 +32,6 @@
* SUCH DAMAGE.
*/
-#if defined(LIBC_SCCS) && !defined(lint)
-static char sccsid[] = "@(#)strtoq.c 8.1 (Berkeley) 6/4/93";
-#endif /* LIBC_SCCS and not lint */
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-#include <limits.h>
-#include <errno.h>
-#include <ctype.h>
-#include <stdlib.h>
-#include "xlocale_private.h"
-
/*
* Convert a string to a long long integer.
*
@@ -53,15 +39,13 @@ __FBSDID("$FreeBSD$");
* alphabets and digits are each contiguous.
*/
long long
-strtoll_l(const char * __restrict nptr, char ** __restrict endptr, int base,
- locale_t locale)
+qt_strtoll(const char * nptr, char **endptr, int base)
{
const char *s;
unsigned long long acc;
char c;
unsigned long long cutoff;
int neg, any, cutlim;
- FIX_LOCALE(locale);
/*
* Skip white space and pick up leading +/- sign if any.
@@ -71,7 +55,7 @@ strtoll_l(const char * __restrict nptr, char ** __restrict endptr, int base,
s = nptr;
do {
c = *s++;
- } while (isspace_l((unsigned char)c, locale));
+ } while (ascii_isspace(c));
if (c == '-') {
neg = 1;
c = *s++;
@@ -145,11 +129,6 @@ noconv:
} else if (neg)
acc = -acc;
if (endptr != NULL)
- *endptr = (char *)(any ? s - 1 : nptr);
+ *endptr = const_cast<char *>(any ? s - 1 : nptr);
return (acc);
}
-long long
-strtoll(const char * __restrict nptr, char ** __restrict endptr, int base)
-{
- return strtoll_l(nptr, endptr, base, __get_locale());
-}
diff --git a/src/3rdparty/freebsd/strtoull.c b/src/3rdparty/freebsd/strtoull.c
index 58a9b23b56..cf151691ad 100644
--- a/src/3rdparty/freebsd/strtoull.c
+++ b/src/3rdparty/freebsd/strtoull.c
@@ -1,6 +1,4 @@
/*-
- * SPDX-License-Identifier: BSD-3-Clause
- *
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
@@ -34,18 +32,6 @@
* SUCH DAMAGE.
*/
-#if defined(LIBC_SCCS) && !defined(lint)
-static char sccsid[] = "@(#)strtouq.c 8.1 (Berkeley) 6/4/93";
-#endif /* LIBC_SCCS and not lint */
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-#include <limits.h>
-#include <errno.h>
-#include <ctype.h>
-#include <stdlib.h>
-#include "xlocale_private.h"
-
/*
* Convert a string to an unsigned long long integer.
*
@@ -53,15 +39,13 @@ __FBSDID("$FreeBSD$");
* alphabets and digits are each contiguous.
*/
unsigned long long
-strtoull_l(const char * __restrict nptr, char ** __restrict endptr, int base,
- locale_t locale)
+qt_strtoull(const char * nptr, char **endptr, int base)
{
const char *s;
unsigned long long acc;
char c;
unsigned long long cutoff;
int neg, any, cutlim;
- FIX_LOCALE(locale);
/*
* See strtoq for comments as to the logic used.
@@ -69,7 +53,7 @@ strtoull_l(const char * __restrict nptr, char ** __restrict endptr, int base,
s = nptr;
do {
c = *s++;
- } while (isspace_l((unsigned char)c, locale));
+ } while (ascii_isspace(c));
if (c == '-') {
neg = 1;
c = *s++;
@@ -123,11 +107,6 @@ noconv:
} else if (neg)
acc = -acc;
if (endptr != NULL)
- *endptr = (char *)(any ? s - 1 : nptr);
+ *endptr = const_cast<char *>(any ? s - 1 : nptr);
return (acc);
}
-unsigned long long
-strtoull(const char * __restrict nptr, char ** __restrict endptr, int base)
-{
- return strtoull_l(nptr, endptr, base, __get_locale());
-}
--
2.25.1

View File

@ -1,31 +0,0 @@
Copyright (c) 1992, 1993
The Regents of the University of California. All rights reserved.
Copyright (c) 2011 The FreeBSD Foundation
All rights reserved.
Portions of this software were developed by David Chisnall
under sponsorship from the FreeBSD Foundation.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the University nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.

View File

@ -1,18 +0,0 @@
{
"Id": "freebsd",
"Name": "FreeBSD strtoll and strtoull",
"QDocModule": "qtcore",
"QtUsage": "Used in Qt Core.",
"Files": "strtoll.c strtoull.c",
"Description": "strtoll() and strtoull() are functions for converting a string to (unsigned) long long integer.",
"Homepage": "https://github.com/freebsd/freebsd/",
"DownloadLocation": "https://github.com/freebsd/freebsd/tree/master/lib/libc/stdlib",
"Version": "upstream has complicated with std locales; do not update",
"Version": "18b29f3fb8abee5d57ed8f4a44f806bec7e0eeff",
"License": "BSD 3-clause \"New\" or \"Revised\" License",
"LicenseId": "BSD-3-Clause",
"LicenseFile": "LICENSE",
"Copyright": "Copyright (c) 1992, 1993 The Regents of the University of California.
Copyright (c) 2011 The FreeBSD Foundation"
}

View File

@ -1,134 +0,0 @@
/*-
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* Copyright (c) 2011 The FreeBSD Foundation
* All rights reserved.
* Portions of this software were developed by David Chisnall
* under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Convert a string to a long long integer.
*
* Assumes that the upper and lower case
* alphabets and digits are each contiguous.
*/
long long
qt_strtoll(const char * nptr, char **endptr, int base)
{
const char *s;
unsigned long long acc;
char c;
unsigned long long cutoff;
int neg, any, cutlim;
/*
* Skip white space and pick up leading +/- sign if any.
* If base is 0, allow 0x for hex and 0 for octal, else
* assume decimal; if base is already 16, allow 0x.
*/
s = nptr;
do {
c = *s++;
} while (ascii_isspace(c));
if (c == '-') {
neg = 1;
c = *s++;
} else {
neg = 0;
if (c == '+')
c = *s++;
}
if ((base == 0 || base == 16) &&
c == '0' && (*s == 'x' || *s == 'X') &&
((s[1] >= '0' && s[1] <= '9') ||
(s[1] >= 'A' && s[1] <= 'F') ||
(s[1] >= 'a' && s[1] <= 'f'))) {
c = s[1];
s += 2;
base = 16;
}
if (base == 0)
base = c == '0' ? 8 : 10;
acc = any = 0;
if (base < 2 || base > 36)
goto noconv;
/*
* Compute the cutoff value between legal numbers and illegal
* numbers. That is the largest legal value, divided by the
* base. An input number that is greater than this value, if
* followed by a legal input character, is too big. One that
* is equal to this value may be valid or not; the limit
* between valid and invalid numbers is then based on the last
* digit. For instance, if the range for quads is
* [-9223372036854775808..9223372036854775807] and the input base
* is 10, cutoff will be set to 922337203685477580 and cutlim to
* either 7 (neg==0) or 8 (neg==1), meaning that if we have
* accumulated a value > 922337203685477580, or equal but the
* next digit is > 7 (or 8), the number is too big, and we will
* return a range error.
*
* Set 'any' if any `digits' consumed; make it negative to indicate
* overflow.
*/
cutoff = neg ? (unsigned long long)-(LLONG_MIN + LLONG_MAX) + LLONG_MAX
: LLONG_MAX;
cutlim = cutoff % base;
cutoff /= base;
for ( ; ; c = *s++) {
if (c >= '0' && c <= '9')
c -= '0';
else if (c >= 'A' && c <= 'Z')
c -= 'A' - 10;
else if (c >= 'a' && c <= 'z')
c -= 'a' - 10;
else
break;
if (c >= base)
break;
if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
any = -1;
else {
any = 1;
acc *= base;
acc += c;
}
}
if (any < 0) {
acc = neg ? LLONG_MIN : LLONG_MAX;
errno = ERANGE;
} else if (!any) {
noconv:
errno = EINVAL;
} else if (neg)
acc = -acc;
if (endptr != NULL)
*endptr = const_cast<char *>(any ? s - 1 : nptr);
return (acc);
}

View File

@ -1,112 +0,0 @@
/*-
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* Copyright (c) 2011 The FreeBSD Foundation
* All rights reserved.
* Portions of this software were developed by David Chisnall
* under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Convert a string to an unsigned long long integer.
*
* Assumes that the upper and lower case
* alphabets and digits are each contiguous.
*/
unsigned long long
qt_strtoull(const char * nptr, char **endptr, int base)
{
const char *s;
unsigned long long acc;
char c;
unsigned long long cutoff;
int neg, any, cutlim;
/*
* See strtoq for comments as to the logic used.
*/
s = nptr;
do {
c = *s++;
} while (ascii_isspace(c));
if (c == '-') {
neg = 1;
c = *s++;
} else {
neg = 0;
if (c == '+')
c = *s++;
}
if ((base == 0 || base == 16) &&
c == '0' && (*s == 'x' || *s == 'X') &&
((s[1] >= '0' && s[1] <= '9') ||
(s[1] >= 'A' && s[1] <= 'F') ||
(s[1] >= 'a' && s[1] <= 'f'))) {
c = s[1];
s += 2;
base = 16;
}
if (base == 0)
base = c == '0' ? 8 : 10;
acc = any = 0;
if (base < 2 || base > 36)
goto noconv;
cutoff = ULLONG_MAX / base;
cutlim = ULLONG_MAX % base;
for ( ; ; c = *s++) {
if (c >= '0' && c <= '9')
c -= '0';
else if (c >= 'A' && c <= 'Z')
c -= 'A' - 10;
else if (c >= 'a' && c <= 'z')
c -= 'a' - 10;
else
break;
if (c >= base)
break;
if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
any = -1;
else {
any = 1;
acc *= base;
acc += c;
}
}
if (any < 0) {
acc = ULLONG_MAX;
errno = ERANGE;
} else if (!any) {
noconv:
errno = EINVAL;
} else if (neg)
acc = -acc;
if (endptr != NULL)
*endptr = const_cast<char *>(any ? s - 1 : nptr);
return (acc);
}

View File

@ -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;
}
unsigned long long
qstrtoull(const char * nptr, const char **endptr, int base, bool *ok)
/* Detect base if 0 and, if base is hex, skip over 0x prefix */
static auto scanPrefix(const char *p, const char *stop, int base)
{
// 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 == '-') {
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
qstrntoull(const char *begin, qsizetype size, const char **endptr, int base, bool *ok)
{
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;
}
const auto prefix = scanPrefix(*p == '+' ? p + 1 : p, stop, base);
if (!prefix.base || prefix.next >= stop) {
if (endptr)
*endptr = begin;
*ok = false;
return 0;
}
*ok = true;
errno = 0;
char *endptr2 = nullptr;
unsigned long long result = qt_strtoull(nptr, &endptr2, base);
const auto res = std::from_chars(prefix.next, stop, result, prefix.base);
*ok = res.ec == std::errc{};
if (endptr)
*endptr = endptr2;
if ((result == 0 || result == std::numeric_limits<unsigned long long>::max())
&& (errno || endptr2 == nptr)) {
*ok = false;
return 0;
}
*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>

View File

@ -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

View File

@ -36,6 +36,7 @@
#include <private/qtools_p.h>
#include "../shared/test_number_shared.h"
#include <limits>
class tst_QByteArray : public QObject
{
@ -1674,6 +1675,20 @@ void tst_QByteArray::toULong()
QCOMPARE(b, ok);
}
static QByteArray decNext(QByteArray &&big)
{
// Increments a decimal digit-string (ignoring sign, so decrements if
// negative); only intended for taking a boundary value just out of range,
// so big is never a string of only 9s (that'd be one less than a power of
// ten, which cannot be a power of two, as odd, or one less than one, as the
// power of ten isn't a power of two).
int i = big.size() - 1;
while (big.at(i) == '9')
big[i--] = '0';
big[i] += 1;
return big;
}
void tst_QByteArray::toLongLong_data()
{
QTest::addColumn<QByteArray>("str");
@ -1689,10 +1704,14 @@ void tst_QByteArray::toLongLong_data()
<< 7679359922672374856LL << true;
QTest::newRow("in range dec neg") << QByteArray("-7679359922672374856") << 10
<< -7679359922672374856LL << true;
QTest::newRow("in range hex") << QByteArray("6A929129A5421448") << 16 << 0x6A929129A5421448LL
<< true;
QTest::newRow("in range hex neg") << QByteArray("-6A929129A5421448") << 16
<< -0x6A929129A5421448LL << true;
QTest::newRow("in range hex")
<< QByteArray("6A929129A5421448") << 16 << 0x6A929129A5421448LL << true;
QTest::newRow("in range hex prefix")
<< QByteArray("0x6A929129A5421448") << 16 << 0x6A929129A5421448LL << true;
QTest::newRow("in range hex neg")
<< QByteArray("-6A929129A5421448") << 16 << -0x6A929129A5421448LL << true;
QTest::newRow("in range hex prefix neg")
<< QByteArray("-0x6A929129A5421448") << 16 << -0x6A929129A5421448LL << true;
QTest::newRow("Fibonacci's last int64") << QByteArray("7540113804746346429") << 10
<< 7540113804746346429LL << true;
@ -1700,6 +1719,8 @@ void tst_QByteArray::toLongLong_data()
<< 0xABCFFFFFFF123LL << true;
QTest::newRow("trailing spaces") << QByteArray("9876543210\t\r \n") << 10
<< 9876543210LL << true;
QTest::newRow("space after plus") << QByteArray("+ 12") << 10 << 0LL << false;
QTest::newRow("space after minus") << QByteArray("- 12") << 10 << 0LL << false;
QTest::newRow("leading junk") << QByteArray("q12345") << 10 << 0LL << false;
QTest::newRow("trailing junk") << QByteArray("abc12345t") << 16 << 0LL << false;
@ -1716,13 +1737,86 @@ void tst_QByteArray::toLongLong_data()
QTest::newRow("base 3") << QByteArray("12012") << 3 << 140LL << true;
QTest::newRow("neg base 3") << QByteArray("-201") << 3 << -19LL << true;
QTest::newRow("max dec") << QByteArray("9223372036854775807") << 10 << 9223372036854775807LL
<< true;
QTest::newRow("mix hex") << QByteArray("-7FFFFFFFFFFFFFFF") << 16 << -0x7FFFFFFFFFFFFFFFLL
<< true;
// Boundary values, first in every base:
using LL = std::numeric_limits<qlonglong>;
for (int b = 0; b <= 36; ++b) {
if (b == 1) // bases 0 and 2 through 36 are allowed
++b;
QTest::addRow("max base %d", b)
<< QByteArray::number(LL::max(), b ? b : 10) << b << LL::max() << true;
QTest::addRow("min base %d", b)
<< QByteArray::number(LL::min(), b ? b : 10) << b << LL::min() << true;
}
// Check leading zeros don't hit any buffer-too-big problems:
QTest::newRow("many-0 max dec")
<< (QByteArray(512, '0') + QByteArray::number(LL::max())) << 10 << LL::max() << true;
QTest::newRow("max + 1 dec") << QByteArray("9223372036854775808") << 10 << 0LL << false;
QTest::newRow("min - 1 hex") << QByteArray("-8000000000000001") << 16 << 0LL << false;
// Special bases (and let's include some leading space, too !), first decimal:
QTest::newRow("max dec, base 0") << QByteArray::number(LL::max()) << 0 << LL::max() << true;
QTest::newRow("max space dec")
<< ("\t\r\n\f\v " + QByteArray::number(LL::max())) << 10 << LL::max() << true;
QTest::newRow("max space dec, base 0")
<< ("\t\r\n\f\v " + QByteArray::number(LL::max())) << 0 << LL::max() << true;
QTest::newRow("min dec, base 0") << QByteArray::number(LL::min()) << 0 << LL::min() << true;
QTest::newRow("min space dec")
<< ("\t\r\n\f\v " + QByteArray::number(LL::min())) << 10 << LL::min() << true;
QTest::newRow("min space dec, base 0")
<< ("\t\r\n\f\v " + QByteArray::number(LL::min())) << 0 << LL::min() << true;
// Hex with prefix:
QTest::newRow("max 0x base 0")
<< ("0x" + QByteArray::number(LL::max(), 16)) << 0 << LL::max() << true;
QTest::newRow("max +0x base 0")
<< ("+0x" + QByteArray::number(LL::max(), 16)) << 0 << LL::max() << true;
QTest::newRow("max space 0x base 0")
<< ("\t\r\n\f\v 0x" + QByteArray::number(LL::max(), 16)) << 0 << LL::max() << true;
QTest::newRow("max space +0x base 0")
<< ("\t\r\n\f\v +0x" + QByteArray::number(LL::max(), 16)) << 0 << LL::max() << true;
QByteArray big = QByteArray::number(LL::min(), 16);
big.insert(1, "0x"); // after sign
QTest::newRow("min hex prefix") << big << 16 << LL::min() << true;
QTest::newRow("min 0x base 0") << big << 0 << LL::min() << true;
big.prepend("\t\r\n\f\v ");
QTest::newRow("min space hex prefix") << big << 16 << LL::min() << true;
QTest::newRow("min space 0x base 0") << big << 0 << LL::min() << true;
// Octal with prefix:
QTest::newRow("max octal base 0")
<< ('0' + QByteArray::number(LL::max(), 8)) << 0 << LL::max() << true;
QTest::newRow("max +octal base 0")
<< ("+0" + QByteArray::number(LL::max(), 8)) << 0 << LL::max() << true;
QTest::newRow("max space octal base 0")
<< ("\t\r\n\f\v 0" + QByteArray::number(LL::max(), 8)) << 0 << LL::max() << true;
QTest::newRow("max space +octal base 0")
<< ("\t\r\n\f\v +0" + QByteArray::number(LL::max(), 8)) << 0 << LL::max() << true;
big = QByteArray::number(LL::min(), 8);
big.insert(1, '0'); // after sign
QTest::newRow("min octal prefix") << big << 8 << LL::min() << true;
QTest::newRow("min octal base 0") << big << 0 << LL::min() << true;
big.prepend("\t\r\n\f\v ");
QTest::newRow("min space octal prefix") << big << 8 << LL::min() << true;
QTest::newRow("min space octal base 0") << big << 0 << LL::min() << true;
// Values *just* out of range:
QTest::newRow("max + 1 dec") << decNext(QByteArray::number(LL::max())) << 10 << 0LL << false;
QTest::newRow("max + 1 dec base 0")
<< decNext(QByteArray::number(LL::max())) << 0 << 0LL << false;
QTest::newRow("min - 1 dec") << decNext(QByteArray::number(LL::min())) << 10 << 0LL << false;
QTest::newRow("min - 1 dec base 0")
<< decNext(QByteArray::number(LL::min())) << 0 << 0LL << false;
// For hex and octal, we know the last digit of min is 0 and skipping its sign gets max+1:
big = QByteArray::number(LL::min(), 8);
QTest::newRow("max + 1 oct") << big.sliced(1) << 8 << 0LL << false;
big[big.size() - 1] = '1';
QTest::newRow("min - 1 oct") << big << 8 << 0LL << false;
big.insert(1, '0'); // after minus sign
QTest::newRow("min - 1 octal base 0") << big << 0 << 0LL << false;
big = QByteArray::number(LL::min(), 16);
QTest::newRow("max + 1 hex") << big.sliced(1) << 16 << 0LL << false;
big[big.size() - 1] = '1';
QTest::newRow("min - 1 hex") << big << 16 << 0LL << false;
big.insert(1, "0x"); // after minus sign
QTest::newRow("min - 1, 0x base 0") << big << 0 << 0LL << false;
}
void tst_QByteArray::toLongLong()
@ -1749,14 +1843,81 @@ void tst_QByteArray::toULongLong_data()
QTest::addColumn<qulonglong>("result");
QTest::addColumn<bool>("ok");
QTest::newRow("null") << QByteArray() << 10 << (qulonglong)0 << false;
QTest::newRow("empty") << QByteArray("") << 10 << (qulonglong)0 << false;
QTest::newRow("out of base bound") << QByteArray("c") << 10 << (qulonglong)0 << false;
QTest::newRow("null") << QByteArray() << 10 << 0ULL << false;
QTest::newRow("empty") << QByteArray("") << 10 << 0ULL << false;
QTest::newRow("out of base bound") << QByteArray("c") << 10 << 0ULL << false;
QTest::newRow("leading spaces") << QByteArray(" \n\r\t100") << 10 << qulonglong(100) << true;
QTest::newRow("trailing spaces") << QByteArray("100 \n\r\t") << 10 << qulonglong(100) << true;
QTest::newRow("leading junk") << QByteArray("x100") << 10 << qulonglong(0) << false;
QTest::newRow("trailing junk") << QByteArray("100x") << 10 << qulonglong(0) << false;
QTest::newRow("in range dec")
<< QByteArray("7679359922672374856") << 10 << 7679359922672374856ULL << true;
QTest::newRow("in range hex")
<< QByteArray("6A929129A5421448") << 16 << 0x6A929129A5421448ULL << true;
QTest::newRow("in range hex prefix")
<< QByteArray("0x6A929129A5421448") << 16 << 0x6A929129A5421448ULL << true;
QTest::newRow("leading spaces") << QByteArray(" \n\r\t100") << 10 << 100ULL << true;
QTest::newRow("trailing spaces") << QByteArray("100 \n\r\t") << 10 << 100ULL << true;
QTest::newRow("leading plus") << QByteArray("+100") << 10 << 100ULL << true;
QTest::newRow("space after plus") << QByteArray("+ 12") << 10 << 0ULL << false;
QTest::newRow("leading minus") << QByteArray("-100") << 10 << 0ULL << false;
QTest::newRow("leading junk") << QByteArray("x100") << 10 << 0ULL << false;
QTest::newRow("trailing junk") << QByteArray("100x") << 10 << 0ULL << false;
QTest::newRow("dec, base 0") << QByteArray("9876543210") << 0 << 9876543210ULL << true;
QTest::newRow("hex, base 0") << QByteArray("0x9876543210") << 0 << 0x9876543210ULL << true;
QTest::newRow("oct, base 0") << QByteArray("07654321234567") << 0 << 07654321234567ULL << true;
QTest::newRow("base 3") << QByteArray("12012") << 3 << 140ULL << true;
// Boundary values, first in every base:
using ULL = std::numeric_limits<qulonglong>;
for (int b = 0; b <= 36; ++b) {
if (b == 1) // bases 0 and 2 through 36 are allowed
++b;
QTest::addRow("max base %d", b)
<< QByteArray::number(ULL::max(), b ? b : 10) << b << ULL::max() << true;
}
// Check leading zeros don't hit any buffer-too-big problems:
QTest::newRow("many-0 max dec")
<< (QByteArray(512, '0') + QByteArray::number(ULL::max())) << 10 << ULL::max() << true;
// Special bases (and let's include some leading space, too !), first decimal:
QTest::newRow("max dec, base 0") << QByteArray::number(ULL::max()) << 0 << ULL::max() << true;
QTest::newRow("max space dec")
<< ("\t\r\n\f\v " + QByteArray::number(ULL::max())) << 10 << ULL::max() << true;
QTest::newRow("max space dec, base 0")
<< ("\t\r\n\f\v " + QByteArray::number(ULL::max())) << 0 << ULL::max() << true;
// Hex with prefix:
QTest::newRow("max 0x base 0")
<< ("0x" + QByteArray::number(ULL::max(), 16)) << 0 << ULL::max() << true;
QTest::newRow("max +0x base 0")
<< ("+0x" + QByteArray::number(ULL::max(), 16)) << 0 << ULL::max() << true;
QTest::newRow("max space 0x base 0")
<< ("\t\r\n\f\v 0x" + QByteArray::number(ULL::max(), 16)) << 0 << ULL::max() << true;
QTest::newRow("max space +0x base 0")
<< ("\t\r\n\f\v +0x" + QByteArray::number(ULL::max(), 16)) << 0 << ULL::max() << true;
// Octal with prefix:
QTest::newRow("max octal base 0")
<< ('0' + QByteArray::number(ULL::max(), 8)) << 0 << ULL::max() << true;
QTest::newRow("max +octal base 0")
<< ("+0" + QByteArray::number(ULL::max(), 8)) << 0 << ULL::max() << true;
QTest::newRow("max space octal base 0")
<< ("\t\r\n\f\v 0" + QByteArray::number(ULL::max(), 8)) << 0 << ULL::max() << true;
QTest::newRow("max space +octal base 0")
<< ("\t\r\n\f\v +0" + QByteArray::number(ULL::max(), 8)) << 0 << ULL::max() << true;
// Values *just* out of range:
QTest::newRow("max + 1 dec") << decNext(QByteArray::number(ULL::max())) << 10 << 0ULL << false;
QTest::newRow("max + 1 dec base 0")
<< decNext(QByteArray::number(ULL::max())) << 0 << 0ULL << false;
auto big = QByteArray::number(ULL::max(), 8).replace('7', '0');
// Number of bits is a power of two, so not a multiple of three; so (only)
// first digit of max wasn't 7:
big[0] += 1;
QTest::newRow("max + 1 oct") << big << 8 << 0ULL << false;
// Number of bits is a multiple of four, so every digit of max is 'f'.
big = '1' + QByteArray::number(ULL::max(), 16).replace('f', '0');
QTest::newRow("max + 1 hex") << big << 16 << 0ULL << false;
}
void tst_QByteArray::toULongLong()

View File

@ -1319,6 +1319,9 @@ void tst_QLocale::long_long_conversion_data()
QTest::newRow("C 12345,67") << QString("C") << "12345,67" << false << (qlonglong) 0;
QTest::newRow("C 123456,7") << QString("C") << "123456,7" << false << (qlonglong) 0;
QTest::newRow("C 1,234,567") << QString("C") << "1,234,567" << true << (qlonglong) 1234567;
using LL = std::numeric_limits<qlonglong>;
QTest::newRow("C LLONG_MIN") << QString("C") << QString::number(LL::min()) << true << LL::min();
QTest::newRow("C LLONG_MAX") << QString("C") << QString::number(LL::max()) << true << LL::max();
QTest::newRow("de_DE 1") << QString("de_DE") << "1" << true << (qlonglong) 1;
QTest::newRow("de_DE 1.") << QString("de_DE") << "1." << false << (qlonglong) 0;

View File

@ -3897,7 +3897,7 @@ void tst_QString::toLong()
void tst_QString::toULongLong()
{
QString str;
bool ok;
bool ok = true;
QCOMPARE(str.toULongLong(), Q_UINT64_C(0));
QCOMPARE(str.toULongLong(&ok), Q_UINT64_C(0));
@ -3917,6 +3917,15 @@ void tst_QString::toULongLong()
QCOMPARE( str.toULongLong( 0 ), Q_UINT64_C(0) );
QCOMPARE( str.toULongLong( &ok ), Q_UINT64_C(0) );
QVERIFY( !ok );
// Check limits round-trip in every base:
using ULL = std::numeric_limits<qulonglong>;
for (int b = 0; b <= 36; ++b) {
if (b == 1) // 0 and 2 through 36 are valid bases
++b;
QCOMPARE(QString::number(ULL::max(), b ? b : 10).toULongLong(&ok, b), ULL::max());
QVERIFY(ok);
}
}
void tst_QString::toLongLong()
@ -3969,6 +3978,71 @@ void tst_QString::toLongLong()
}
}
}
// Check bounds.
// First in every base, with no prefix:
using LL = std::numeric_limits<qlonglong>;
for (int b = 0; b <= 36; ++b) {
if (b == 1) // 0 and 2 through 36 are valid bases
++b;
QCOMPARE(QString::number(LL::max(), b ? b : 10).toLongLong(&ok, b), LL::max());
QVERIFY(ok);
QCOMPARE(QString::number(LL::min(), b ? b : 10).toLongLong(&ok, b), LL::min());
QVERIFY(ok);
}
// Then in base 16 or 0 with 0x prefix:
auto big = QString::number(LL::min(), 16);
big.insert(1, u"0x"); // after the minus sign
big.prepend(u"\t\r\n\f\v ");
QCOMPARE(big.toLongLong(&ok, 16), LL::min());
QVERIFY(ok);
QCOMPARE(big.toLongLong(&ok, 0), LL::min());
QVERIFY(ok);
big = QString::number(LL::max(), 16);
big.prepend(u"\t\r\n\f\v 0x");
QCOMPARE(big.toLongLong(&ok, 16), LL::max());
QVERIFY(ok);
QCOMPARE(big.toLongLong(&ok, 0), LL::max());
QVERIFY(ok);
big.insert(6, u'+');
QCOMPARE(big.toLongLong(&ok, 16), LL::max());
QVERIFY(ok);
QCOMPARE(big.toLongLong(&ok, 0), LL::max());
QVERIFY(ok);
// Next octal:
big = QString::number(LL::min(), 8);
big.insert(1, u'0'); // after the minus sign
big.prepend(u"\t\r\n\f\v ");
QCOMPARE(big.toLongLong(&ok, 8), LL::min());
QVERIFY(ok);
QCOMPARE(big.toLongLong(&ok, 0), LL::min());
QVERIFY(ok);
big = QString::number(LL::max(), 8);
big.prepend(u"\t\r\n\f\v 0");
QCOMPARE(big.toLongLong(&ok, 8), LL::max());
QVERIFY(ok);
QCOMPARE(big.toLongLong(&ok, 0), LL::max());
QVERIFY(ok);
big.insert(6, u'+');
QCOMPARE(big.toLongLong(&ok, 8), LL::max());
QVERIFY(ok);
QCOMPARE(big.toLongLong(&ok, 0), LL::max());
QVERIFY(ok);
// Finally decimal for base 0:
big = QString::number(LL::min(), 10);
big.prepend(u"\t\r\n\f\v ");
QCOMPARE(big.toLongLong(&ok, 0), LL::min());
QVERIFY(ok);
big = QString::number(LL::max(), 10);
big.prepend(u"\t\r\n\f\v ");
QCOMPARE(big.toLongLong(&ok, 0), LL::max());
QVERIFY(ok);
big.insert(6, u'+');
QCOMPARE(big.toLongLong(&ok, 0), LL::max());
QVERIFY(ok);
}
////////////////////////////////////////////////////////////////////////////