os: improve networkInterfaces performance

This algorithm uses less data transformations and is therefore
significantly faster than the one before.

PR-URL: https://github.com/nodejs/node/pull/22359
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
This commit is contained in:
Ruben Bridgewater 2018-08-16 14:31:04 +02:00
parent 104492bd83
commit 9bcb744ee5
No known key found for this signature in database
GPG Key ID: F07496B3EB3C1762
5 changed files with 59 additions and 86 deletions

View File

@ -1,41 +0,0 @@
'use strict';
function getCIDRSuffix(mask, protocol = 'ipv4') {
const isV6 = protocol === 'ipv6';
const bitsString = mask
.split(isV6 ? ':' : '.')
.filter((v) => !!v)
.map((v) => pad(parseInt(v, isV6 ? 16 : 10).toString(2), isV6))
.join('');
if (isValidMask(bitsString)) {
return countOnes(bitsString);
} else {
return null;
}
}
function pad(binaryString, isV6) {
const groupLength = isV6 ? 16 : 8;
const binLen = binaryString.length;
return binLen < groupLength ?
`${'0'.repeat(groupLength - binLen)}${binaryString}` : binaryString;
}
function isValidMask(bitsString) {
const firstIndexOfZero = bitsString.indexOf(0);
const lastIndexOfOne = bitsString.lastIndexOf(1);
return firstIndexOfZero < 0 || firstIndexOfZero > lastIndexOfOne;
}
function countOnes(bitsString) {
return bitsString
.split('')
.reduce((acc, bit) => acc += parseInt(bit, 10), 0);
}
module.exports = {
getCIDRSuffix
};

View File

@ -24,7 +24,6 @@
const { pushValToArrayMax, safeGetenv } = process.binding('util');
const constants = process.binding('constants').os;
const { deprecate } = require('internal/util');
const { getCIDRSuffix } = require('internal/os');
const isWindows = process.platform === 'win32';
const { ERR_SYSTEM_ERROR } = require('internal/errors');
@ -144,19 +143,67 @@ function endianness() {
}
endianness[Symbol.toPrimitive] = () => kEndianness;
// Returns the number of ones in the binary representation of the decimal
// number.
function countBinaryOnes(n) {
let count = 0;
// Remove one "1" bit from n until n is the power of 2. This iterates k times
// while k is the number of "1" in the binary representation.
// For more check https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators
while (n !== 0) {
n = n & (n - 1);
count++;
}
return count;
}
function getCIDR({ address, netmask, family }) {
let ones = 0;
let split = '.';
let range = 10;
let groupLength = 8;
let hasZeros = false;
if (family === 'IPv6') {
split = ':';
range = 16;
groupLength = 16;
}
const parts = netmask.split(split);
for (var i = 0; i < parts.length; i++) {
if (parts[i] !== '') {
const binary = parseInt(parts[i], range);
const tmp = countBinaryOnes(binary);
ones += tmp;
if (hasZeros) {
if (tmp !== 0) {
return null;
}
} else if (tmp !== groupLength) {
if ((binary & 1) !== 0) {
return null;
}
hasZeros = true;
}
}
}
return `${address}/${ones}`;
}
function networkInterfaces() {
const interfaceAddresses = getInterfaceAddresses();
return Object.entries(interfaceAddresses).reduce((acc, [key, val]) => {
acc[key] = val.map((v) => {
const protocol = v.family.toLowerCase();
const suffix = getCIDRSuffix(v.netmask, protocol);
const cidr = suffix ? `${v.address}/${suffix}` : null;
const keys = Object.keys(interfaceAddresses);
for (var i = 0; i < keys.length; i++) {
const arr = interfaceAddresses[keys[i]];
for (var j = 0; j < arr.length; j++) {
arr[j].cidr = getCIDR(arr[j]);
}
}
return Object.assign({}, v, { cidr });
});
return acc;
}, {});
return interfaceAddresses;
}
module.exports = {

View File

@ -132,7 +132,6 @@
'lib/internal/modules/esm/translators.js',
'lib/internal/safe_globals.js',
'lib/internal/net.js',
'lib/internal/os.js',
'lib/internal/priority_queue.js',
'lib/internal/process/esm_loader.js',
'lib/internal/process/main_thread_only.js',

View File

@ -1,32 +0,0 @@
// Flags: --expose-internals
'use strict';
require('../common');
const assert = require('assert');
const getCIDRSuffix = require('internal/os').getCIDRSuffix;
const specs = [
// valid
['128.0.0.0', 'ipv4', 1],
['255.0.0.0', 'ipv4', 8],
['255.255.255.128', 'ipv4', 25],
['255.255.255.255', 'ipv4', 32],
['ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', 'ipv6', 128],
['ffff:ffff:ffff:ffff::', 'ipv6', 64],
['ffff:ffff:ffff:ff80::', 'ipv6', 57],
// invalid
['255.0.0.1', 'ipv4', null],
['255.255.9.0', 'ipv4', null],
['255.255.1.0', 'ipv4', null],
['ffff:ffff:43::', 'ipv6', null],
['ffff:ffff:ffff:1::', 'ipv6', null]
];
specs.forEach(([mask, protocol, expectedSuffix]) => {
const actualSuffix = getCIDRSuffix(mask, protocol);
assert.strictEqual(
actualSuffix, expectedSuffix,
`Mask: ${mask}, expected: ${expectedSuffix}, actual: ${actualSuffix}`
);
});

View File

@ -130,8 +130,8 @@ switch (platform) {
const expected = [{
address: '127.0.0.1',
netmask: '255.0.0.0',
mac: '00:00:00:00:00:00',
family: 'IPv4',
mac: '00:00:00:00:00:00',
internal: true,
cidr: '127.0.0.1/8'
}];
@ -146,8 +146,8 @@ switch (platform) {
const expected = [{
address: '127.0.0.1',
netmask: '255.0.0.0',
mac: '00:00:00:00:00:00',
family: 'IPv4',
mac: '00:00:00:00:00:00',
internal: true,
cidr: '127.0.0.1/8'
}];