Refactor QNetworkAddress not to keep a full QHostAddress

QHostAddressPrivate is one QString and 24 bytes, allocated on the heap,
which is WAY too heavy for something that fits into 8 bits. So instead
of storing the expanded netmask inside QNetworkAddressEntryPrivate, we
store the simple prefix length and calculate the mask only if asked.

Change-Id: Ie05c6480d8a44fda817ffffd14d9ad4707aa8a92
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
This commit is contained in:
Thiago Macieira 2017-08-10 20:34:31 -07:00
parent 758982bd74
commit 8656ee950b
4 changed files with 52 additions and 60 deletions

View File

@ -206,18 +206,8 @@ void QHostAddressPrivate::clear()
}
bool QNetmaskAddress::setAddress(const QString &address)
bool QNetmask::setAddress(const QHostAddress &address)
{
d.detach();
length = -1;
QHostAddress other;
return other.setAddress(address) && setAddress(other);
}
bool QNetmaskAddress::setAddress(const QHostAddress &address)
{
d.detach();
static const quint8 zeroes[16] = { 0 };
union {
quint32 v4;
@ -229,16 +219,13 @@ bool QNetmaskAddress::setAddress(const QHostAddress &address)
quint8 *end;
length = -1;
QHostAddress::operator=(address);
if (d->protocol == QAbstractSocket::IPv4Protocol) {
ip.v4 = qToBigEndian(d->a);
if (address.protocol() == QAbstractSocket::IPv4Protocol) {
ip.v4 = qToBigEndian(address.toIPv4Address());
end = ptr + 4;
} else if (d->protocol == QAbstractSocket::IPv6Protocol) {
memcpy(ip.v6, d->a6.c, 16);
} else if (address.protocol() == QAbstractSocket::IPv6Protocol) {
memcpy(ip.v6, address.toIPv6Address().c, 16);
end = ptr + 16;
} else {
d->clear();
return false;
}
@ -250,7 +237,6 @@ bool QNetmaskAddress::setAddress(const QHostAddress &address)
continue;
default:
d->clear();
return false; // invalid IP-style netmask
case 254:
@ -281,10 +267,8 @@ bool QNetmaskAddress::setAddress(const QHostAddress &address)
}
// confirm that the rest is only zeroes
if (ptr < end && memcmp(ptr + 1, zeroes, end - ptr - 1) != 0) {
d->clear();
if (ptr < end && memcmp(ptr + 1, zeroes, end - ptr - 1) != 0)
return false;
}
length = netmask;
return true;
@ -304,35 +288,25 @@ static void clearBits(quint8 *where, int start, int end)
memset(where + (start + 7) / 8, 0, end / 8 - (start + 7) / 8);
}
int QNetmaskAddress::prefixLength() const
QHostAddress QNetmask::address(QAbstractSocket::NetworkLayerProtocol protocol) const
{
return length;
}
void QNetmaskAddress::setPrefixLength(QAbstractSocket::NetworkLayerProtocol proto, int newLength)
{
d.detach();
length = newLength;
if (length < 0 || length > (proto == QAbstractSocket::IPv4Protocol ? 32 :
proto == QAbstractSocket::IPv6Protocol ? 128 : -1)) {
// invalid information, reject
d->protocol = QAbstractSocket::UnknownNetworkLayerProtocol;
length = -1;
return;
}
d->protocol = proto;
if (d->protocol == QAbstractSocket::IPv4Protocol) {
if (length == 0) {
d->a = 0;
} else if (length == 32) {
d->a = quint32(0xffffffff);
} else {
d->a = quint32(0xffffffff) >> (32 - length) << (32 - length);
}
if (length == 255 || protocol == QAbstractSocket::AnyIPProtocol ||
protocol == QAbstractSocket::UnknownNetworkLayerProtocol) {
return QHostAddress();
} else if (protocol == QAbstractSocket::IPv4Protocol) {
quint32 a;
if (length == 0)
a = 0;
else if (length == 32)
a = quint32(0xffffffff);
else
a = quint32(0xffffffff) >> (32 - length) << (32 - length);
return QHostAddress(a);
} else {
memset(d->a6.c, 0xFF, sizeof(d->a6));
clearBits(d->a6.c, length, 128);
Q_IPV6ADDR a6;
memset(a6.c, 0xFF, sizeof(a6));
clearBits(a6.c, length, 128);
return QHostAddress(a6);
}
}
@ -1104,8 +1078,11 @@ QPair<QHostAddress, int> QHostAddress::parseSubnet(const QString &subnet)
// is the netmask given in IP-form or in bit-count form?
if (!isIpv6 && subnet.indexOf(QLatin1Char('.'), slash + 1) != -1) {
// IP-style, convert it to bit-count form
QNetmaskAddress parser;
if (!parser.setAddress(subnet.mid(slash + 1)))
QHostAddress mask;
QNetmask parser;
if (!mask.setAddress(subnet.mid(slash + 1)))
return invalid;
if (!parser.setAddress(mask))
return invalid;
netmask = parser.prefixLength();
} else {

View File

@ -57,17 +57,32 @@
QT_BEGIN_NAMESPACE
class QNetmaskAddress: public QHostAddress
class QNetmask
{
int length;
// stores 0-32 for IPv4, 0-128 for IPv6, or 255 for invalid
quint8 length;
public:
QNetmaskAddress() : QHostAddress(), length(-1) { }
Q_DECL_CONSTEXPR QNetmask() : length(255) {}
bool setAddress(const QString &address);
bool setAddress(const QHostAddress &address);
QHostAddress address(QAbstractSocket::NetworkLayerProtocol protocol) const;
int prefixLength() const;
void setPrefixLength(QAbstractSocket::NetworkLayerProtocol proto, int len);
int prefixLength() const { return length == 255 ? -1 : length; }
void setPrefixLength(QAbstractSocket::NetworkLayerProtocol proto, int len)
{
int maxlen = -1;
if (proto == QAbstractSocket::IPv4Protocol)
maxlen = 32;
else if (proto == QAbstractSocket::IPv6Protocol)
maxlen = 128;
if (len > maxlen || len < 0)
length = 255U;
else
length = unsigned(len);
}
friend bool operator==(QNetmask n1, QNetmask n2)
{ return n1.length == n2.length; }
};
QT_END_NAMESPACE

View File

@ -257,7 +257,7 @@ void QNetworkAddressEntry::setIp(const QHostAddress &newIp)
*/
QHostAddress QNetworkAddressEntry::netmask() const
{
return d->netmask;
return d->netmask.address(d->address.protocol());
}
/*!
@ -270,7 +270,7 @@ QHostAddress QNetworkAddressEntry::netmask() const
void QNetworkAddressEntry::setNetmask(const QHostAddress &newNetmask)
{
if (newNetmask.protocol() != ip().protocol()) {
d->netmask = QNetmaskAddress();
d->netmask = QNetmask();
return;
}

View File

@ -68,8 +68,8 @@ class QNetworkAddressEntryPrivate
{
public:
QHostAddress address;
QNetmaskAddress netmask;
QHostAddress broadcast;
QNetmask netmask;
};
class QNetworkInterfacePrivate: public QSharedData