зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1304605: Part 2. Optimizing NormalizeIPv4. Seems to be 4-6x faster. r=valentin.gosu
MozReview-Commit-ID: FjkNmbW5I93 --HG-- extra : rebase_source : f3b6e2c07e9376b6b25a62a93cffc4a58584e243
This commit is contained in:
Родитель
ff6839a8e1
Коммит
da5b96ea90
|
@ -419,128 +419,213 @@ nsStandardURL::InvalidateCache(bool invalidateCachedFile)
|
|||
mSpecEncoding = eEncoding_Unknown;
|
||||
}
|
||||
|
||||
// |base| should be 8, 10 or 16. Not check the precondition for performance.
|
||||
/* static */ inline bool
|
||||
nsStandardURL::IsValidOfBase(unsigned char c, const uint32_t base) {
|
||||
MOZ_ASSERT(base == 8 || base == 10 || base == 16, "invalid base");
|
||||
if ('0' <= c && c <= '7') {
|
||||
return true;
|
||||
} else if (c == '8' || c== '9') {
|
||||
return base != 8;
|
||||
} else if (('a' <= c && c <= 'f') || ('A' <= c && c <= 'F')) {
|
||||
return base == 16;
|
||||
// Return the number of "dots" in the string, or -1 if invalid. Note that the
|
||||
// number of relevant entries in the bases/starts/ends arrays is number of
|
||||
// dots + 1.
|
||||
// Since the trailing dot is allowed, we pass and adjust "length".
|
||||
//
|
||||
// length is assumed to be <= host.Length(); the callers is responsible for that
|
||||
//
|
||||
// Note that the value returned is guaranteed to be in [-1, 3] range.
|
||||
inline int32_t
|
||||
ValidateIPv4Number(const nsCSubstring& host,
|
||||
int32_t bases[4], int32_t dotIndex[3],
|
||||
bool& onlyBase10, int32_t& length)
|
||||
{
|
||||
MOZ_ASSERT(length <= (int32_t)host.Length());
|
||||
if (length <= 0) {
|
||||
return -1;
|
||||
}
|
||||
return false;
|
||||
|
||||
bool lastWasNumber = false; // We count on this being false for i == 0
|
||||
int32_t dotCount = 0;
|
||||
onlyBase10 = true;
|
||||
|
||||
for (int32_t i = 0; i < length; i++) {
|
||||
char current = host[i];
|
||||
if (current == '.') {
|
||||
if (!lastWasNumber) { // A dot should not follow an X or a dot, or be first
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (dotCount > 0 && i == (length - 1)) { // Trailing dot is OK; shorten and return
|
||||
length--;
|
||||
return dotCount;
|
||||
}
|
||||
|
||||
if (dotCount > 2) {
|
||||
return -1;
|
||||
}
|
||||
lastWasNumber = false;
|
||||
dotIndex[dotCount] = i;
|
||||
dotCount ++;
|
||||
} else if (current == 'X' || current == 'x') {
|
||||
if (!lastWasNumber || // An X should not follow an X or a dot or be first
|
||||
i == (length - 1) || // No trailing Xs allowed
|
||||
(dotCount == 0 && i != 1) || // If we had no dots, an X should be second
|
||||
host[i-1] != '0' || // X should always follow a 0. Guaranteed i > 0
|
||||
// as lastWasNumber is true
|
||||
(dotCount > 0 && host[i - 2] != '.')) { // And that zero follows a dot if it exists
|
||||
return -1;
|
||||
}
|
||||
lastWasNumber = false;
|
||||
bases[dotCount] = 16;
|
||||
onlyBase10 = false;
|
||||
|
||||
} else if (current == '0') {
|
||||
if (i < length - 1 && // Trailing zero doesn't signal octal
|
||||
host[i + 1] != '.' && // Lone zero is not octal
|
||||
(i == 0 || host[i - 1] == '.')) { // Zero at start or following a dot is
|
||||
// a candidate for octal
|
||||
bases[dotCount] = 8; // This will turn to 16 above if X shows up
|
||||
onlyBase10 = false;
|
||||
}
|
||||
lastWasNumber = true;
|
||||
|
||||
} else if (current >= '1' && current <= '7') {
|
||||
lastWasNumber = true;
|
||||
|
||||
} else if (current >= '8' && current <= '9') {
|
||||
if (bases[dotCount] == 8) {
|
||||
return -1;
|
||||
}
|
||||
lastWasNumber = true;
|
||||
|
||||
} else if ((current >= 'a' && current <= 'f') ||
|
||||
(current >= 'A' && current <= 'F')) {
|
||||
if (bases[dotCount] != 16) {
|
||||
return -1;
|
||||
}
|
||||
lastWasNumber = true;
|
||||
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return dotCount;
|
||||
}
|
||||
|
||||
/* static */ inline nsresult
|
||||
nsStandardURL::ParseIPv4Number(nsCString &input, uint32_t &number)
|
||||
inline nsresult
|
||||
ParseIPv4Number10(const nsCSubstring& input, uint32_t& number, uint32_t maxNumber)
|
||||
{
|
||||
if (input.Length() == 0) {
|
||||
return NS_ERROR_FAILURE;
|
||||
uint64_t value = 0;
|
||||
const char* current = input.BeginReading();
|
||||
const char* end = input.EndReading();
|
||||
for (; current < end; ++current) {
|
||||
char c = *current;
|
||||
MOZ_ASSERT(c >= '0' && c <= '9');
|
||||
value *= 10;
|
||||
value += c - '0';
|
||||
}
|
||||
uint32_t base;
|
||||
uint32_t prefixLength = 0;
|
||||
|
||||
if (input[0] == '0') {
|
||||
if (input.Length() == 1) {
|
||||
base = 10;
|
||||
} else if (input[1] == 'x' || input[1] == 'X') {
|
||||
base = 16;
|
||||
prefixLength = 2;
|
||||
} else {
|
||||
base = 8;
|
||||
prefixLength = 1;
|
||||
}
|
||||
} else {
|
||||
base = 10;
|
||||
}
|
||||
if (prefixLength == input.Length()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
// ignore leading zeros to calculate the valid length of number
|
||||
while (prefixLength < input.Length() && input[prefixLength] == '0') {
|
||||
prefixLength++;
|
||||
}
|
||||
// all zero case
|
||||
if (prefixLength == input.Length()) {
|
||||
number = 0;
|
||||
if (value <= maxNumber) {
|
||||
number = value;
|
||||
return NS_OK;
|
||||
}
|
||||
// overflow case
|
||||
if (input.Length() - prefixLength > 16) {
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
// The error case
|
||||
number = 0;
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
inline nsresult
|
||||
ParseIPv4Number(const nsCSubstring& input, int32_t base, uint32_t& number, uint32_t maxNumber)
|
||||
{
|
||||
// Accumulate in the 64-bit value
|
||||
uint64_t value = 0;
|
||||
const char* current = input.BeginReading();
|
||||
const char* end = input.EndReading();
|
||||
switch(base) {
|
||||
case 16:
|
||||
++current;
|
||||
MOZ_FALLTHROUGH;
|
||||
case 8:
|
||||
++current;
|
||||
break;
|
||||
case 10:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
for (uint32_t i = prefixLength; i < input.Length(); ++i) {
|
||||
if (!IsValidOfBase(input[i], base)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
for (; current < end; ++current) {
|
||||
value *= base;
|
||||
char c = *current;
|
||||
MOZ_ASSERT((base == 10 && isdigit(c)) ||
|
||||
(base == 8 && c >= '0' && c <= '7') ||
|
||||
(base == 16 && isxdigit(c)));
|
||||
if (isdigit(c)) {
|
||||
value += c - '0';
|
||||
} else if (c >= 'a' && c <= 'f') {
|
||||
value += c - 'a' + 10;
|
||||
} else if (c >= 'A' && c <= 'F') {
|
||||
value += c - 'A' + 10;
|
||||
}
|
||||
}
|
||||
const char* fmt = "";
|
||||
switch (base) {
|
||||
case 8:
|
||||
fmt = "%llo";
|
||||
break;
|
||||
case 10:
|
||||
fmt = "%lli";
|
||||
break;
|
||||
case 16:
|
||||
fmt = "%llx";
|
||||
break;
|
||||
default:
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
uint64_t number64;
|
||||
if (PR_sscanf(input.get(), fmt, &number64) == 1 &&
|
||||
number64 <= 0xffffffffu) {
|
||||
number = number64;
|
||||
return NS_OK;
|
||||
|
||||
if (value <= maxNumber) {
|
||||
number = value;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// The error case
|
||||
number = 0;
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// IPv4 parser spec: https://url.spec.whatwg.org/#concept-ipv4-parser
|
||||
/* static */ nsresult
|
||||
nsStandardURL::NormalizeIPv4(const nsCSubstring &host, nsCString &result)
|
||||
nsStandardURL::NormalizeIPv4(const nsCSubstring& host, nsCString& result)
|
||||
{
|
||||
if (host.Length() == 0 ||
|
||||
host[0] < '0' || '9' < host[0] || // bail-out fast
|
||||
FindInReadable(NS_LITERAL_CSTRING(".."), host)) {
|
||||
int32_t bases[4] = {10,10,10,10};
|
||||
bool onlyBase10 = true; // Track this as a special case
|
||||
int32_t dotIndex[3]; // The positions of the dots in the string
|
||||
|
||||
// The length may be adjusted by ValidateIPv4Number (ignoring the trailing period)
|
||||
// so use "length", rather than host.Length() after that call.
|
||||
int32_t length = static_cast<int32_t>(host.Length());
|
||||
int32_t dotCount = ValidateIPv4Number(host, bases, dotIndex,
|
||||
onlyBase10, length);
|
||||
if (dotCount < 0 || length <= 0) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsTArray<nsCString> parts;
|
||||
if (!ParseString(host, '.', parts) ||
|
||||
parts.Length() == 0 ||
|
||||
parts.Length() > 4) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
uint32_t n = 0;
|
||||
nsTArray<int32_t> numbers;
|
||||
for (uint32_t i = 0; i < parts.Length(); ++i) {
|
||||
if (NS_FAILED(ParseIPv4Number(parts[i], n))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
numbers.AppendElement(n);
|
||||
}
|
||||
uint32_t ipv4 = numbers.LastElement();
|
||||
// Max values specified by the spec
|
||||
static const uint32_t upperBounds[] = {0xffffffffu, 0xffffffu,
|
||||
0xffffu, 0xffu};
|
||||
if (ipv4 > upperBounds[numbers.Length() - 1]) {
|
||||
uint32_t ipv4;
|
||||
int32_t start = (dotCount > 0 ? dotIndex[dotCount-1] + 1 : 0);
|
||||
|
||||
nsresult res;
|
||||
// Doing a special case for all items being base 10 gives ~35% speedup
|
||||
res = (onlyBase10 ?
|
||||
ParseIPv4Number10(Substring(host, start, length - start),
|
||||
ipv4, upperBounds[dotCount]) :
|
||||
ParseIPv4Number(Substring(host, start, length - start),
|
||||
bases[dotCount],
|
||||
ipv4, upperBounds[dotCount]));
|
||||
if (NS_FAILED(res)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
for (uint32_t i = 0; i < numbers.Length() - 1; ++i) {
|
||||
if (numbers[i] > 255) {
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
int32_t lastUsed = -1;
|
||||
for (int32_t i = 0; i < dotCount; i++) {
|
||||
uint32_t number;
|
||||
start = lastUsed + 1;
|
||||
lastUsed = dotIndex[i];
|
||||
res = (onlyBase10 ?
|
||||
ParseIPv4Number10(Substring(host, start, lastUsed - start),
|
||||
number, 255) :
|
||||
ParseIPv4Number(Substring(host, start, lastUsed - start),
|
||||
bases[i], number, 255));
|
||||
if (NS_FAILED(res)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
ipv4 += numbers[i] << (8 * (3 - i));
|
||||
ipv4 += number << (8 * (3 - i));
|
||||
}
|
||||
|
||||
uint8_t ipSegments[4];
|
||||
NetworkEndian::writeUint32(ipSegments, ipv4);
|
||||
result = nsPrintfCString("%d.%d.%d.%d", ipSegments[0], ipSegments[1],
|
||||
ipSegments[2], ipSegments[3]);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -192,7 +192,6 @@ private:
|
|||
|
||||
bool ValidIPv6orHostname(const char *host, uint32_t aLen);
|
||||
static bool IsValidOfBase(unsigned char c, const uint32_t base);
|
||||
static nsresult ParseIPv4Number(nsCString &input, uint32_t &number);
|
||||
nsresult NormalizeIDN(const nsCSubstring &host, nsCString &result);
|
||||
void CoalescePath(netCoalesceFlags coalesceFlag, char *path);
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче