QHostAddress: Improve code generation
authorThiago Macieira <thiago.macieira@intel.com>
Mon, 10 Aug 2015 20:08:15 +0000 (13:08 -0700)
committerJędrzej Nowacki <jedrzej.nowacki@theqtcompany.com>
Wed, 19 Aug 2015 06:13:33 +0000 (06:13 +0000)
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>
src/network/kernel/qhostaddress.cpp
tests/auto/network/kernel/qhostaddress/tst_qhostaddress.cpp

index 6ee76a4250413ac8fd39b3ec16fd0b57a3d88df1..935af04e31c67360d4dd28430703c2fe9b1c6b5b 100644 (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_;
+    protocol = QAbstractSocket::IPv4Protocol;
+    isParsed = true;
+
     //create mapped address, except for a_ == 0 (any)
-    memset(&a6, 0, sizeof(a6));
+    a6_64.c[0] = 0;
     if (a) {
-        a6[11] = 0xFF;
-        a6[10] = 0xFF;
+        a6_32.c[2] = qToBigEndian(0xffff);
+        a6_32.c[3] = qToBigEndian(a);
     } else {
-        a6[11] = 0;
-        a6[10] = 0;
+        a6_64.c[1] = 0;
     }
-
-    int i;
-    for (i=15; a_ != 0; i--) {
-        a6[i] = a_ & 0xFF;
-        a_ >>=8;
-    }
-    Q_ASSERT(i >= 11);
-    protocol = QAbstractSocket::IPv4Protocol;
-    isParsed = true;
 }
 
 /// 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;
index b644372dab22c26ab84752abbfa46c404eeeae00..8069865d937840369f05c4327ca4d01753f85a23 100644 (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()