/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// https://url.spec.whatwg.org/#ends-in-a-number-checker bool EndsInANumber(const nsCString& input) { // 1. Let parts be the result of strictly splitting input on U+002E (.).
nsTArray<nsDependentCSubstring> parts; for (const nsDependentCSubstring& part : input.Split('.')) {
parts.AppendElement(part);
}
if (parts.Length() == 0) { returnfalse;
}
// 2.If the last item in parts is the empty string, then: // 1. If parts’s size is 1, then return false. // 2. Remove the last item from parts. if (parts.LastElement().IsEmpty()) { if (parts.Length() == 1) { returnfalse;
}
Unused << parts.PopLastElement();
}
// 3. Let last be the last item in parts. const nsDependentCSubstring& last = parts.LastElement();
// 4. If last is non-empty and contains only ASCII digits, then return true. // The erroneous input "09" will be caught by the IPv4 parser at a later // stage. if (!last.IsEmpty()) { if (ContainsOnlyAsciiDigits(last)) { returntrue;
}
}
// 5. If parsing last as an IPv4 number does not return failure, then return // true. This is equivalent to checking that last is "0X" or "0x", followed by // zero or more ASCII hex digits. if (StringBeginsWith(last, "0x"_ns) || StringBeginsWith(last, "0X"_ns)) { if (ContainsOnlyAsciiHexDigits(Substring(last, 2))) { returntrue;
}
}
returnfalse;
}
nsresult ParseIPv4Number10(const nsACString& input, uint32_t& number,
uint32_t maxNumber) {
uint64_t value = 0; constchar* current = input.BeginReading(); constchar* end = input.EndReading(); for (; current < end; ++current) { char c = *current;
MOZ_ASSERT(c >= '0' && c <= '9');
value *= 10;
value += c - '0';
} if (value <= maxNumber) {
number = value; return NS_OK;
}
// The error case
number = 0; return NS_ERROR_FAILURE;
}
nsresult ParseIPv4Number(const nsACString& input, int32_t base,
uint32_t& number, uint32_t maxNumber) { // Accumulate in the 64-bit value
uint64_t value = 0; constchar* current = input.BeginReading(); constchar* end = input.EndReading(); switch (base) { case 16:
++current;
[[fallthrough]]; case 8:
++current; break; case 10: default: break;
} for (; current < end; ++current) {
value *= base; char c = *current;
MOZ_ASSERT((base == 10 && IsAsciiDigit(c)) ||
(base == 8 && c >= '0' && c <= '7') ||
(base == 16 && IsAsciiHexDigit(c))); if (IsAsciiDigit(c)) {
value += c - '0';
} elseif (c >= 'a' && c <= 'f') {
value += c - 'a' + 10;
} elseif (c >= 'A' && c <= 'F') {
value += c - 'A' + 10;
}
}
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
nsresult NormalizeIPv4(const nsACString& host, nsCString& result) {
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
// Use "length" rather than host.Length() after call to // ValidateIPv4Number because of potential trailing period.
nsDependentCSubstring filteredHost; bool trailingDot = false; if (host.Length() > 0 && host.Last() == '.') {
trailingDot = true;
filteredHost.Rebind(host.BeginReading(), host.Length() - 1);
} else {
filteredHost.Rebind(host.BeginReading(), host.Length());
}
// parse the last part first
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;
}
// parse remaining parts starting from first part
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 += number << (8 * (3 - i));
}
// A special case for ipv4 URL like "127." should have the same result as // "127". if (dotCount == 1 && dotIndex[0] == length - 1) {
ipv4 = (ipv4 & 0xff000000) >> 24;
}
// 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. // // length is assumed to be <= host.Length(); the caller is responsible for that // // Note that the value returned is guaranteed to be in [-1, 3] range.
int32_t ValidateIPv4Number(const nsACString& host, int32_t bases[4],
int32_t dotIndex[3], bool& onlyBase10,
int32_t length, bool trailingDot) {
MOZ_ASSERT(length <= (int32_t)host.Length()); if (length <= 0) { return -1;
}
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 == '.') { // A dot should not follow a dot, or be first - it can follow an x though. if (!(lastWasNumber ||
(i >= 2 && (host[i - 1] == 'X' || host[i - 1] == 'x') &&
host[i - 2] == '0')) ||
(i == (length - 1) && trailingDot)) { return -1;
}
if (dotCount > 2) { return -1;
}
lastWasNumber = false;
dotIndex[dotCount] = i;
dotCount++;
} elseif (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;
} elseif (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;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.