QHostAddress: Improve code generation

Mostly related to IPv6, because Q_IPV6ADDR is an array of char, so the
compilers were generating byte access to each value. Instead, force
access as 32- and 64-bit in most places that make sense (64-bit access
decays to 32-bit on 32-bit machines). In one isLoopback(), this is now a
128-bit access for best improvement.

Some smaller improvements relating to SpecialAddress by combining the
three IPv4 special addresses.

Change-Id: I7de033f80b0e4431b7f1ffff13f932b1cd7b5d21
Reviewed-by: Richard J. Moore <rich@kde.org>
This commit is contained in:
Thiago Macieira 2015-08-10 13:08:15 -07:00 committed by Jędrzej Nowacki
parent b479d5befb
commit dd92002416
2 changed files with 56 additions and 49 deletions

View File

@ -46,6 +46,9 @@
#ifndef QT_NO_DATASTREAM
#include <qdatastream.h>
#endif
#ifdef __SSE2__
# include <private/qsimd_p.h>
#endif
#ifdef QT_LINUXBASE
# include <arpa/inet.h>
@ -106,7 +109,11 @@ public:
QString scopeId;
quint32 a; // IPv4 address
Q_IPV6ADDR a6; // IPv6 address
union {
Q_IPV6ADDR a6; // IPv6 address
struct { quint64 c[2]; } a6_64;
struct { quint32 c[4]; } a6_32;
};
QAbstractSocket::NetworkLayerProtocol protocol;
bool isParsed;
@ -123,24 +130,17 @@ QHostAddressPrivate::QHostAddressPrivate()
void QHostAddressPrivate::setAddress(quint32 a_)
{
a = a_;
//create mapped address, except for a_ == 0 (any)
memset(&a6, 0, sizeof(a6));
if (a) {
a6[11] = 0xFF;
a6[10] = 0xFF;
} else {
a6[11] = 0;
a6[10] = 0;
}
int i;
for (i=15; a_ != 0; i--) {
a6[i] = a_ & 0xFF;
a_ >>=8;
}
Q_ASSERT(i >= 11);
protocol = QAbstractSocket::IPv4Protocol;
isParsed = true;
//create mapped address, except for a_ == 0 (any)
a6_64.c[0] = 0;
if (a) {
a6_32.c[2] = qToBigEndian(0xffff);
a6_32.c[3] = qToBigEndian(a);
} else {
a6_64.c[1] = 0;
}
}
/// parses v4-mapped addresses or the AnyIPv6 address and stores in \a a;
@ -163,21 +163,16 @@ static bool convertToIpv4(quint32& a, const Q_IPV6ADDR &a6)
void QHostAddressPrivate::setAddress(const quint8 *a_)
{
for (int i = 0; i < 16; i++)
a6[i] = a_[i];
a = 0;
convertToIpv4(a, a6);
protocol = QAbstractSocket::IPv6Protocol;
isParsed = true;
memcpy(a6.c, a_, sizeof(a6));
a = 0;
convertToIpv4(a, a6);
}
void QHostAddressPrivate::setAddress(const Q_IPV6ADDR &a_)
{
a6 = a_;
a = 0;
convertToIpv4(a, a6);
protocol = QAbstractSocket::IPv6Protocol;
isParsed = true;
setAddress(a_.c);
}
static bool parseIp6(const QString &address, QIPAddressUtils::IPv6Address &addr, QString *scopeId)
@ -486,31 +481,35 @@ QHostAddress::QHostAddress(SpecialAddress address)
{
Q_IPV6ADDR ip6;
memset(&ip6, 0, sizeof ip6);
quint32 ip4 = INADDR_ANY;
switch (address) {
case Null:
break;
return;
case Broadcast:
d->setAddress(quint32(-1));
ip4 = INADDR_BROADCAST;
break;
case LocalHost:
d->setAddress(0x7f000001);
break;
case LocalHostIPv6:
ip6[15] = 1;
d->setAddress(ip6);
ip4 = INADDR_LOOPBACK;
break;
case AnyIPv4:
setAddress(0u);
break;
case LocalHostIPv6:
ip6[15] = 1;
// fall through
case AnyIPv6:
d->setAddress(ip6);
break;
return;
case Any:
d->clear();
d->protocol = QAbstractSocket::AnyIPProtocol;
break;
return;
}
// common IPv4 part
d->setAddress(ip4);
}
/*!
@ -837,34 +836,36 @@ bool QHostAddress::operator==(const QHostAddress &other) const
bool QHostAddress::operator ==(SpecialAddress other) const
{
QT_ENSURE_PARSED(this);
quint32 ip4 = INADDR_ANY;
switch (other) {
case Null:
return d->protocol == QAbstractSocket::UnknownNetworkLayerProtocol;
case Broadcast:
return d->protocol == QAbstractSocket::IPv4Protocol && d->a == INADDR_BROADCAST;
ip4 = INADDR_BROADCAST;
break;
case LocalHost:
return d->protocol == QAbstractSocket::IPv4Protocol && d->a == INADDR_LOOPBACK;
ip4 = INADDR_LOOPBACK;
break;
case Any:
return d->protocol == QAbstractSocket::AnyIPProtocol;
case AnyIPv4:
return d->protocol == QAbstractSocket::IPv4Protocol && d->a == INADDR_ANY;
break;
case LocalHostIPv6:
case AnyIPv6:
if (d->protocol == QAbstractSocket::IPv6Protocol) {
Q_IPV6ADDR ip6 = { { 0 } };
ip6[15] = quint8(other == LocalHostIPv6); // 1 for localhost, 0 for any
return memcmp(&d->a6, &ip6, sizeof ip6) == 0;
quint64 second = quint8(other == LocalHostIPv6); // 1 for localhost, 0 for any
return d->a6_64.c[0] == 0 && d->a6_64.c[1] == qToBigEndian(second);
}
return false;
}
Q_UNREACHABLE();
return false;
// common IPv4 part
return d->protocol == QAbstractSocket::IPv4Protocol && d->a == ip4;
}
/*!
@ -1086,11 +1087,15 @@ bool QHostAddress::isLoopback() const
if ((d->a & 0xFF000000) == 0x7F000000)
return true; // v4 range (including IPv6 wrapped IPv4 addresses)
if (d->protocol == QAbstractSocket::IPv6Protocol) {
if (d->a6.c[15] != 1)
#ifdef __SSE2__
const __m128i loopback = _mm_setr_epi8(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1);
__m128i ipv6 = _mm_loadu_si128((const __m128i *)d->a6.c);
__m128i cmp = _mm_cmpeq_epi8(ipv6, loopback);
return _mm_movemask_epi8(cmp) == 0xffff;
#else
if (d->a6_64.c[0] != 0 || qFromBigEndian(d->a6_64.c[1]) != 1)
return false;
for (int i = 0; i < 15; i++)
if (d->a6[i] != 0)
return false;
#endif
return true;
}
return false;

View File

@ -642,10 +642,12 @@ void tst_QHostAddress::isLoopback_data()
QTest::newRow("AnyIPv6") << QHostAddress(QHostAddress::AnyIPv6) << false;
QTest::newRow("Broadcast") << QHostAddress(QHostAddress::Broadcast) << false;
QTest::newRow("Null") << QHostAddress(QHostAddress::Null) << false;
QTest::newRow("ipv6-all-ffff") << QHostAddress("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff") << false;
QTest::newRow("::ffff:127.0.0.1") << QHostAddress("::ffff:127.0.0.1") << true;
QTest::newRow("::ffff:127.0.0.2") << QHostAddress("::ffff:127.0.0.2") << true;
QTest::newRow("::ffff:127.3.2.1") << QHostAddress("::ffff:127.3.2.1") << true;
}
void tst_QHostAddress::isLoopback()