Bug 1889536 - Migrate IDNA handling to ICU4X. r=necko-reviewers,geckoview-reviewers,credential-management-reviewers,home-newtab-reviewers,valentin,owlish,dimi,thecount,supply-chain-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D206579
This commit is contained in:
Henri Sivonen 2024-07-09 11:26:43 +00:00
Родитель 2df8575a28
Коммит 3c60d406b9
79 изменённых файлов: 5692 добавлений и 1818 удалений

21
Cargo.lock сгенерированный
Просмотреть файл

@ -145,6 +145,15 @@ version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
[[package]]
name = "arraystring"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d517c467117e1d8ca795bc8cc90857ff7f79790cca0e26f6e9462694ece0185"
dependencies = [
"typenum",
]
[[package]]
name = "arrayvec"
version = "0.7.2"
@ -2315,6 +2324,7 @@ dependencies = [
"geckoservo",
"gkrust_utils",
"http_sfv",
"idna_glue",
"jog",
"jsrust_shared",
"kvstore",
@ -2995,6 +3005,17 @@ dependencies = [
"utf8_iter",
]
[[package]]
name = "idna_glue"
version = "0.1.0"
dependencies = [
"arraystring",
"idna",
"nserror",
"nsstring",
"percent-encoding",
]
[[package]]
name = "indexmap"
version = "1.999.999"

Просмотреть файл

@ -8,6 +8,7 @@ members = [
"browser/app/nmhproxy/",
"js/src/frontend/smoosh",
"js/src/rust",
"netwerk/base/idna_glue",
"netwerk/test/http3server",
"security/manager/ssl/builtins",
"security/manager/ssl/tests/unit/test_builtins",

Просмотреть файл

@ -727,7 +727,7 @@ var gIdentityHandler = {
);
}
try {
return this._IDNService.convertToDisplayIDN(this._uri.host, {});
return this._IDNService.convertToDisplayIDN(this._uri.host);
} catch (e) {
// If something goes wrong (e.g. host is an IP address) just fail back
// to the full domain.

Просмотреть файл

@ -29,7 +29,7 @@ XPCOMUtils.defineLazyServiceGetter(
*/
function handleIDNHost(hostname) {
try {
return lazy.IDNService.convertToDisplayIDN(hostname, {});
return lazy.IDNService.convertToDisplayIDN(hostname);
} catch (e) {
// If something goes wrong (e.g. host is an IP address) just fail back
// to the full domain.

Просмотреть файл

@ -379,6 +379,8 @@ export class UrlbarValueFormatter {
let IDNService = Cc["@mozilla.org/network/idn-service;1"].getService(
Ci.nsIIDNService
);
// XXX This should probably convert to display IDN instead.
// https://bugzilla.mozilla.org/show_bug.cgi?id=1906048
baseDomain = IDNService.convertACEtoUTF8(baseDomain);
}
} catch (e) {}

Просмотреть файл

@ -1340,7 +1340,7 @@ class StorageAccessPermissionPrompt extends PermissionPromptForRequest {
prettifyHostPort(hostport) {
let [host, port] = hostport.split(":");
host = lazy.IDNService.convertToDisplayIDN(host, {});
host = lazy.IDNService.convertToDisplayIDN(host);
if (port) {
return `${host}:${port}`;
}

1
config/external/icu/defs.mozbuild поставляемый
Просмотреть файл

@ -17,6 +17,7 @@ DEFINES["UCONFIG_NO_LEGACY_CONVERSION"] = True
DEFINES["UCONFIG_NO_TRANSLITERATION"] = True
DEFINES["UCONFIG_NO_REGULAR_EXPRESSIONS"] = True
DEFINES["UCONFIG_NO_BREAK_ITERATION"] = True
DEFINES["UCONFIG_NO_IDNA"] = True
# We don't need to pass data to and from legacy char* APIs.
DEFINES["U_CHARSET_IS_UTF8"] = True

Просмотреть файл

@ -25,7 +25,7 @@ const idnService = Cc["@mozilla.org/network/idn-service;1"].getService(
* a readable ASCII hostname or a Unicode hostname.
*/
function getUnicodeHostname(hostname) {
return idnService.convertToDisplayIDN(hostname, {});
return idnService.convertToDisplayIDN(hostname);
}
/**

Просмотреть файл

@ -5996,6 +5996,11 @@ already_AddRefed<nsIURI> nsDocShell::AttemptURIFixup(
//
// Since we don't have access to the exact original string
// that was entered by the user, this will just have to do.
//
// XXX: Since we are not trying to use the result as an
// actual domain name, label-wise Punycode decode would
// likely be more appropriate than the full ToUnicode
// operation.
bool isACE;
nsAutoCString utf8Host;
nsCOMPtr<nsIIDNService> idnSrv =

Просмотреть файл

@ -12,7 +12,6 @@
#include "HTMLSplitOnSpacesTokenizer.h"
#include "nsContentUtils.h"
#include "nsCRTGlue.h"
#include "nsIIDNService.h"
#include "nsIIOService.h"
#include "nsNetCID.h"
#include "nsNetUtil.h"
@ -255,35 +254,26 @@ bool EmailInputType::PunycodeEncodeEmailAddress(const nsAString& aEmail,
return true;
}
nsCOMPtr<nsIIDNService> idnSrv = do_GetService(NS_IDNSERVICE_CONTRACTID);
if (!idnSrv) {
NS_ERROR("nsIIDNService isn't present!");
return false;
}
uint32_t indexOfDomain = *aIndexOfAt + 1;
const nsDependentCSubstring domain = Substring(value, indexOfDomain);
bool ace;
if (NS_SUCCEEDED(idnSrv->IsACE(domain, &ace)) && !ace) {
nsAutoCString domainACE;
if (NS_FAILED(idnSrv->ConvertUTF8toACE(domain, domainACE))) {
nsAutoCString domainACE;
NS_DomainToASCII(domain, domainACE);
// NS_DomainToASCII does not check length (removed in bug 1788115), so we
// check for that limit here as required by the spec:
// https://html.spec.whatwg.org/#valid-e-mail-address
nsCCharSeparatedTokenizer tokenizer(domainACE, '.');
while (tokenizer.hasMoreTokens()) {
// XXX should check each token for not starting or ending with hyphen;
// https://bugzilla.mozilla.org/show_bug.cgi?id=1890466
if (tokenizer.nextToken().Length() > 63) {
return false;
}
// Bug 1788115 removed the 63 character limit from the
// IDNService::ConvertUTF8toACE so we check for that limit here as required
// by the spec: https://html.spec.whatwg.org/#valid-e-mail-address
nsCCharSeparatedTokenizer tokenizer(domainACE, '.');
while (tokenizer.hasMoreTokens()) {
if (tokenizer.nextToken().Length() > 63) {
return false;
}
}
value.Replace(indexOfDomain, domain.Length(), domainACE);
}
value.Replace(indexOfDomain, domain.Length(), domainACE);
aEncodedEmail = value;
return true;
}

Просмотреть файл

@ -132,7 +132,7 @@ class EmailInputType : public SingleLineTextInputTypeBase {
* Returns true in all cases unless an attempt to punycode encode fails. If
* false is returned, aEncodedEmail has not been set.
*
* This function exists because ConvertUTF8toACE() splits on ".", meaning that
* This function exists because NS_DomainToASCII() splits on ".", meaning that
* for 'user.name@sld.tld' it would treat "name@sld" as a label. We want to
* encode the domain part only.
*/

Просмотреть файл

@ -8,7 +8,7 @@
#include "mozilla/DebugOnly.h"
#include "nsCOMPtr.h"
#include "nsIIDNService.h"
#include "nsNetUtil.h"
#include "nsNetCID.h"
#include "nsServiceManagerUtils.h"
@ -32,17 +32,10 @@ bool PeerIdentity::Equals(const nsAString& aOtherString) const {
nsString otherHost;
GetHost(aOtherString, otherHost);
nsresult rv;
nsCOMPtr<nsIIDNService> idnService =
do_GetService("@mozilla.org/network/idn-service;1", &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return host == otherHost;
}
nsCString normHost;
GetNormalizedHost(idnService, host, normHost);
GetNormalizedHost(host, normHost);
nsCString normOtherHost;
GetNormalizedHost(idnService, otherHost, normOtherHost);
GetNormalizedHost(otherHost, normOtherHost);
return normHost == normOtherHost;
}
@ -67,12 +60,11 @@ void PeerIdentity::GetHost(const nsAString& aPeerIdentity, nsAString& aHost) {
}
/* static */
void PeerIdentity::GetNormalizedHost(const nsCOMPtr<nsIIDNService>& aIdnService,
const nsAString& aHost,
void PeerIdentity::GetNormalizedHost(const nsAString& aHost,
nsACString& aNormalizedHost) {
const nsCString chost = NS_ConvertUTF16toUTF8(aHost);
DebugOnly<nsresult> rv =
aIdnService->ConvertUTF8toACE(chost, aNormalizedHost);
NS_DomainToASCIIAllowAnyGlyphfulASCII(chost, aNormalizedHost);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"Failed to convert UTF-8 host to ASCII");
}

Просмотреть файл

@ -10,10 +10,6 @@
#include "nsISupportsImpl.h"
#include "nsString.h"
template <class T>
class nsCOMPtr;
class nsIIDNService;
namespace mozilla {
/**
@ -42,8 +38,7 @@ class PeerIdentity final {
static void GetUser(const nsAString& aPeerIdentity, nsAString& aUser);
static void GetHost(const nsAString& aPeerIdentity, nsAString& aHost);
static void GetNormalizedHost(const nsCOMPtr<nsIIDNService>& aIdnService,
const nsAString& aHost,
static void GetNormalizedHost(const nsAString& aHost,
nsACString& aNormalizedHost);
nsString mPeerIdentity;

Просмотреть файл

@ -19,7 +19,6 @@
#include "pk11pub.h"
#include "nsNetCID.h"
#include "nsIIDNService.h"
#include "nsILoadContext.h"
#include "nsEffectiveTLDService.h"
#include "nsServiceManagerUtils.h"
@ -2186,9 +2185,9 @@ bool PeerConnectionImpl::HostnameInPref(const char* aPref,
};
nsCString domainList;
nsresult nr = Preferences::GetCString(aPref, domainList);
nsresult rv = Preferences::GetCString(aPref, domainList);
if (NS_FAILED(nr)) {
if (NS_FAILED(rv)) {
return false;
}
@ -2198,20 +2197,12 @@ bool PeerConnectionImpl::HostnameInPref(const char* aPref,
return false;
}
// Get UTF8 to ASCII domain name normalization service
nsresult rv;
nsCOMPtr<nsIIDNService> idnService =
do_GetService("@mozilla.org/network/idn-service;1", &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return false;
}
// Test each domain name in the comma separated list
// after converting from UTF8 to ASCII. Each domain
// must match exactly or have a single leading '*.' wildcard.
for (const nsACString& each : domainList.Split(',')) {
nsCString domainPattern;
rv = idnService->ConvertUTF8toACE(each, domainPattern);
rv = NS_DomainToASCIIAllowAnyGlyphfulASCII(each, domainPattern);
if (NS_SUCCEEDED(rv)) {
if (HostInDomain(aHostName, domainPattern)) {
return true;

Просмотреть файл

@ -18,7 +18,7 @@
#include "nsIObserverService.h"
#include "nsIURI.h"
#include "nsIPermission.h"
#include "nsIIDNService.h"
#include "nsNetUtil.h"
#include "nsICookiePermission.h"
#include "nsPrintfCString.h"
@ -168,16 +168,7 @@ nsresult StorageObserver::GetOriginScope(const char16_t* aData,
NS_ConvertUTF16toUTF8 domain(aData);
nsAutoCString convertedDomain;
nsCOMPtr<nsIIDNService> converter = do_GetService(NS_IDNSERVICE_CONTRACTID);
if (converter) {
// Convert the domain name to the ACE format
rv = converter->ConvertUTF8toACE(domain, convertedDomain);
} else {
// In case the IDN service is not available, this is the best we can come
// up with!
rv = NS_EscapeURL(domain, esc_OnlyNonASCII | esc_AlwaysCopy,
convertedDomain, fallible);
}
rv = NS_DomainToASCIIAllowAnyGlyphfulASCII(domain, convertedDomain);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}

Просмотреть файл

@ -1,118 +0,0 @@
/* 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/. */
#include "gtest/gtest.h"
#include "mozilla/intl/IDNA.h"
#include "mozilla/Span.h"
#include "TestBuffer.h"
namespace mozilla::intl {
TEST(IntlIDNA, LabelToUnicodeBasic)
{
auto createResult = IDNA::TryCreate(IDNA::ProcessingType::NonTransitional);
ASSERT_TRUE(createResult.isOk());
auto idna = createResult.unwrap();
// 'A' and 'ª' are mapped to 'a'.
TestBuffer<char16_t> buf16;
auto convertResult = idna->LabelToUnicode(MakeStringSpan(u"Aa\u00aa"), buf16);
ASSERT_TRUE(convertResult.isOk());
intl::IDNA::Info info = convertResult.unwrap();
ASSERT_TRUE(!info.HasErrors());
ASSERT_EQ(buf16.get_string_view(), u"aaa");
buf16.clear();
// For nontransitional processing 'ß' is still mapped to 'ß'.
convertResult = idna->LabelToUnicode(MakeStringSpan(u"Faß"), buf16);
ASSERT_TRUE(convertResult.isOk());
info = convertResult.unwrap();
ASSERT_TRUE(!info.HasErrors());
ASSERT_EQ(buf16.get_string_view(), u"faß");
}
TEST(IntlIDNA, LabelToUnicodeBasicTransitional)
{
auto createResult = IDNA::TryCreate(IDNA::ProcessingType::Transitional);
ASSERT_TRUE(createResult.isOk());
auto idna = createResult.unwrap();
TestBuffer<char16_t> buf16;
// For transitional processing 'ß' will be mapped to 'ss'.
auto convertResult = idna->LabelToUnicode(MakeStringSpan(u"Faß"), buf16);
ASSERT_TRUE(convertResult.isOk());
intl::IDNA::Info info = convertResult.unwrap();
ASSERT_TRUE(!info.HasErrors());
ASSERT_EQ(buf16.get_string_view(), u"fass");
}
TEST(IntlIDNA, LabelToUnicodeHasErrors)
{
auto createResult = IDNA::TryCreate(IDNA::ProcessingType::NonTransitional);
ASSERT_TRUE(createResult.isOk());
auto idna = createResult.unwrap();
TestBuffer<char16_t> buf16;
// \u0378 is a reserved charactor, conversion should be disallowed.
auto convertResult = idna->LabelToUnicode(MakeStringSpan(u"\u0378"), buf16);
ASSERT_TRUE(convertResult.isOk());
intl::IDNA::Info info = convertResult.unwrap();
ASSERT_TRUE(info.HasErrors());
buf16.clear();
// FULL STOP '.' is not allowed.
convertResult = idna->LabelToUnicode(MakeStringSpan(u"a.b"), buf16);
ASSERT_TRUE(convertResult.isOk());
info = convertResult.unwrap();
ASSERT_TRUE(info.HasErrors());
}
TEST(IntlIDNA, LabelToUnicodeHasInvalidPunycode)
{
auto createResult = IDNA::TryCreate(IDNA::ProcessingType::NonTransitional);
ASSERT_TRUE(createResult.isOk());
auto idna = createResult.unwrap();
TestBuffer<char16_t> buf16;
auto convertResult =
idna->LabelToUnicode(MakeStringSpan(u"xn--a-ecp.ru"), buf16);
ASSERT_TRUE(convertResult.isOk());
intl::IDNA::Info info = convertResult.unwrap();
ASSERT_TRUE(info.HasInvalidPunycode());
buf16.clear();
convertResult = idna->LabelToUnicode(MakeStringSpan(u"xn--0.pt"), buf16);
ASSERT_TRUE(convertResult.isOk());
info = convertResult.unwrap();
ASSERT_TRUE(info.HasInvalidPunycode());
}
TEST(IntlIDNA, LabelToUnicodeHasInvalidHyphen)
{
auto createResult = IDNA::TryCreate(IDNA::ProcessingType::NonTransitional);
ASSERT_TRUE(createResult.isOk());
auto idna = createResult.unwrap();
TestBuffer<char16_t> buf16;
// Leading hyphen.
auto convertResult = idna->LabelToUnicode(MakeStringSpan(u"-a"), buf16);
ASSERT_TRUE(convertResult.isOk());
intl::IDNA::Info info = convertResult.unwrap();
ASSERT_TRUE(info.HasErrors());
ASSERT_TRUE(info.HasInvalidHyphen());
buf16.clear();
// Trailing hyphen.
convertResult = idna->LabelToUnicode(MakeStringSpan(u"a-"), buf16);
ASSERT_TRUE(convertResult.isOk());
info = convertResult.unwrap();
ASSERT_TRUE(info.HasInvalidHyphen());
buf16.clear();
// Contains hyphens in both 3rd and 4th positions.
convertResult = idna->LabelToUnicode(MakeStringSpan(u"ab--c"), buf16);
ASSERT_TRUE(convertResult.isOk());
info = convertResult.unwrap();
ASSERT_TRUE(info.HasInvalidHyphen());
}
} // namespace mozilla::intl

Просмотреть файл

@ -12,7 +12,6 @@ UNIFIED_SOURCES += [
"TestDateIntervalFormat.cpp",
"TestDateTimeFormat.cpp",
"TestDisplayNames.cpp",
"TestIDNA.cpp",
"TestListFormat.cpp",
"TestLocale.cpp",
"TestLocaleCanonicalizer.cpp",

Просмотреть файл

@ -20,7 +20,6 @@ EXPORTS.mozilla.intl = [
"src/ICU4CGlue.h",
"src/ICU4CLibrary.h",
"src/ICUError.h",
"src/IDNA.h",
"src/ListFormat.h",
"src/Locale.h",
"src/LocaleCanonicalizer.h",
@ -52,7 +51,6 @@ UNIFIED_SOURCES += [
"src/DisplayNames.cpp",
"src/ICU4CGlue.cpp",
"src/ICU4CLibrary.cpp",
"src/IDNA.cpp",
"src/ListFormat.cpp",
"src/Locale.cpp",
"src/LocaleCanonicalizer.cpp",

Просмотреть файл

@ -1,26 +0,0 @@
/* 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/. */
#include "mozilla/intl/IDNA.h"
namespace mozilla::intl {
// static
Result<UniquePtr<IDNA>, ICUError> IDNA::TryCreate(ProcessingType aProcessing) {
uint32_t IDNAOptions = UIDNA_CHECK_BIDI | UIDNA_CHECK_CONTEXTJ;
if (aProcessing == ProcessingType::NonTransitional) {
IDNAOptions |= UIDNA_NONTRANSITIONAL_TO_UNICODE;
}
UErrorCode status = U_ZERO_ERROR;
UIDNA* idna = uidna_openUTS46(IDNAOptions, &status);
if (U_FAILURE(status)) {
return Err(ToICUError(status));
}
return UniquePtr<IDNA>(new IDNA(idna));
}
IDNA::~IDNA() { uidna_close(mIDNA.GetMut()); }
} // namespace mozilla::intl

Просмотреть файл

@ -1,138 +0,0 @@
/* 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/. */
#ifndef intl_components_IDNA_h_
#define intl_components_IDNA_h_
#include "mozilla/Try.h"
#include "mozilla/intl/ICU4CGlue.h"
#include "unicode/uidna.h"
namespace mozilla::intl {
/**
* This component is a Mozilla-focused API for the Internationalizing Domain
* Names in Applications (IDNA).
*
* See UTS #46 for details.
* http://unicode.org/reports/tr46/
*/
class IDNA final {
public:
~IDNA();
/**
* UTS #46 specifies two specific types of processing: Transitional Processing
* and NonTransitional Processing.
*
* See http://unicode.org/reports/tr46/#Compatibility_Processing
*/
enum class ProcessingType {
Transitional,
NonTransitional,
};
/**
* Create an IDNA object, with specifying the type of processing by enum
* ProcessingType.
*
* Currently the implementation enables CheckBidi flag and CheckJoiners by
* default.
*
* See UTS #46, '4 Processing' for details.
* http://unicode.org/reports/tr46/#Processing
*/
static Result<UniquePtr<IDNA>, ICUError> TryCreate(
ProcessingType aProcessing);
/**
* This class contains the error code information of IDNA processing.
*/
class Info final {
public:
/**
* Check if there's any error.
*/
bool HasErrors() const { return mErrorCode != 0; }
/**
* If the domain name label starts with "xn--", then the label contains
* Punycode. This checks if the domain name label has invalid Punycode.
*
* See https://www.rfc-editor.org/rfc/rfc3492.html
*/
bool HasInvalidPunycode() const {
return (mErrorCode & UIDNA_ERROR_PUNYCODE) != 0;
}
/* The label was successfully ACE (Punycode) decoded but the resulting
* string had severe validation errors. For example,
* it might contain characters that are not allowed in ACE labels,
* or it might not be normalized.
*/
bool HasInvalidAceLabel() const {
return (mErrorCode & UIDNA_ERROR_INVALID_ACE_LABEL) != 0;
}
/**
* Checks if the domain name label has any invalid hyphen characters.
*
* See CheckHyphens flag for details in UTS #46[1].
* - The label must not contain a U+002D HYPHEN-MINUS character in both the
* third and fourth positions.
* - The label must neither begin nor end with a U+002D HYPHEN-MINUS
* character.
*
* [1]: http://unicode.org/reports/tr46/#Validity_Criteria
*/
bool HasInvalidHyphen() const {
uint32_t hyphenErrors = UIDNA_ERROR_LEADING_HYPHEN |
UIDNA_ERROR_TRAILING_HYPHEN |
UIDNA_ERROR_HYPHEN_3_4;
return (mErrorCode & hyphenErrors) != 0;
}
bool HasErrorsIgnoringInvalidHyphen() const {
uint32_t hyphenErrors = UIDNA_ERROR_LEADING_HYPHEN |
UIDNA_ERROR_TRAILING_HYPHEN |
UIDNA_ERROR_HYPHEN_3_4;
return (mErrorCode & ~hyphenErrors) != 0;
}
private:
friend class IDNA;
explicit Info(const UIDNAInfo* aUinfo) : mErrorCode(aUinfo->errors) {}
uint32_t mErrorCode = 0;
};
/**
* Converts a domain name label to its Unicode form for human-readable
* display, and writes the Unicode form into buffer, and returns IDNA::Info
* object.
* The IDNA::Info object contains the detail information about the processing
* result of IDNA call, caller should check the result by calling
* IDNA::Info::HasErrors() as well.
*/
template <typename Buffer>
Result<Info, ICUError> LabelToUnicode(Span<const char16_t> aLabel,
Buffer& aBuffer) {
UIDNAInfo uinfo = UIDNA_INFO_INITIALIZER;
MOZ_TRY(FillBufferWithICUCall(
aBuffer, [&](UChar* target, int32_t length, UErrorCode* status) {
return uidna_labelToUnicode(mIDNA.GetConst(), aLabel.data(),
aLabel.size(), target, length, &uinfo,
status);
}));
return Info{&uinfo};
}
private:
explicit IDNA(UIDNA* aIDNA) : mIDNA(aIDNA) {}
ICUPointer<UIDNA> mIDNA = ICUPointer<UIDNA>(nullptr);
};
} // namespace mozilla::intl
#endif // intl_components_IDNA_h_

Просмотреть файл

@ -242,6 +242,7 @@ def update_data_file(topsrcdir):
+ "-DUCONFIG_NO_TRANSLITERATION "
+ "-DUCONFIG_NO_REGULAR_EXPRESSIONS "
+ "-DUCONFIG_NO_BREAK_ITERATION "
+ "-DUCONFIG_NO_BREAK_IDNA "
+ "-DU_CHARSET_IS_UTF8 "
)
}

Просмотреть файл

@ -133,7 +133,7 @@ var IdentityHandler = {
} catch (e) {}
try {
result.host = lazy.IDNService.convertToDisplayIDN(uri.host, {});
result.host = lazy.IDNService.convertToDisplayIDN(uri.host);
} catch (e) {
result.host = uri.host;
}

Просмотреть файл

@ -0,0 +1,12 @@
[package]
name = "idna_glue"
version = "0.1.0"
edition = "2021"
license = "MPL-2.0"
[dependencies]
idna = "1.0.2"
arraystring = "0.3.0"
percent-encoding = "2.3.1"
nsstring = { path = "../../../xpcom/rust/nsstring" }
nserror = { path = "../../../xpcom/rust/nserror" }

Просмотреть файл

@ -0,0 +1,15 @@
header = """/* 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/. */"""
autogen_warning = """/* DO NOT MODIFY THIS MANUALLY! This file was generated using cbindgen. */"""
include_guard = "mozilla_net_idna_glue_h"
include_version = true
braces = "SameLine"
line_length = 100
tab_width = 2
language = "C++"
includes = ["nsError.h", "nsString.h"]
[export]
item_types = ["functions"]
exclude = ["mozilla_net_is_label_safe"]

Просмотреть файл

@ -0,0 +1,12 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
if CONFIG["COMPILE_ENVIRONMENT"]:
CbindgenHeader("idna_glue.h", inputs=["/netwerk/base/idna_glue"])
EXPORTS.mozilla.net += [
"!idna_glue.h",
]

Просмотреть файл

@ -0,0 +1,217 @@
/* 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/. */
use core::fmt::Write;
use std::borrow::Cow;
use idna::uts46::AsciiDenyList;
use idna::uts46::ErrorPolicy;
use idna::uts46::Hyphens;
use idna::uts46::ProcessingError;
use idna::uts46::ProcessingSuccess;
use idna::uts46::Uts46;
use nserror::*;
use nsstring::*;
use percent_encoding::percent_decode;
/// The URL deny list plus asterisk and double quote.
/// Using AsciiDenyList::URL is https://bugzilla.mozilla.org/show_bug.cgi?id=1815926 .
const GECKO: AsciiDenyList = AsciiDenyList::new(true, "%#/:<>?@[\\]^|*\"");
/// The `GECKO` deny list minus percent. https://bugzilla.mozilla.org/show_bug.cgi?id=1527462
const MAILNEWS: AsciiDenyList = AsciiDenyList::new(true, "#/:<>?@[\\]^|*\"");
/// Deny only glyphless ASCII to accommodate legacy callers.
const GLYPHLESS: AsciiDenyList = AsciiDenyList::new(true, "");
extern "C" {
#[allow(improper_ctypes)]
// char is now actually guaranteed to have the same representation as u32
fn mozilla_net_is_label_safe(
label: *const char,
label_len: usize,
tld: *const char,
tld_len: usize,
) -> bool;
}
#[no_mangle]
pub unsafe extern "C" fn mozilla_net_domain_to_ascii_impl(
src: *const nsACString,
allow_any_glyphful_ascii: bool,
dst: *mut nsACString,
) -> nsresult {
debug_assert_ne!(src, dst as *const nsACString, "src and dst must not alias");
process(|_, _, _| false, allow_any_glyphful_ascii, &*src, &mut *dst)
}
#[no_mangle]
pub unsafe extern "C" fn mozilla_net_domain_to_unicode_impl(
src: *const nsACString,
allow_any_glyphful_ascii: bool,
dst: *mut nsACString,
) -> nsresult {
debug_assert_ne!(src, dst as *const nsACString, "src and dst must not alias");
process(|_, _, _| true, allow_any_glyphful_ascii, &*src, &mut *dst)
}
#[no_mangle]
pub unsafe extern "C" fn mozilla_net_domain_to_display_impl(
src: *const nsACString,
allow_any_glyphful_ascii: bool,
dst: *mut nsACString,
) -> nsresult {
debug_assert_ne!(src, dst as *const nsACString, "src and dst must not alias");
// XXX do we want to change this not to fail fast?
process(
|label, tld, _| unsafe {
debug_assert!(!label.is_empty());
mozilla_net_is_label_safe(
label.as_ptr(),
label.len(),
if tld.is_empty() {
core::ptr::null()
} else {
tld.as_ptr()
},
tld.len(),
)
},
allow_any_glyphful_ascii,
&*src,
&mut *dst,
)
}
#[no_mangle]
pub unsafe extern "C" fn mozilla_net_domain_to_display_and_ascii_impl(
src: *const nsACString,
mailnews: bool,
dst: *mut nsACString,
ascii_dst: *mut nsACString,
) -> nsresult {
debug_assert_ne!(src, dst as *const nsACString, "src and dst must not alias");
debug_assert_ne!(
src, ascii_dst as *const nsACString,
"src and ascii_dst must not alias"
);
debug_assert_ne!(dst, ascii_dst, "dst and ascii_dst must not alias");
{
let src = &*src;
let dst: &mut nsACString = &mut *dst;
let ascii_dst: &mut nsACString = &mut *ascii_dst;
dst.truncate();
ascii_dst.truncate();
let unpercent: Cow<'_, [u8]> = percent_decode(src).into();
match Uts46::new().process(
&unpercent,
if mailnews { MAILNEWS } else { GECKO },
Hyphens::Allow,
ErrorPolicy::FailFast,
|label, tld, _| unsafe {
debug_assert!(!label.is_empty());
mozilla_net_is_label_safe(
label.as_ptr(),
label.len(),
if tld.is_empty() {
core::ptr::null()
} else {
tld.as_ptr()
},
tld.len(),
)
},
&mut IdnaWriteWrapper::new(dst),
Some(&mut IdnaWriteWrapper::new(ascii_dst)),
) {
Ok(ProcessingSuccess::Passthrough) => {
// Let the borrow the `IdnaWriteWrapper`s end and fall through.
}
Ok(ProcessingSuccess::WroteToSink) => return nserror::NS_OK,
Err(ProcessingError::ValidityError) => return nserror::NS_ERROR_MALFORMED_URI,
Err(ProcessingError::SinkError) => unreachable!(),
}
match unpercent {
Cow::Borrowed(_) => dst.assign(src),
Cow::Owned(vec) => dst.append(&vec),
}
nserror::NS_OK
}
}
type BufferString = arraystring::ArrayString<arraystring::typenum::U255>;
/// Buffering type to avoid atomic check of destination
/// `nsACString` on a per character basis.
struct IdnaWriteWrapper<'a> {
sink: &'a mut nsACString,
buffer: BufferString,
}
impl<'a> IdnaWriteWrapper<'a> {
fn new(sink: &'a mut nsACString) -> IdnaWriteWrapper<'a> {
IdnaWriteWrapper {
sink,
buffer: BufferString::new(),
}
}
}
impl<'a> Write for IdnaWriteWrapper<'a> {
fn write_str(&mut self, s: &str) -> std::fmt::Result {
if self.buffer.try_push_str(s).is_ok() {
return Ok(());
}
if !self.buffer.is_empty() {
self.sink.append(self.buffer.as_bytes());
self.buffer.clear();
if self.buffer.try_push_str(s).is_ok() {
return Ok(());
}
}
// Input too long to fit in the buffer.
self.sink.append(s.as_bytes());
Ok(())
}
}
impl<'a> Drop for IdnaWriteWrapper<'a> {
fn drop(&mut self) {
if !self.buffer.is_empty() {
self.sink.append(self.buffer.as_bytes());
}
}
}
fn process<OutputUnicode: FnMut(&[char], &[char], bool) -> bool>(
output_as_unicode: OutputUnicode,
allow_any_glyphful_ascii: bool,
src: &nsACString,
dst: &mut nsACString,
) -> nsresult {
dst.truncate();
match Uts46::new().process(
&src,
if allow_any_glyphful_ascii {
GLYPHLESS
} else {
GECKO
},
Hyphens::Allow,
ErrorPolicy::FailFast,
output_as_unicode,
&mut IdnaWriteWrapper::new(dst),
None,
) {
Ok(ProcessingSuccess::Passthrough) => {
// Let the borrow of `dst` inside `IdnaWriteWrapper` end and fall through.
}
Ok(ProcessingSuccess::WroteToSink) => return nserror::NS_OK,
Err(ProcessingError::ValidityError) => return nserror::NS_ERROR_MALFORMED_URI,
Err(ProcessingError::SinkError) => unreachable!(),
}
dst.assign(src);
nserror::NS_OK
}

Просмотреть файл

@ -290,7 +290,7 @@ EXTRA_JS_MODULES += [
"NetUtil.sys.mjs",
]
DIRS += ["mozurl", "rust-helper", "http-sfv"]
DIRS += ["mozurl", "rust-helper", "http-sfv", "idna_glue"]
include("/ipc/chromium/chromium-config.mozbuild")

Просмотреть файл

@ -332,6 +332,9 @@ fn get_base_domain(url: &MozURL) -> Result<Option<String>, nsresult> {
if host_str.starts_with('[') && host_str.ends_with(']') {
host_str = &host_str[1..host_str.len() - 1];
// IPv6 is its own base domain, and GetBaseDomainFromSchemeHost
// does not work with the square brackets omitted.
return Ok(Some(host_str.to_string()))
}
let host = nsCString::from(host_str);

Просмотреть файл

@ -711,20 +711,6 @@ int32_t NS_GetRealPort(nsIURI* aURI) {
return NS_GetDefaultPort(scheme.get());
}
nsresult NS_DomainToASCII(const nsACString& aHost, nsACString& aASCII) {
return nsStandardURL::GetIDNService()->ConvertUTF8toACE(aHost, aASCII);
}
nsresult NS_DomainToDisplay(const nsACString& aHost, nsACString& aDisplay) {
bool ignored;
return nsStandardURL::GetIDNService()->ConvertToDisplayIDN(aHost, &ignored,
aDisplay);
}
nsresult NS_DomainToUnicode(const nsACString& aHost, nsACString& aUnicode) {
return nsStandardURL::GetIDNService()->ConvertACEtoUTF8(aHost, aUnicode);
}
nsresult NS_NewInputStreamChannelInternal(
nsIChannel** outChannel, nsIURI* aUri,
already_AddRefed<nsIInputStream> aStream, const nsACString& aContentType,

Просмотреть файл

@ -29,6 +29,8 @@
#include "nsServiceManagerUtils.h"
#include "nsString.h"
#include "nsTArray.h"
#include "mozilla/net/idna_glue.h"
#include "mozilla/net/MozURL_ffi.h"
class nsIPrincipal;
class nsIAsyncStreamCopier;
@ -297,8 +299,33 @@ int32_t NS_GetDefaultPort(const char* scheme,
* The UTS #46 ToASCII operation as parametrized by the WHATWG URL Standard.
*
* Use this function to prepare a host name for network protocols.
*
* Do not try to optimize and avoid calling this function if you already
* have ASCII. This function optimizes internally, and calling it is
* required for correctness!
*
* Use `NS_DomainToDisplayAndASCII` if you need both this function and
* `NS_DomainToDisplay` together.
*
* The function is available to privileged JavaScript callers via
* nsIIDNService.
*
* Rust callers that don't happen to be using XPCOM strings are better
* off using the `idna` crate directly.
*/
nsresult NS_DomainToASCII(const nsACString& aHost, nsACString& aASCII);
inline nsresult NS_DomainToASCII(const nsACString& aDomain,
nsACString& aASCII) {
return mozilla_net_domain_to_ascii_impl(&aDomain, false, &aASCII);
}
/**
* Bogus variant for callers that try pass through IPv6 addresses or even port
* numbers!
*/
inline nsresult NS_DomainToASCIIAllowAnyGlyphfulASCII(const nsACString& aDomain,
nsACString& aASCII) {
return mozilla_net_domain_to_ascii_impl(&aDomain, true, &aASCII);
}
/**
* The UTS #46 ToUnicode operation as parametrized by the WHATWG URL Standard,
@ -306,16 +333,57 @@ nsresult NS_DomainToASCII(const nsACString& aHost, nsACString& aASCII);
* instead.
*
* Use this function to prepare a host name for display to the user.
*
* Use `NS_DomainToDisplayAndASCII` if you need both this function and
* `NS_DomainToASCII` together.
*
* The function is available to privileged JavaScript callers via
* nsIIDNService.
*
* Rust callers that don't happen to be using XPCOM strings are better
* off using the `idna` crate directly. (See `idna_glue` for what policy
* closure to use.)
*/
nsresult NS_DomainToDisplay(const nsACString& aHost, nsACString& aDisplay);
inline nsresult NS_DomainToDisplay(const nsACString& aDomain,
nsACString& aDisplay) {
return mozilla_net_domain_to_display_impl(&aDomain, false, &aDisplay);
}
/**
* Bogus variant for callers that try pass through IPv6 addresses or even port
* numbers!
*/
inline nsresult NS_DomainToDisplayAllowAnyGlyphfulASCII(
const nsACString& aDomain, nsACString& aDisplay) {
return mozilla_net_domain_to_display_impl(&aDomain, true, &aDisplay);
}
/**
* The UTS #46 ToUnicode operation as parametrized by the WHATWG URL Standard.
*
* It's most likely incorrect to call this function, and `NS_DomainToDisplay`
* should typically be called instead.
* It's most likely INCORRECT to call this function, and `NS_DomainToDisplay`
* should typically be called instead. Please avoid adding new callers, so
* that this conversion could be removed entirely!
*
* The function is available to privileged JavaScript callers via
* nsIIDNService.
*
* Rust callers that don't happen to be using XPCOM strings are better
* off using the `idna` crate directly.
*/
nsresult NS_DomainToUnicode(const nsACString& aHost, nsACString& aUnicode);
inline nsresult NS_DomainToUnicode(const nsACString& aDomain,
nsACString& aUnicode) {
return mozilla_net_domain_to_unicode_impl(&aDomain, false, &aUnicode);
}
/**
* Bogus variant for callers that try pass through IPv6 addresses or even port
* numbers!
*/
inline nsresult NS_DomainToUnicodeAllowAnyGlyphfulASCII(
const nsACString& aDomain, nsACString& aDisplay) {
return mozilla_net_domain_to_unicode_impl(&aDomain, true, &aDisplay);
}
/**
* This function is a helper function to get a protocol's default port if the

Просмотреть файл

@ -46,6 +46,46 @@ static mozilla::LazyLogModule gStandardURLLog("nsStandardURL");
using namespace mozilla::ipc;
inline bool IsMailNews() {
#if defined(MOZ_THUNDERBIRD) || defined(MOZ_SUITE)
// This gives bogus behavior to https/http URLs
// in Thuderbird and Suite. This really should
// be a call to a method that checks for all the
// relevant schemes that need the exception.
// See
// https://searchfox.org/comm-central/rev/6ed52969390e2998785fb7587ef2a0b798bc8469/mailnews/base/src/nsNewMailnewsURI.cpp#48-128
// for candidates.
// https://bugzilla.mozilla.org/show_bug.cgi?id=1527462
return true;
#else
return false;
#endif
}
/**
* The UTS #46 ToUnicode operation as parametrized by the WHATWG URL Standard,
* except potentially misleading labels are treated according to ToASCII
* instead. Combined with the ToASCII operation without rerunning the expensive
* part.
*
* NOTE: This function performs percent-decoding on the argument unlike
* the other `NS_DomainTo` functions!
*
* If upon successfull return `aASCII` is empty, it is the caller's
* responsibility to treat the value of `aDisplay` also as the value of
* `aASCII`. (The weird semantics avoid useless allocation / copying.)
*
* Rust callers that don't happen to be using XPCOM strings are better
* off using the `idna` crate directly. (See `idna_glue` for what policy
* closure to use.)
*/
inline nsresult NS_DomainToDisplayAndASCII(const nsACString& aDomain,
bool aMailnews, nsACString& aDisplay,
nsACString& aASCII) {
return mozilla_net_domain_to_display_and_ascii_impl(&aDomain, aMailnews,
&aDisplay, &aASCII);
}
namespace mozilla {
namespace net {
@ -60,28 +100,6 @@ static Atomic<bool, Relaxed> gInitialized{false};
const char nsStandardURL::gHostLimitDigits[] = {'/', '\\', '?', '#', 0};
// Invalid host characters
// Note that the array below will be initialized at compile time,
// so we do not need to "optimize" TestForInvalidHostCharacters.
//
constexpr bool TestForInvalidHostCharacters(char c) {
// Testing for these:
// CONTROL_CHARACTERS " #/:?@[\\]*<>|\"";
return (c > 0 && c < 32) || // The control characters are [1, 31]
c == 0x7F || // // DEL (delete)
c == ' ' || c == '#' || c == '/' || c == ':' || c == '?' || c == '@' ||
c == '[' || c == '\\' || c == ']' || c == '*' || c == '<' ||
c == '^' ||
#if defined(MOZ_THUNDERBIRD) || defined(MOZ_SUITE)
// Mailnews %-escapes file paths into URLs.
c == '>' || c == '|' || c == '"';
#else
c == '>' || c == '|' || c == '"' || c == '%';
#endif
}
constexpr ASCIIMaskArray sInvalidHostChars =
CreateASCIIMask(TestForInvalidHostCharacters);
//----------------------------------------------------------------------------
// nsStandardURL::nsSegmentEncoder
//----------------------------------------------------------------------------
@ -624,77 +642,25 @@ nsresult nsStandardURL::NormalizeIPv4(const nsACString& host,
nsIIDNService* nsStandardURL::GetIDNService() { return gIDN.get(); }
nsresult nsStandardURL::NormalizeIDN(const nsCString& host, nsCString& result) {
result.Truncate();
nsresult nsStandardURL::NormalizeIDN(const nsACString& aHost, bool aMailnews,
nsACString& aResult) {
mDisplayHost.Truncate();
nsresult rv;
if (!gIDN) {
return NS_ERROR_UNEXPECTED;
}
// Even if it's already ACE, we must still call ConvertUTF8toACE in order
// for the input normalization to take place.
rv = gIDN->ConvertUTF8toACE(host, result);
if (NS_FAILED(rv)) {
return rv;
}
// If the ASCII representation doesn't contain the xn-- token then we don't
// need to call ConvertToDisplayIDN as that would not change anything.
if (!StringBeginsWith(result, "xn--"_ns) &&
result.Find(".xn--"_ns) == kNotFound) {
mCheckedIfHostA = true;
return NS_OK;
}
bool isAscii = true;
nsAutoCString displayHost;
rv = gIDN->ConvertToDisplayIDN(result, &isAscii, displayHost);
if (NS_FAILED(rv)) {
return rv;
}
mCheckedIfHostA = true;
if (!isAscii) {
nsCString displayHost; // Intentionally not nsAutoCString to avoid copy when
// assigning to field
nsresult rv =
NS_DomainToDisplayAndASCII(aHost, aMailnews, displayHost, aResult);
if (NS_FAILED(rv)) {
return rv;
}
if (aResult.IsEmpty()) {
aResult.Assign(displayHost);
} else {
mDisplayHost = displayHost;
}
return NS_OK;
}
bool nsStandardURL::ValidIPv6orHostname(const char* host, uint32_t length) {
if (!host || !*host) {
// Should not be NULL or empty string
return false;
}
if (length != strlen(host)) {
// Embedded null
return false;
}
bool openBracket = host[0] == '[';
bool closeBracket = host[length - 1] == ']';
if (openBracket && closeBracket) {
return net_IsValidIPv6Addr(Substring(host + 1, length - 2));
}
if (openBracket || closeBracket) {
// Fail if only one of the brackets is present
return false;
}
const char* end = host + length;
const char* iter = host;
for (; iter != end && *iter; ++iter) {
if (ASCIIMask::IsMasked(sInvalidHostChars, *iter)) {
return false;
}
}
return true;
}
void nsStandardURL::CoalescePath(netCoalesceFlags coalesceFlag, char* path) {
auto resultCoalesceDirs = net_CoalesceDirs(coalesceFlag, path);
int32_t newLen = strlen(path);
@ -902,48 +868,34 @@ nsresult nsStandardURL::BuildNormalizedSpec(const char* spec,
// However, perform Unicode normalization on it, as IDN does.
// Note that we don't disallow URLs without a host - file:, etc
if (mHost.mLen > 0) {
nsAutoCString tempHost;
NS_UnescapeURL(spec + mHost.mPos, mHost.mLen, esc_AlwaysCopy | esc_Host,
tempHost);
if (tempHost.Contains('\0')) {
return NS_ERROR_MALFORMED_URI; // null embedded in hostname
}
if (tempHost.Contains(' ')) {
return NS_ERROR_MALFORMED_URI; // don't allow spaces in the hostname
}
nsresult rv = NormalizeIDN(tempHost, encHost);
if (NS_FAILED(rv)) {
return rv;
}
if (!SegmentIs(spec, mScheme, "resource") &&
!SegmentIs(spec, mScheme, "chrome")) {
nsAutoCString ipString;
if (encHost.Length() > 0 && encHost.First() == '[' &&
encHost.Last() == ']' &&
ValidIPv6orHostname(encHost.get(), encHost.Length())) {
rv = (nsresult)rusturl_parse_ipv6addr(&encHost, &ipString);
nsDependentCSubstring tempHost(spec + mHost.mPos, mHost.mLen);
nsresult rv;
bool allowIp = !SegmentIs(spec, mScheme, "resource") &&
!SegmentIs(spec, mScheme, "chrome");
if (tempHost.First() == '[' && allowIp) {
mCheckedIfHostA = true;
rv = (nsresult)rusturl_parse_ipv6addr(&tempHost, &encHost);
if (NS_FAILED(rv)) {
return rv;
}
} else {
rv = NormalizeIDN(tempHost, IsMailNews(), encHost);
if (NS_FAILED(rv)) {
return rv;
}
if (EndsInANumber(encHost) && allowIp) {
nsAutoCString ipString;
rv = NormalizeIPv4(encHost, ipString);
if (NS_FAILED(rv)) {
return rv;
}
encHost = ipString;
} else {
if (EndsInANumber(encHost)) {
rv = NormalizeIPv4(encHost, ipString);
if (NS_FAILED(rv)) {
return rv;
}
encHost = ipString;
}
}
}
// NormalizeIDN always copies, if the call was successful.
useEncHost = true;
approxLen += encHost.Length();
if (!ValidIPv6orHostname(encHost.BeginReading(), encHost.Length())) {
return NS_ERROR_MALFORMED_URI;
}
} else {
// empty host means empty mDisplayHost
mDisplayHost.Truncate();
@ -1017,7 +969,6 @@ nsresult nsStandardURL::BuildNormalizedSpec(const char* spec,
&diff);
ShiftFromPath(diff);
net_ToLowerCase(buf + mHost.mPos, mHost.mLen);
MOZ_ASSERT(mPort >= -1, "Invalid negative mPort");
if (mPort != -1 && mPort != mDefaultPort) {
buf[i++] = ':';
@ -1539,20 +1490,16 @@ nsresult nsStandardURL::CheckIfHostIsAscii() {
mCheckedIfHostA = true;
if (!gIDN) {
return NS_ERROR_NOT_INITIALIZED;
}
nsAutoCString displayHost;
bool isAscii;
rv = gIDN->ConvertToDisplayIDN(Host(), &isAscii, displayHost);
// IPC deseriazation can have IPv6 without square brackets here.
rv = NS_DomainToDisplayAllowAnyGlyphfulASCII(Host(), displayHost);
if (NS_FAILED(rv)) {
mDisplayHost.Truncate();
mCheckedIfHostA = false;
return rv;
}
if (!isAscii) {
if (!mozilla::IsAscii(displayHost)) {
mDisplayHost = displayHost;
}
@ -1775,7 +1722,7 @@ nsresult nsStandardURL::SetSpecWithEncoding(const nsACString& input,
}
// Make sure that a URLTYPE_AUTHORITY has a non-empty hostname.
if (mURLType == URLTYPE_AUTHORITY && mHost.mLen == -1) {
if (mURLType == URLTYPE_AUTHORITY && mHost.mLen <= 0) {
rv = NS_ERROR_MALFORMED_URI;
}
@ -2201,19 +2148,15 @@ nsresult nsStandardURL::SetHost(const nsACString& input) {
nsAutoCString hostname(input);
hostname.StripTaggedASCII(ASCIIMask::MaskCRLFTab());
LOG(("nsStandardURL::SetHost [host=%s]\n", hostname.get()));
nsACString::const_iterator start, end;
hostname.BeginReading(start);
hostname.EndReading(end);
FindHostLimit(start, end);
// Do percent decoding on the the input.
nsAutoCString flat;
NS_UnescapeURL(hostname.BeginReading(), end - start,
esc_AlwaysCopy | esc_Host, flat);
const char* host = flat.get();
LOG(("nsStandardURL::SetHost [host=%s]\n", host));
nsDependentCSubstring flat(start, end);
if (mURLType == URLTYPE_NO_AUTHORITY) {
if (flat.IsEmpty()) {
@ -2228,17 +2171,7 @@ nsresult nsStandardURL::SetHost(const nsACString& input) {
return NS_ERROR_UNEXPECTED;
}
if (strlen(host) < flat.Length()) {
return NS_ERROR_MALFORMED_URI; // found embedded null
}
// For consistency with SetSpec/nsURLParsers, don't allow spaces
// in the hostname.
if (strchr(host, ' ')) {
return NS_ERROR_MALFORMED_URI;
}
if (mSpec.Length() + strlen(host) - Host().Length() >
if (mSpec.Length() + flat.Length() - Host().Length() >
StaticPrefs::network_standard_url_max_length()) {
return NS_ERROR_MALFORMED_URI;
}
@ -2248,37 +2181,34 @@ nsresult nsStandardURL::SetHost(const nsACString& input) {
uint32_t len;
nsAutoCString hostBuf;
nsresult rv = NormalizeIDN(flat, hostBuf);
if (NS_FAILED(rv)) {
return rv;
}
if (!SegmentIs(mScheme, "resource") && !SegmentIs(mScheme, "chrome")) {
nsAutoCString ipString;
if (hostBuf.Length() > 0 && hostBuf.First() == '[' &&
hostBuf.Last() == ']' &&
ValidIPv6orHostname(hostBuf.get(), hostBuf.Length())) {
rv = (nsresult)rusturl_parse_ipv6addr(&hostBuf, &ipString);
nsresult rv;
bool allowIp =
!SegmentIs(mScheme, "resource") && !SegmentIs(mScheme, "chrome");
if (!flat.IsEmpty() && flat.First() == '[' && allowIp) {
mCheckedIfHostA = true;
rv = rusturl_parse_ipv6addr(&flat, &hostBuf);
if (NS_FAILED(rv)) {
return rv;
}
} else {
rv = NormalizeIDN(flat, IsMailNews(), hostBuf);
if (NS_FAILED(rv)) {
return rv;
}
if (EndsInANumber(hostBuf) && allowIp) {
nsAutoCString ipString;
rv = NormalizeIPv4(hostBuf, ipString);
if (NS_FAILED(rv)) {
return rv;
}
hostBuf = ipString;
} else {
if (EndsInANumber(hostBuf)) {
rv = NormalizeIPv4(hostBuf, ipString);
if (NS_FAILED(rv)) {
return rv;
}
hostBuf = ipString;
}
}
}
// NormalizeIDN always copies if the call was successful
host = hostBuf.get();
len = hostBuf.Length();
if (!ValidIPv6orHostname(host, len)) {
if (!len) {
return NS_ERROR_MALFORMED_URI;
}
@ -2299,7 +2229,7 @@ nsresult nsStandardURL::SetHost(const nsACString& input) {
}
}
int32_t shift = ReplaceSegment(mHost.mPos, mHost.mLen, host, len);
int32_t shift = ReplaceSegment(mHost.mPos, mHost.mLen, hostBuf.get(), len);
if (shift) {
mHost.mLen = len;
@ -2307,8 +2237,6 @@ nsresult nsStandardURL::SetHost(const nsACString& input) {
ShiftFromPath(shift);
}
// Now canonicalize the host to lowercase
net_ToLowerCase(mSpec.BeginWriting() + mHost.mPos, mHost.mLen);
return NS_OK;
}

Просмотреть файл

@ -259,9 +259,9 @@ class nsStandardURL : public nsIFileURL,
void Clear();
void InvalidateCache(bool invalidateCachedFile = true);
bool ValidIPv6orHostname(const char* host, uint32_t length);
static bool IsValidOfBase(unsigned char c, const uint32_t base);
nsresult NormalizeIDN(const nsCString& host, nsCString& result);
nsresult NormalizeIDN(const nsACString& aHost, bool aMailnews,
nsACString& aResult);
nsresult CheckIfHostIsAscii();
void CoalescePath(netCoalesceFlags coalesceFlag, char* path);

Просмотреть файл

@ -30,7 +30,6 @@
#include "nsICookiePermission.h"
#include "nsIConsoleReportCollector.h"
#include "nsIEffectiveTLDService.h"
#include "nsIIDNService.h"
#include "nsIScriptError.h"
#include "nsIScriptSecurityManager.h"
#include "nsIURI.h"
@ -265,9 +264,6 @@ nsresult CookieService::Init() {
mTLDService = mozilla::components::EffectiveTLD::Service(&rv);
NS_ENSURE_SUCCESS(rv, rv);
mIDNService = mozilla::components::IDN::Service(&rv);
NS_ENSURE_SUCCESS(rv, rv);
mThirdPartyUtil = mozilla::components::ThirdPartyUtil::Service();
NS_ENSURE_SUCCESS(rv, rv);
@ -1278,21 +1274,14 @@ void CookieService::GetCookiesForURI(
* private domain & permission compliance enforcement functions
******************************************************************************/
// Normalizes the given hostname, component by component. ASCII/ACE
// components are lower-cased, and UTF-8 components are normalized per
// RFC 3454 and converted to ACE.
nsresult CookieService::NormalizeHost(nsCString& aHost) {
if (!IsAscii(aHost)) {
nsAutoCString host;
nsresult rv = mIDNService->ConvertUTF8toACE(aHost, host);
if (NS_FAILED(rv)) {
return rv;
}
aHost = host;
nsAutoCString host;
nsresult rv = NS_DomainToASCIIAllowAnyGlyphfulASCII(aHost, host);
if (NS_FAILED(rv)) {
return rv;
}
ToLowerCase(aHost);
aHost = host;
return NS_OK;
}

Просмотреть файл

@ -21,7 +21,6 @@
class nsIConsoleReportCollector;
class nsICookieJarSettings;
class nsIEffectiveTLDService;
class nsIIDNService;
class nsIURI;
class nsIChannel;
class mozIThirdPartyUtil;
@ -125,7 +124,6 @@ class CookieService final : public nsICookieService,
// cached members.
nsCOMPtr<mozIThirdPartyUtil> mThirdPartyUtil;
nsCOMPtr<nsIEffectiveTLDService> mTLDService;
nsCOMPtr<nsIIDNService> mIDNService;
// we have two separate Cookie Storages: one for normal browsing and one for
// private browsing.

Просмотреть файл

@ -88,7 +88,6 @@ UNIFIED_SOURCES += [
"nsHostRecord.cpp",
"nsHostResolver.cpp",
"nsIDNService.cpp",
"punycode.c",
"TRR.cpp",
"TRRQuery.cpp",
"TRRService.cpp",

Просмотреть файл

@ -28,6 +28,7 @@
#include "prio.h"
#include "nsCharSeparatedTokenizer.h"
#include "nsNetAddr.h"
#include "nsNetUtil.h"
#include "nsProxyRelease.h"
#include "nsQueryObject.h"
#include "nsIObserverService.h"
@ -897,9 +898,6 @@ nsDNSService::Init() {
mTrrService = nullptr;
}
nsCOMPtr<nsIIDNService> idn = do_GetService(NS_IDNSERVICE_CONTRACTID);
mIDN = idn;
return NS_OK;
}
@ -958,7 +956,6 @@ already_AddRefed<nsHostResolver> nsDNSService::GetResolverLocked() {
nsresult nsDNSService::PreprocessHostname(bool aLocalDomain,
const nsACString& aInput,
nsIIDNService* aIDN,
nsACString& aACE) {
// Enforce RFC 7686
if (mBlockDotOnion && StringEndsWith(aInput, ".onion"_ns)) {
@ -983,12 +980,7 @@ nsresult nsDNSService::PreprocessHostname(bool aLocalDomain,
}
}
if (!aIDN || IsAscii(aInput)) {
aACE = aInput;
return NS_OK;
}
if (!(IsUtf8(aInput) && NS_SUCCEEDED(aIDN->ConvertUTF8toACE(aInput, aACE)))) {
if (!NS_SUCCEEDED(NS_DomainToASCIIAllowAnyGlyphfulASCII(aInput, aACE))) {
return NS_ERROR_FAILURE;
}
return NS_OK;
@ -1011,7 +1003,6 @@ nsresult nsDNSService::AsyncResolveInternal(
// grab reference to global host resolver and IDN service. beware
// simultaneous shutdown!!
RefPtr<nsHostResolver> res;
nsCOMPtr<nsIIDNService> idn;
nsCOMPtr<nsIEventTarget> target = target_;
nsCOMPtr<nsIDNSListener> listener = aListener;
bool localDomain = false;
@ -1023,7 +1014,6 @@ nsresult nsDNSService::AsyncResolveInternal(
}
res = mResolver;
idn = mIDN;
localDomain = IsLocalDomain(aHostname);
}
@ -1049,7 +1039,7 @@ nsresult nsDNSService::AsyncResolveInternal(
}
nsCString hostname;
nsresult rv = PreprocessHostname(localDomain, aHostname, idn, hostname);
nsresult rv = PreprocessHostname(localDomain, aHostname, hostname);
if (NS_FAILED(rv)) {
return rv;
}
@ -1101,7 +1091,6 @@ nsresult nsDNSService::CancelAsyncResolveInternal(
// grab reference to global host resolver and IDN service. beware
// simultaneous shutdown!!
RefPtr<nsHostResolver> res;
nsCOMPtr<nsIIDNService> idn;
bool localDomain = false;
{
MutexAutoLock lock(mLock);
@ -1111,7 +1100,6 @@ nsresult nsDNSService::CancelAsyncResolveInternal(
}
res = mResolver;
idn = mIDN;
localDomain = IsLocalDomain(aHostname);
}
if (!res) {
@ -1119,7 +1107,7 @@ nsresult nsDNSService::CancelAsyncResolveInternal(
}
nsCString hostname;
nsresult rv = PreprocessHostname(localDomain, aHostname, idn, hostname);
nsresult rv = PreprocessHostname(localDomain, aHostname, hostname);
if (NS_FAILED(rv)) {
return rv;
}
@ -1242,12 +1230,10 @@ nsresult nsDNSService::ResolveInternal(
// grab reference to global host resolver and IDN service. beware
// simultaneous shutdown!!
RefPtr<nsHostResolver> res;
nsCOMPtr<nsIIDNService> idn;
bool localDomain = false;
{
MutexAutoLock lock(mLock);
res = mResolver;
idn = mIDN;
localDomain = IsLocalDomain(aHostname);
}
@ -1258,7 +1244,7 @@ nsresult nsDNSService::ResolveInternal(
NS_ENSURE_TRUE(res, NS_ERROR_OFFLINE);
nsCString hostname;
nsresult rv = PreprocessHostname(localDomain, aHostname, idn, hostname);
nsresult rv = PreprocessHostname(localDomain, aHostname, hostname);
if (NS_FAILED(rv)) {
return rv;
}

Просмотреть файл

@ -10,7 +10,6 @@
#include "DNSServiceBase.h"
#include "nsClassHashtable.h"
#include "nsPIDNSService.h"
#include "nsIIDNService.h"
#include "nsIMemoryReporter.h"
#include "nsIObserver.h"
#include "nsHostResolver.h"
@ -82,7 +81,7 @@ class nsDNSService final : public mozilla::net::DNSServiceBase,
nsIDNSService::DNSFlags flags);
nsresult PreprocessHostname(bool aLocalDomain, const nsACString& aInput,
nsIIDNService* aIDN, nsACString& aACE);
nsACString& aACE);
bool IsLocalDomain(const nsACString& aHostname) const;
@ -108,7 +107,6 @@ class nsDNSService final : public mozilla::net::DNSServiceBase,
already_AddRefed<nsHostResolver> GetResolverLocked();
RefPtr<nsHostResolver> mResolver;
nsCOMPtr<nsIIDNService> mIDN;
// mLock protects access to mResolver, mLocalDomains, mIPv4OnlyDomains,
// mFailedSVCDomainNames, and mMockHTTPSRRDomain.

Просмотреть файл

@ -21,7 +21,6 @@
#include "nsCRT.h"
#include "nsEffectiveTLDService.h"
#include "nsIFile.h"
#include "nsIIDNService.h"
#include "nsIObserverService.h"
#include "nsIURI.h"
#include "nsNetCID.h"
@ -59,12 +58,6 @@ nsresult nsEffectiveTLDService::Init() {
return NS_ERROR_ALREADY_INITIALIZED;
}
nsresult rv;
mIDNService = mozilla::components::IDN::Service(&rv);
if (NS_FAILED(rv)) {
return rv;
}
gService = this;
RegisterWeakMemoryReporter(this);
@ -106,10 +99,7 @@ NS_IMETHODIMP nsEffectiveTLDService::Observe(nsISupports* aSubject,
nsEffectiveTLDService::~nsEffectiveTLDService() {
UnregisterWeakMemoryReporter(this);
if (mIDNService) {
// Only clear gService if Init() finished successfully.
gService = nullptr;
}
gService = nullptr;
}
// static
@ -149,10 +139,6 @@ size_t nsEffectiveTLDService::SizeOfIncludingThis(
mozilla::MallocSizeOf aMallocSizeOf) {
size_t n = aMallocSizeOf(this);
// Measurement of the following members may be added later if DMD finds it is
// worthwhile:
// - mIDNService
return n;
}
@ -258,10 +244,9 @@ nsEffectiveTLDService::GetSite(nsIURI* aURI, nsACString& aSite) {
NS_IMETHODIMP
nsEffectiveTLDService::GetPublicSuffixFromHost(const nsACString& aHostname,
nsACString& aPublicSuffix) {
// Create a mutable copy of the hostname and normalize it to ACE.
// This will fail if the hostname includes invalid characters.
nsAutoCString normHostname(aHostname);
nsresult rv = NormalizeHostname(normHostname);
nsAutoCString normHostname;
nsresult rv = NS_DomainToASCIIAllowAnyGlyphfulASCII(aHostname, normHostname);
if (NS_FAILED(rv)) {
return rv;
}
@ -272,10 +257,9 @@ nsEffectiveTLDService::GetPublicSuffixFromHost(const nsACString& aHostname,
NS_IMETHODIMP
nsEffectiveTLDService::GetKnownPublicSuffixFromHost(const nsACString& aHostname,
nsACString& aPublicSuffix) {
// Create a mutable copy of the hostname and normalize it to ACE.
// This will fail if the hostname includes invalid characters.
nsAutoCString normHostname(aHostname);
nsresult rv = NormalizeHostname(normHostname);
nsAutoCString normHostname;
nsresult rv = NS_DomainToASCIIAllowAnyGlyphfulASCII(aHostname, normHostname);
if (NS_FAILED(rv)) {
return rv;
}
@ -292,10 +276,9 @@ nsEffectiveTLDService::GetBaseDomainFromHost(const nsACString& aHostname,
nsACString& aBaseDomain) {
NS_ENSURE_TRUE(((int32_t)aAdditionalParts) >= 0, NS_ERROR_INVALID_ARG);
// Create a mutable copy of the hostname and normalize it to ACE.
// This will fail if the hostname includes invalid characters.
nsAutoCString normHostname(aHostname);
nsresult rv = NormalizeHostname(normHostname);
nsAutoCString normHostname;
nsresult rv = NS_DomainToASCIIAllowAnyGlyphfulASCII(aHostname, normHostname);
if (NS_FAILED(rv)) {
return rv;
}
@ -307,11 +290,12 @@ nsEffectiveTLDService::GetBaseDomainFromHost(const nsACString& aHostname,
NS_IMETHODIMP
nsEffectiveTLDService::GetNextSubDomain(const nsACString& aHostname,
nsACString& aBaseDomain) {
// Create a mutable copy of the hostname and normalize it to ACE.
// This will fail if the hostname includes invalid characters.
nsAutoCString normHostname(aHostname);
nsresult rv = NormalizeHostname(normHostname);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCString normHostname;
nsresult rv = NS_DomainToASCIIAllowAnyGlyphfulASCII(aHostname, normHostname);
if (NS_FAILED(rv)) {
return rv;
}
return GetBaseDomainInternal(normHostname, -1, false, aBaseDomain);
}
@ -497,21 +481,6 @@ nsresult nsEffectiveTLDService::GetBaseDomainInternal(
return NS_OK;
}
// Normalizes the given hostname, component by component. ASCII/ACE
// components are lower-cased, and UTF-8 components are normalized per
// RFC 3454 and converted to ACE.
nsresult nsEffectiveTLDService::NormalizeHostname(nsCString& aHostname) {
if (!IsAscii(aHostname)) {
nsresult rv = mIDNService->ConvertUTF8toACE(aHostname, aHostname);
if (NS_FAILED(rv)) {
return rv;
}
}
ToLowerCase(aHostname);
return NS_OK;
}
NS_IMETHODIMP
nsEffectiveTLDService::HasRootDomain(const nsACString& aInput,
const nsACString& aHost, bool* aResult) {
@ -536,8 +505,8 @@ nsEffectiveTLDService::HasKnownPublicSuffixFromHost(const nsACString& aHostname,
bool* aResult) {
// Create a mutable copy of the hostname and normalize it to ACE.
// This will fail if the hostname includes invalid characters.
nsCString hostname(aHostname);
nsresult rv = NormalizeHostname(hostname);
nsAutoCString hostname;
nsresult rv = NS_DomainToASCIIAllowAnyGlyphfulASCII(aHostname, hostname);
if (NS_FAILED(rv)) {
return rv;
}

Просмотреть файл

@ -44,12 +44,8 @@ class nsEffectiveTLDService final : public nsIEffectiveTLDService,
nsresult GetBaseDomainInternal(nsCString& aHostname, int32_t aAdditionalParts,
bool aOnlyKnownPublicSuffix,
nsACString& aBaseDomain);
nsresult NormalizeHostname(nsCString& aHostname);
~nsEffectiveTLDService();
// Only set in `Init()` which is called during service construction.
nsCOMPtr<nsIIDNService> mIDNService;
// The DAFSA provides a compact encoding of the rather large eTLD list.
mozilla::Maybe<mozilla::Dafsa> mGraph MOZ_GUARDED_BY(mGraphLock);

Просмотреть файл

@ -15,17 +15,15 @@
#include "nsUnicharUtils.h"
#include "nsUnicodeProperties.h"
#include "harfbuzz/hb.h"
#include "punycode.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Casting.h"
#include "mozilla/StaticPrefs_network.h"
#include "mozilla/TextUtils.h"
#include "mozilla/Utf8.h"
#include "mozilla/intl/FormatBuffer.h"
#include "mozilla/intl/UnicodeProperties.h"
#include "mozilla/intl/UnicodeScriptCodes.h"
#include "ICUUtils.h"
#include "nsNetUtil.h"
#include "nsStandardURL.h"
using namespace mozilla;
using namespace mozilla::intl;
@ -33,40 +31,39 @@ using namespace mozilla::unicode;
using namespace mozilla::net;
using mozilla::Preferences;
// Currently we use the non-transitional processing option -- see
// http://unicode.org/reports/tr46/
// To switch to transitional processing, change the value of this flag
// and kTransitionalProcessing in netwerk/test/unit/test_idna2008.js to true
// (revert bug 1218179).
const intl::IDNA::ProcessingType kIDNA2008_DefaultProcessingType =
intl::IDNA::ProcessingType::NonTransitional;
//-----------------------------------------------------------------------------
// According to RFC 1034 - 3.1. Name space specifications and terminology
// the maximum label size would be 63. However, this is enforced at the DNS
// level and none of the other browsers seem to not enforce the VerifyDnsLength
// check in https://unicode.org/reports/tr46/#ToASCII
// Instead, we choose a rather arbitrary but larger size.
static const uint32_t kMaxULabelSize = 256;
// RFC 3490 - 5. ACE prefix
static const char kACEPrefix[] = "xn--";
//-----------------------------------------------------------------------------
#define NS_NET_PREF_EXTRAALLOWED "network.IDN.extra_allowed_chars"
#define NS_NET_PREF_EXTRABLOCKED "network.IDN.extra_blocked_chars"
#define NS_NET_PREF_IDNRESTRICTION "network.IDN.restriction_profile"
static inline bool isOnlySafeChars(const nsString& in,
template <int N>
static inline bool TLDEqualsLiteral(mozilla::Span<const char32_t> aTLD,
const char (&aStr)[N]) {
if (aTLD.Length() != N - 1) {
return false;
}
const char* a = aStr;
for (const char32_t c : aTLD) {
if (c != char32_t(*a)) {
return false;
}
++a;
}
return true;
}
static inline bool isOnlySafeChars(mozilla::Span<const char32_t> aLabel,
const nsTArray<BlocklistRange>& aBlocklist) {
if (aBlocklist.IsEmpty()) {
return true;
}
const char16_t* cur = in.BeginReading();
const char16_t* end = in.EndReading();
for (; cur < end; ++cur) {
if (CharInBlocklist(*cur, aBlocklist)) {
for (const char32_t c : aLabel) {
if (c > 0xFFFF) {
// The blocklist only support BMP!
continue;
}
if (CharInBlocklist(char16_t(c), aBlocklist)) {
return false;
}
}
@ -131,215 +128,18 @@ void nsIDNService::prefsChanged(const char* pref) {
}
}
nsIDNService::nsIDNService() {
MOZ_ASSERT(NS_IsMainThread());
auto createResult =
mozilla::intl::IDNA::TryCreate(kIDNA2008_DefaultProcessingType);
MOZ_ASSERT(createResult.isOk());
mIDNA = createResult.unwrap();
}
nsIDNService::nsIDNService() { MOZ_ASSERT(NS_IsMainThread()); }
nsIDNService::~nsIDNService() = default;
nsresult nsIDNService::IDNA2008ToUnicode(const nsACString& input,
nsAString& output) {
NS_ConvertUTF8toUTF16 inputStr(input);
Span<const char16_t> inputSpan{inputStr};
intl::nsTStringToBufferAdapter buffer(output);
auto result = mIDNA->LabelToUnicode(inputSpan, buffer);
nsresult rv = NS_OK;
if (result.isErr()) {
rv = ICUUtils::ICUErrorToNsResult(result.unwrapErr());
if (rv == NS_ERROR_FAILURE) {
rv = NS_ERROR_MALFORMED_URI;
}
}
NS_ENSURE_SUCCESS(rv, rv);
intl::IDNA::Info info = result.unwrap();
if (info.HasErrors()) {
rv = NS_ERROR_MALFORMED_URI;
}
return rv;
}
nsresult nsIDNService::IDNA2008StringPrep(const nsAString& input,
nsAString& output,
stringPrepFlag flag) {
Span<const char16_t> inputSpan{input};
intl::nsTStringToBufferAdapter buffer(output);
auto result = mIDNA->LabelToUnicode(inputSpan, buffer);
nsresult rv = NS_OK;
if (result.isErr()) {
rv = ICUUtils::ICUErrorToNsResult(result.unwrapErr());
if (rv == NS_ERROR_FAILURE) {
rv = NS_ERROR_MALFORMED_URI;
}
}
NS_ENSURE_SUCCESS(rv, rv);
intl::IDNA::Info info = result.unwrap();
// Output the result of nameToUnicode even if there were errors.
// But in the case of invalid punycode, the uidna_labelToUnicode result
// appears to get an appended U+FFFD REPLACEMENT CHARACTER, which will
// confuse our subsequent processing, so we drop that.
// (https://bugzilla.mozilla.org/show_bug.cgi?id=1399540#c9)
if ((info.HasInvalidPunycode() || info.HasInvalidAceLabel()) &&
!output.IsEmpty() && output.Last() == 0xfffd) {
output.Truncate(output.Length() - 1);
}
if (flag == eStringPrepIgnoreErrors) {
return NS_OK;
}
if (flag == eStringPrepForDNS) {
// We ignore errors if the result is empty, or if the errors were just
// invalid hyphens (not punycode-decoding failure or invalid chars).
if (!output.IsEmpty()) {
if (info.HasErrorsIgnoringInvalidHyphen()) {
output.Truncate();
rv = NS_ERROR_MALFORMED_URI;
}
}
} else {
if (info.HasErrors()) {
rv = NS_ERROR_MALFORMED_URI;
}
}
return rv;
}
NS_IMETHODIMP nsIDNService::ConvertUTF8toACE(const nsACString& input,
nsACString& ace) {
return UTF8toACE(input, ace, eStringPrepForDNS);
}
nsresult nsIDNService::UTF8toACE(const nsACString& input, nsACString& ace,
stringPrepFlag flag) {
nsresult rv;
NS_ConvertUTF8toUTF16 ustr(input);
// map ideographic period to ASCII period etc.
normalizeFullStops(ustr);
uint32_t len, offset;
len = 0;
offset = 0;
nsAutoCString encodedBuf;
nsAString::const_iterator start, end;
ustr.BeginReading(start);
ustr.EndReading(end);
ace.Truncate();
// encode nodes if non ASCII
while (start != end) {
len++;
if (*start++ == (char16_t)'.') {
rv = stringPrepAndACE(Substring(ustr, offset, len - 1), encodedBuf, flag);
NS_ENSURE_SUCCESS(rv, rv);
ace.Append(encodedBuf);
ace.Append('.');
offset += len;
len = 0;
}
}
// encode the last node if non ASCII
if (len) {
rv = stringPrepAndACE(Substring(ustr, offset, len), encodedBuf, flag);
NS_ENSURE_SUCCESS(rv, rv);
ace.Append(encodedBuf);
}
return NS_OK;
return NS_DomainToASCIIAllowAnyGlyphfulASCII(input, ace);
}
NS_IMETHODIMP nsIDNService::ConvertACEtoUTF8(const nsACString& input,
nsACString& _retval) {
return ACEtoUTF8(input, _retval, eStringPrepForDNS);
}
nsresult nsIDNService::ACEtoUTF8(const nsACString& input, nsACString& _retval,
stringPrepFlag flag) {
// RFC 3490 - 4.2 ToUnicode
// ToUnicode never fails. If any step fails, then the original input
// sequence is returned immediately in that step.
//
// Note that this refers to the decoding of a single label.
// ACEtoUTF8 may be called with a sequence of labels separated by dots;
// this test applies individually to each label.
uint32_t len = 0, offset = 0;
nsAutoCString decodedBuf;
nsACString::const_iterator start, end;
input.BeginReading(start);
input.EndReading(end);
_retval.Truncate();
if (input.IsEmpty()) {
return NS_OK;
}
nsAutoCString tld;
nsCString::const_iterator it = end, tldEnd = end;
--it;
if (it != start && *it == (char16_t)'.') {
// This is an FQDN (ends in .)
// Skip this dot to extract the TLD
tldEnd = it;
--it;
}
// Find last . and compute TLD
while (it != start) {
if (*it == (char16_t)'.') {
++it;
tld.Assign(Substring(it, tldEnd));
break;
}
--it;
}
// loop and decode nodes
while (start != end) {
len++;
if (*start++ == '.') {
nsDependentCSubstring origLabel(input, offset, len - 1);
if (NS_FAILED(decodeACE(origLabel, decodedBuf, flag, tld))) {
// If decoding failed, use the original input sequence
// for this label.
_retval.Append(origLabel);
} else {
_retval.Append(decodedBuf);
}
_retval.Append('.');
offset += len;
len = 0;
}
}
// decode the last node
if (len) {
nsDependentCSubstring origLabel(input, offset, len);
if (NS_FAILED(decodeACE(origLabel, decodedBuf, flag, tld))) {
_retval.Append(origLabel);
} else {
_retval.Append(decodedBuf);
}
}
return NS_OK;
return NS_DomainToUnicodeAllowAnyGlyphfulASCII(input, _retval);
}
NS_IMETHODIMP nsIDNService::IsACE(const nsACString& input, bool* _retval) {
@ -368,293 +168,13 @@ NS_IMETHODIMP nsIDNService::IsACE(const nsACString& input, bool* _retval) {
return NS_OK;
}
nsresult nsIDNService::Normalize(const nsACString& input, nsACString& output) {
// protect against bogus input
NS_ENSURE_TRUE(IsUtf8(input), NS_ERROR_UNEXPECTED);
NS_ConvertUTF8toUTF16 inUTF16(input);
normalizeFullStops(inUTF16);
// pass the domain name to stringprep label by label
nsAutoString outUTF16, outLabel;
uint32_t len = 0, offset = 0;
nsresult rv;
nsAString::const_iterator start, end;
inUTF16.BeginReading(start);
inUTF16.EndReading(end);
while (start != end) {
len++;
if (*start++ == char16_t('.')) {
rv = stringPrep(Substring(inUTF16, offset, len - 1), outLabel,
eStringPrepIgnoreErrors);
NS_ENSURE_SUCCESS(rv, rv);
outUTF16.Append(outLabel);
outUTF16.Append(char16_t('.'));
offset += len;
len = 0;
}
}
if (len) {
rv = stringPrep(Substring(inUTF16, offset, len), outLabel,
eStringPrepIgnoreErrors);
NS_ENSURE_SUCCESS(rv, rv);
outUTF16.Append(outLabel);
}
CopyUTF16toUTF8(outUTF16, output);
return NS_OK;
}
NS_IMETHODIMP nsIDNService::ConvertToDisplayIDN(const nsACString& input,
bool* _isASCII,
nsACString& _retval) {
// If host is ACE, then convert to UTF-8 if the host is in the IDN whitelist.
// Else, if host is already UTF-8, then make sure it is normalized per IDN.
nsresult rv = NS_OK;
// Even if the hostname is not ASCII, individual labels may still be ACE, so
// test IsACE before testing IsASCII
bool isACE;
IsACE(input, &isACE);
if (IsAscii(input)) {
// first, canonicalize the host to lowercase, for whitelist lookup
_retval = input;
ToLowerCase(_retval);
if (isACE && !StaticPrefs::network_IDN_show_punycode()) {
// ACEtoUTF8() can't fail, but might return the original ACE string
nsAutoCString temp(_retval);
// Convert from ACE to UTF8 only those labels which are considered safe
// for display
ACEtoUTF8(temp, _retval, eStringPrepForUI);
*_isASCII = IsAscii(_retval);
} else {
*_isASCII = true;
}
} else {
// We have to normalize the hostname before testing against the domain
// whitelist (see bug 315411), and to ensure the entire string gets
// normalized.
//
// Normalization and the tests for safe display below, assume that the
// input is Unicode, so first convert any ACE labels to UTF8
if (isACE) {
nsAutoCString temp;
ACEtoUTF8(input, temp, eStringPrepIgnoreErrors);
rv = Normalize(temp, _retval);
} else {
rv = Normalize(input, _retval);
}
if (NS_FAILED(rv)) {
return rv;
}
if (StaticPrefs::network_IDN_show_punycode() &&
NS_SUCCEEDED(UTF8toACE(_retval, _retval, eStringPrepIgnoreErrors))) {
*_isASCII = true;
return NS_OK;
}
// normalization could result in an ASCII-only hostname. alternatively, if
// the host is converted to ACE by the normalizer, then the host may contain
// unsafe characters, so leave it ACE encoded. see bug 283016, bug 301694,
// and bug 309311.
*_isASCII = IsAscii(_retval);
if (!*_isASCII) {
// UTF8toACE with eStringPrepForUI may return a domain name where
// some labels are in UTF-8 and some are in ACE, depending on
// whether they are considered safe for display
rv = UTF8toACE(_retval, _retval, eStringPrepForUI);
*_isASCII = IsAscii(_retval);
return rv;
}
}
return NS_OK;
} // Will generate a mutex still-held warning
//-----------------------------------------------------------------------------
static nsresult utf16ToUcs4(const nsAString& in, uint32_t* out,
uint32_t outBufLen, uint32_t* outLen) {
uint32_t i = 0;
nsAString::const_iterator start, end;
in.BeginReading(start);
in.EndReading(end);
while (start != end) {
char16_t curChar;
curChar = *start++;
if (start != end && NS_IS_SURROGATE_PAIR(curChar, *start)) {
out[i] = SURROGATE_TO_UCS4(curChar, *start);
++start;
} else {
out[i] = curChar;
}
i++;
if (i >= outBufLen) {
return NS_ERROR_MALFORMED_URI;
}
}
out[i] = (uint32_t)'\0';
*outLen = i;
return NS_OK;
}
static nsresult punycode(const nsAString& in, nsACString& out) {
uint32_t ucs4Buf[kMaxULabelSize + 1];
uint32_t ucs4Len = 0u;
nsresult rv = utf16ToUcs4(in, ucs4Buf, kMaxULabelSize, &ucs4Len);
NS_ENSURE_SUCCESS(rv, rv);
// need maximum 20 bits to encode 16 bit Unicode character
// (include null terminator)
const uint32_t kEncodedBufSize = kMaxULabelSize * 20 / 8 + 1 + 1;
char encodedBuf[kEncodedBufSize];
punycode_uint encodedLength = kEncodedBufSize;
enum punycode_status status =
punycode_encode(ucs4Len, ucs4Buf, nullptr, &encodedLength, encodedBuf);
if (punycode_success != status || encodedLength >= kEncodedBufSize) {
return NS_ERROR_MALFORMED_URI;
}
encodedBuf[encodedLength] = '\0';
out.Assign(nsDependentCString(kACEPrefix) + nsDependentCString(encodedBuf));
nsresult rv = NS_DomainToDisplayAllowAnyGlyphfulASCII(input, _retval);
return rv;
}
// RFC 3454
//
// 1) Map -- For each character in the input, check if it has a mapping
// and, if so, replace it with its mapping. This is described in section 3.
//
// 2) Normalize -- Possibly normalize the result of step 1 using Unicode
// normalization. This is described in section 4.
//
// 3) Prohibit -- Check for any characters that are not allowed in the
// output. If any are found, return an error. This is described in section
// 5.
//
// 4) Check bidi -- Possibly check for right-to-left characters, and if any
// are found, make sure that the whole string satisfies the requirements
// for bidirectional strings. If the string does not satisfy the requirements
// for bidirectional strings, return an error. This is described in section 6.
//
// 5) Check unassigned code points -- If allowUnassigned is false, check for
// any unassigned Unicode points and if any are found return an error.
// This is described in section 7.
//
nsresult nsIDNService::stringPrep(const nsAString& in, nsAString& out,
stringPrepFlag flag) {
return IDNA2008StringPrep(in, out, flag);
}
nsresult nsIDNService::stringPrepAndACE(const nsAString& in, nsACString& out,
stringPrepFlag flag) {
nsresult rv = NS_OK;
out.Truncate();
if (IsAscii(in)) {
LossyCopyUTF16toASCII(in, out);
// If label begins with xn-- we still want to check its validity
if (!StringBeginsWith(in, u"xn--"_ns, nsCaseInsensitiveStringComparator)) {
return NS_OK;
}
}
nsAutoString strPrep;
rv = stringPrep(in, strPrep, flag);
if (flag == eStringPrepForDNS) {
NS_ENSURE_SUCCESS(rv, rv);
}
if (IsAscii(strPrep)) {
LossyCopyUTF16toASCII(strPrep, out);
return NS_OK;
}
if (flag == eStringPrepForUI && NS_SUCCEEDED(rv) && isLabelSafe(in, u""_ns)) {
CopyUTF16toUTF8(strPrep, out);
return NS_OK;
}
return punycode(strPrep, out);
}
// RFC 3490
// 1) Whenever dots are used as label separators, the following characters
// MUST be recognized as dots: U+002E (full stop), U+3002 (ideographic full
// stop), U+FF0E (fullwidth full stop), U+FF61 (halfwidth ideographic full
// stop).
void nsIDNService::normalizeFullStops(nsAString& s) {
nsAString::const_iterator start, end;
s.BeginReading(start);
s.EndReading(end);
int32_t index = 0;
while (start != end) {
switch (*start) {
case 0x3002:
case 0xFF0E:
case 0xFF61:
s.ReplaceLiteral(index, 1, u".");
break;
default:
break;
}
start++;
index++;
}
}
nsresult nsIDNService::decodeACE(const nsACString& in, nsACString& out,
stringPrepFlag flag, const nsACString& aTLD) {
bool isAce;
IsACE(in, &isAce);
if (!isAce) {
out.Assign(in);
return NS_OK;
}
nsAutoString utf16;
nsresult result = IDNA2008ToUnicode(in, utf16);
NS_ENSURE_SUCCESS(result, result);
NS_ConvertUTF8toUTF16 tld(aTLD);
if (flag != eStringPrepForUI || isLabelSafe(utf16, tld)) {
CopyUTF16toUTF8(utf16, out);
} else {
out.Assign(in);
return NS_OK;
}
// Validation: encode back to ACE and compare the strings
nsAutoCString ace;
nsresult rv = UTF8toACE(out, ace, flag);
NS_ENSURE_SUCCESS(rv, rv);
if (flag == eStringPrepForDNS &&
!ace.Equals(in, nsCaseInsensitiveCStringComparator)) {
return NS_ERROR_MALFORMED_URI;
}
return NS_OK;
}
//-----------------------------------------------------------------------------
namespace mozilla::net {
@ -678,31 +198,30 @@ enum ScriptCombo : int32_t {
} // namespace mozilla::net
bool nsIDNService::isLabelSafe(const nsAString& label, const nsAString& tld) {
bool nsIDNService::IsLabelSafe(mozilla::Span<const char32_t> aLabel,
mozilla::Span<const char32_t> aTLD) {
restrictionProfile profile{eASCIIOnlyProfile};
{
AutoReadLock lock(mLock);
if (!isOnlySafeChars(PromiseFlatString(label), mIDNBlocklist)) {
if (!isOnlySafeChars(aLabel, mIDNBlocklist)) {
return false;
}
// We should never get here if the label is ASCII
NS_ASSERTION(!IsAscii(label), "ASCII label in IDN checking");
if (mRestrictionProfile == eASCIIOnlyProfile) {
return false;
}
profile = mRestrictionProfile;
}
nsAString::const_iterator current, end;
label.BeginReading(current);
label.EndReading(end);
mozilla::Span<const char32_t>::const_iterator current = aLabel.cbegin();
mozilla::Span<const char32_t>::const_iterator end = aLabel.cend();
Script lastScript = Script::INVALID;
uint32_t previousChar = 0;
uint32_t baseChar = 0; // last non-diacritic seen (base char for marks)
uint32_t savedNumberingSystem = 0;
char32_t previousChar = 0;
char32_t baseChar = 0; // last non-diacritic seen (base char for marks)
char32_t savedNumberingSystem = 0;
// Simplified/Traditional Chinese check temporarily disabled -- bug 857481
#if 0
HanVariantType savedHanVariant = HVT_NotHan;
@ -711,11 +230,7 @@ bool nsIDNService::isLabelSafe(const nsAString& label, const nsAString& tld) {
ScriptCombo savedScript = ScriptCombo::UNSET;
while (current != end) {
uint32_t ch = *current++;
if (current != end && NS_IS_SURROGATE_PAIR(ch, *current)) {
ch = SURROGATE_TO_UCS4(ch, *current++);
}
char32_t ch = *current++;
IdentifierType idType = GetIdentifierType(ch);
if (idType == IDTYPE_RESTRICTED) {
@ -754,15 +269,15 @@ bool nsIDNService::isLabelSafe(const nsAString& label, const nsAString& tld) {
}
// U+00B7 is only allowed on Catalan domains between two l's.
if (ch == 0xB7 && (!tld.EqualsLiteral("cat") || previousChar != 'l' ||
if (ch == 0xB7 && (!TLDEqualsLiteral(aTLD, "cat") || previousChar != 'l' ||
current == end || *current != 'l')) {
return false;
}
// Disallow Icelandic confusables for domains outside Icelandic and Faroese
// ccTLD (.is, .fo)
if ((ch == 0xFE || ch == 0xF0) && !tld.EqualsLiteral("is") &&
!tld.EqualsLiteral("fo")) {
if ((ch == 0xFE || ch == 0xF0) && !TLDEqualsLiteral(aTLD, "is") &&
!TLDEqualsLiteral(aTLD, "fo")) {
return false;
}
@ -911,3 +426,12 @@ bool nsIDNService::illegalScriptCombo(restrictionProfile profile, Script script,
return ((savedScript == OTHR && profile == eHighlyRestrictiveProfile) ||
savedScript == FAIL);
}
extern "C" MOZ_EXPORT bool mozilla_net_is_label_safe(const char32_t* aLabel,
size_t aLabelLen,
const char32_t* aTld,
size_t aTldLen) {
return static_cast<nsIDNService*>(nsStandardURL::GetIDNService())
->IsLabelSafe(mozilla::Span<const char32_t>(aLabel, aLabelLen),
mozilla::Span<const char32_t>(aTld, aTldLen));
}

Просмотреть файл

@ -7,16 +7,11 @@
#define nsIDNService_h__
#include "nsIIDNService.h"
#include "nsCOMPtr.h"
#include "mozilla/RWLock.h"
#include "mozilla/intl/UnicodeScriptCodes.h"
#include "mozilla/net/IDNBlocklistUtils.h"
#include "mozilla/intl/IDNA.h"
#include "mozilla/UniquePtr.h"
#include "nsString.h"
#include "nsStringFwd.h"
#include "mozilla/Span.h"
class nsIPrefBranch;
@ -41,66 +36,6 @@ class nsIDNService final : public nsIIDNService {
virtual ~nsIDNService();
private:
enum stringPrepFlag {
eStringPrepForDNS,
eStringPrepForUI,
eStringPrepIgnoreErrors
};
/**
* Convert the following characters that must be recognized as label
* separators per RFC 3490 to ASCII full stop characters
*
* U+3002 (ideographic full stop)
* U+FF0E (fullwidth full stop)
* U+FF61 (halfwidth ideographic full stop)
*/
void normalizeFullStops(nsAString& s);
/**
* Convert and encode a DNS label in ACE/punycode.
* @param flag
* if eStringPrepIgnoreErrors, all non-ASCII labels are
* converted to punycode.
* if eStringPrepForUI, only labels that are considered safe
* for display are converted.
* @see isLabelSafe
* if eStringPrepForDNS and stringPrep finds an illegal
* character, returns NS_FAILURE and out is empty
*/
nsresult stringPrepAndACE(const nsAString& in, nsACString& out,
stringPrepFlag flag);
/**
* Convert a DNS label using the stringprep profile defined in RFC 3454
*/
nsresult stringPrep(const nsAString& in, nsAString& out, stringPrepFlag flag);
/**
* Decode an ACE-encoded DNS label to UTF-8
*
* @param flag
* if eStringPrepForUI and the label is not considered safe to
* display, the output is the same as the input
* @see isLabelSafe
*/
nsresult decodeACE(const nsACString& in, nsACString& out, stringPrepFlag flag,
const nsACString& tld);
/**
* Convert complete domain names between UTF8 and ACE and vice versa
*
* @param flag is passed to decodeACE or stringPrepAndACE for each
* label individually, so the output may contain some labels in
* punycode and some in UTF-8
*/
nsresult UTF8toACE(const nsACString& input, nsACString& ace,
stringPrepFlag flag);
nsresult ACEtoUTF8(const nsACString& input, nsACString& _retval,
stringPrepFlag flag);
nsresult Normalize(const nsACString& input, nsACString& output);
void prefsChanged(const char* pref);
static void PrefChanged(const char* aPref, void* aSelf) {
@ -108,6 +43,7 @@ class nsIDNService final : public nsIIDNService {
self->prefsChanged(aPref);
}
public:
/**
* Determine whether a label is considered safe to display to the user
* according to the algorithm defined in UTR 39 and the profile
@ -135,9 +71,10 @@ class nsIDNService final : public nsIIDNService {
* Both simplified-only and traditional-only Chinese characters
* XXX this test was disabled by bug 857481
*/
bool isLabelSafe(const nsAString& label, const nsAString& tld)
MOZ_EXCLUDES(mLock);
bool IsLabelSafe(mozilla::Span<const char32_t> aLabel,
mozilla::Span<const char32_t> aTLD) MOZ_EXCLUDES(mLock);
private:
/**
* Restriction-level Detection profiles defined in UTR 39
* http://www.unicode.org/reports/tr39/#Restriction_Level_Detection,
@ -167,20 +104,6 @@ class nsIDNService final : public nsIIDNService {
mozilla::intl::Script script,
mozilla::net::ScriptCombo& savedScript);
/**
* Convert a DNS label from ASCII to Unicode using IDNA2008
*/
nsresult IDNA2008ToUnicode(const nsACString& input, nsAString& output);
/**
* Convert a DNS label to a normalized form conforming to IDNA2008
*/
nsresult IDNA2008StringPrep(const nsAString& input, nsAString& output,
stringPrepFlag flag);
// never mutated after initializing.
mozilla::UniquePtr<mozilla::intl::IDNA> mIDNA;
// We use this rwlock to guard access to:
// |mIDNBlocklist|, |mRestrictionProfile|
mozilla::RWLock mLock{"nsIDNService"};
@ -193,4 +116,9 @@ class nsIDNService final : public nsIIDNService {
eASCIIOnlyProfile};
};
extern "C" MOZ_EXPORT bool mozilla_net_is_label_safe(const char32_t* aLabel,
size_t aLabelLen,
const char32_t* aTld,
size_t aTldLen);
#endif // nsIDNService_h__

Просмотреть файл

@ -5,48 +5,70 @@
#include "nsISupports.idl"
/**
* nsIIDNService interface.
*
* IDN (Internationalized Domain Name) support. Provides facilities
* for manipulating IDN hostnames according to the specification set
* forth by the IETF.
* for manipulating IDN hostnames according to UTS #46 as parametrized
* by the WHATWG URL Standard.
*
* IDN effort:
* http://www.ietf.org/html.characters/idn-charter.html
* http://www.i-dns.net
* UTS #46: https://www.unicode.org/reports/tr46/
*
* IDNA specification:
* http://search.ietf.org/internet-drafts/draft-ietf-idn-idna-06.txt
* URL Standard: https://url.spec.whatwg.org/
*/
[scriptable, uuid(a592a60e-3621-4f19-a318-2bf233cfad3e)]
interface nsIIDNService : nsISupports
{
/**
* Prepares the input hostname according to IDNA ToASCII operation,
* the input hostname is assumed to be UTF8-encoded.
* The UTS #46 ToASCII operation as parametrized by the WHATWG URL Standard,
* except allows any ASCII character that has a glyph.
*
* Use this function to prepare a host name for network protocols.
*
* Do not try to optimize and avoid calling this function if you already
* have ASCII. This function optimizes internally, and calling it is
* required for correctness!
*
* The function is available to C++ callers as `NS_DomainToASCIIAllowAnyGlyphfulASCII`.
*
* Rust callers that don't happen to be using XPCOM strings are better
* off using the `idna` crate directly.
*/
ACString convertUTF8toACE(in AUTF8String input);
/**
* The UTS #46 ToUnicode operation as parametrized by the WHATWG URL Standard,
* except allows any ASCII character that has a glyph, except potentially misleading
* labels are treated according to ToASCII instead.
*
* Use this function to prepare a host name for display to the user.
*
* The function is available to C++ callers as `NS_DomainToDisplayAllowAnyGlyphfulASCII`.
*
* The function is available to privileged JavaScript callers via
* nsIIDNService.
*
* Rust callers that don't happen to be using XPCOM strings are better
* off using the `idna` crate directly. (See `idna_glue` for what policy
* closure to use.)
*/
AUTF8String convertToDisplayIDN(in AUTF8String input);
/**
* This is the ToUnicode operation as specified in the IDNA proposal,
* with an additional step to encode the result in UTF-8.
* It takes an ACE-encoded hostname and performs ToUnicode to it, then
* encodes the resulting string into UTF8.
* The UTS #46 ToUnicode operation as parametrized by the WHATWG URL Standard,
* except allows any ASCII character that has a glyph.
*
* It's most likely INCORRECT to call this function, and `NS_DomainToDisplay`
* should typically be called instead. Please avoid adding new callers, so
* that this conversion could be removed entirely!
*
* The function is available to C++ callers as `NS_DomainToUnicodeAllowAnyGlyphfulASCII`.
*
* Rust callers that don't happen to be using XPCOM strings are better
* off using the `idna` crate directly.
*/
AUTF8String convertACEtoUTF8(in ACString input);
/**
* Checks if the input string is ACE encoded or not.
* DO NOT USE! Checks if the input string is ACE encoded or not.
*/
boolean isACE(in ACString input);
/**
* Normalizes and converts a host to UTF-8 if the host is in the IDN
* whitelist, otherwise converts it to ACE. This is useful for display
* purposes and to ensure an encoding consistent with nsIURI::GetHost().
* If the result is ASCII or ACE encoded, |isASCII| will be true.
*/
AUTF8String convertToDisplayIDN(in AUTF8String input, out boolean isASCII);
};

Просмотреть файл

@ -1,325 +0,0 @@
/*
punycode.c from RFC 3492
http://www.nicemice.net/idn/
Adam M. Costello
http://www.nicemice.net/amc/
This is ANSI C code (C89) implementing Punycode (RFC 3492).
C. Disclaimer and license
Regarding this entire document or any portion of it (including
the pseudocode and C code), the author makes no guarantees and
is not responsible for any damage resulting from its use. The
author grants irrevocable permission to anyone to use, modify,
and distribute it in any way that does not diminish the rights
of anyone else to use, modify, and distribute it, provided that
redistributed derivative works do not contain misleading author or
version information. Derivative works need not be licensed under
similar terms.
*/
#include "punycode.h"
/**********************************************************/
/* Implementation (would normally go in its own .c file): */
#include <string.h>
/*** Bootstring parameters for Punycode ***/
enum {
base = 36,
tmin = 1,
tmax = 26,
skew = 38,
damp = 700,
initial_bias = 72,
initial_n = 0x80,
delimiter = 0x2D
};
/* basic(cp) tests whether cp is a basic code point: */
#define basic(cp) ((punycode_uint)(cp) < 0x80)
/* delim(cp) tests whether cp is a delimiter: */
#define delim(cp) ((cp) == delimiter)
/* decode_digit(cp) returns the numeric value of a basic code */
/* point (for use in representing integers) in the range 0 to */
/* base-1, or base if cp is does not represent a value. */
static punycode_uint decode_digit(punycode_uint cp) {
return cp - 48 < 10 ? cp - 22
: cp - 65 < 26 ? cp - 65
: cp - 97 < 26 ? cp - 97
: base;
}
/* encode_digit(d,flag) returns the basic code point whose value */
/* (when used for representing integers) is d, which needs to be in */
/* the range 0 to base-1. The lowercase form is used unless flag is */
/* nonzero, in which case the uppercase form is used. The behavior */
/* is undefined if flag is nonzero and digit d has no uppercase form. */
static char encode_digit(punycode_uint d, int flag) {
return d + 22 + 75 * (d < 26) - ((flag != 0) << 5);
/* 0..25 map to ASCII a..z or A..Z */
/* 26..35 map to ASCII 0..9 */
}
/* flagged(bcp) tests whether a basic code point is flagged */
/* (uppercase). The behavior is undefined if bcp is not a */
/* basic code point. */
#define flagged(bcp) ((punycode_uint)(bcp)-65 < 26)
/* encode_basic(bcp,flag) forces a basic code point to lowercase */
/* if flag is zero, uppercase if flag is nonzero, and returns */
/* the resulting code point. The code point is unchanged if it */
/* is caseless. The behavior is undefined if bcp is not a basic */
/* code point. */
static char encode_basic(punycode_uint bcp, int flag) {
bcp -= (bcp - 97 < 26) << 5;
return bcp + ((!flag && (bcp - 65 < 26)) << 5);
}
/*** Platform-specific constants ***/
/* maxint is the maximum value of a punycode_uint variable: */
static const punycode_uint maxint = (punycode_uint)-1;
/* Because maxint is unsigned, -1 becomes the maximum value. */
/*** Bias adaptation function ***/
static punycode_uint adapt(punycode_uint delta, punycode_uint numpoints,
int firsttime) {
punycode_uint k;
delta = firsttime ? delta / damp : delta >> 1;
/* delta >> 1 is a faster way of doing delta / 2 */
delta += delta / numpoints;
for (k = 0; delta > ((base - tmin) * tmax) / 2; k += base) {
delta /= base - tmin;
}
return k + (base - tmin + 1) * delta / (delta + skew);
}
/*** Main encode function ***/
enum punycode_status punycode_encode(punycode_uint input_length,
const punycode_uint input[],
const unsigned char case_flags[],
punycode_uint* output_length,
char output[]) {
punycode_uint n, delta, h, b, out, max_out, bias, j, m, q, k, t;
/* Initialize the state: */
n = initial_n;
delta = out = 0;
max_out = *output_length;
bias = initial_bias;
/* Handle the basic code points: */
for (j = 0; j < input_length; ++j) {
if (basic(input[j])) {
if (max_out - out < 2) {
return punycode_big_output;
}
output[out++] =
case_flags ? encode_basic(input[j], case_flags[j]) : (char)input[j];
}
/* else if (input[j] < n) return punycode_bad_input; */
/* (not needed for Punycode with unsigned code points) */
}
h = b = out;
/* h is the number of code points that have been handled, b is the */
/* number of basic code points, and out is the number of characters */
/* that have been output. */
if (b > 0) {
output[out++] = delimiter;
}
/* Main encoding loop: */
while (h < input_length) {
/* All non-basic code points < n have been */
/* handled already. Find the next larger one: */
for (m = maxint, j = 0; j < input_length; ++j) {
/* if (basic(input[j])) continue; */
/* (not needed for Punycode) */
if (input[j] >= n && input[j] < m) {
m = input[j];
}
}
/* Increase delta enough to advance the decoder's */
/* <n,i> state to <m,0>, but guard against overflow: */
if (m - n > (maxint - delta) / (h + 1)) {
return punycode_overflow;
}
delta += (m - n) * (h + 1);
n = m;
for (j = 0; j < input_length; ++j) {
/* Punycode does not need to check whether input[j] is basic: */
if (input[j] < n /* || basic(input[j]) */) {
if (++delta == 0) {
return punycode_overflow;
}
}
if (input[j] == n) {
/* Represent delta as a generalized variable-length integer: */
for (q = delta, k = base;; k += base) {
if (out >= max_out) {
return punycode_big_output;
}
t = k <= bias /* + tmin */ ? tmin : /* +tmin not needed */
k >= bias + tmax ? tmax
: k - bias;
if (q < t) {
break;
}
output[out++] = encode_digit(t + (q - t) % (base - t), 0);
q = (q - t) / (base - t);
}
output[out++] = encode_digit(q, case_flags && case_flags[j]);
bias = adapt(delta, h + 1, h == b);
delta = 0;
++h;
}
}
++delta, ++n;
}
*output_length = out;
return punycode_success;
}
/*** Main decode function ***/
enum punycode_status punycode_decode(punycode_uint input_length,
const char input[],
punycode_uint* output_length,
punycode_uint output[],
unsigned char case_flags[]) {
punycode_uint n, out, i, max_out, bias, b, j, in, oldi, w, k, digit, t;
if (!input_length) {
return punycode_bad_input;
}
/* Initialize the state: */
n = initial_n;
out = i = 0;
max_out = *output_length;
bias = initial_bias;
/* Handle the basic code points: Let b be the number of input code */
/* points before the last delimiter, or 0 if there is none, then */
/* copy the first b code points to the output. */
for (b = 0, j = input_length - 1; j > 0; --j) {
if (delim(input[j])) {
b = j;
break;
}
}
if (b > max_out) {
return punycode_big_output;
}
for (j = 0; j < b; ++j) {
if (case_flags) {
case_flags[out] = flagged(input[j]);
}
if (!basic(input[j])) {
return punycode_bad_input;
}
output[out++] = input[j];
}
/* Main decoding loop: Start just after the last delimiter if any */
/* basic code points were copied; start at the beginning otherwise. */
for (in = b > 0 ? b + 1 : 0; in < input_length; ++out) {
/* in is the index of the next character to be consumed, and */
/* out is the number of code points in the output array. */
/* Decode a generalized variable-length integer into delta, */
/* which gets added to i. The overflow checking is easier */
/* if we increase i as we go, then subtract off its starting */
/* value at the end to obtain delta. */
for (oldi = i, w = 1, k = base;; k += base) {
if (in >= input_length) {
return punycode_bad_input;
}
digit = decode_digit(input[in++]);
if (digit >= base) {
return punycode_bad_input;
}
if (digit > (maxint - i) / w) {
return punycode_overflow;
}
i += digit * w;
t = k <= bias /* + tmin */ ? tmin : /* +tmin not needed */
k >= bias + tmax ? tmax
: k - bias;
if (digit < t) {
break;
}
if (w > maxint / (base - t)) {
return punycode_overflow;
}
w *= (base - t);
}
bias = adapt(i - oldi, out + 1, oldi == 0);
/* i was supposed to wrap around from out+1 to 0, */
/* incrementing n each time, so we'll fix that now: */
if (i / (out + 1) > maxint - n) {
return punycode_overflow;
}
n += i / (out + 1);
i %= (out + 1);
/* Insert n at position i of the output: */
/* not needed for Punycode: */
/* if (decode_digit(n) <= base) return punycode_invalid_input; */
if (out >= max_out) {
return punycode_big_output;
}
if (case_flags) {
memmove(case_flags + i + 1, case_flags + i, out - i);
/* Case of last character determines uppercase flag: */
case_flags[i] = flagged(input[in - 1]);
}
memmove(output + i + 1, output + i, (out - i) * sizeof *output);
output[i++] = n;
}
*output_length = out;
return punycode_success;
}

Просмотреть файл

@ -1,106 +0,0 @@
/*
punycode.c from RFC 3492
http://www.nicemice.net/idn/
Adam M. Costello
http://www.nicemice.net/amc/
This is ANSI C code (C89) implementing Punycode (RFC 3492).
C. Disclaimer and license
Regarding this entire document or any portion of it (including
the pseudocode and C code), the author makes no guarantees and
is not responsible for any damage resulting from its use. The
author grants irrevocable permission to anyone to use, modify,
and distribute it in any way that does not diminish the rights
of anyone else to use, modify, and distribute it, provided that
redistributed derivative works do not contain misleading author or
version information. Derivative works need not be licensed under
similar terms.
*/
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/************************************************************/
/* Public interface (would normally go in its own .h file): */
#include <limits.h>
enum punycode_status {
punycode_success,
punycode_bad_input, /* Input is invalid. */
punycode_big_output, /* Output would exceed the space provided. */
punycode_overflow /* Input needs wider integers to process. */
};
#if UINT_MAX >= (1 << 26) - 1
typedef unsigned int punycode_uint;
#else
typedef unsigned long punycode_uint;
#endif
enum punycode_status punycode_encode(punycode_uint input_length,
const punycode_uint input[],
const unsigned char case_flags[],
punycode_uint* output_length,
char output[]);
/* punycode_encode() converts Unicode to Punycode. The input */
/* is represented as an array of Unicode code points (not code */
/* units; surrogate pairs are not allowed), and the output */
/* will be represented as an array of ASCII code points. The */
/* output string is *not* null-terminated; it will contain */
/* zeros if and only if the input contains zeros. (Of course */
/* the caller can leave room for a terminator and add one if */
/* needed.) The input_length is the number of code points in */
/* the input. The output_length is an in/out argument: the */
/* caller passes in the maximum number of code points that it */
/* can receive, and on successful return it will contain the */
/* number of code points actually output. The case_flags array */
/* holds input_length boolean values, where nonzero suggests that */
/* the corresponding Unicode character be forced to uppercase */
/* after being decoded (if possible), and zero suggests that */
/* it be forced to lowercase (if possible). ASCII code points */
/* are encoded literally, except that ASCII letters are forced */
/* to uppercase or lowercase according to the corresponding */
/* uppercase flags. If case_flags is a null pointer then ASCII */
/* letters are left as they are, and other code points are */
/* treated as if their uppercase flags were zero. The return */
/* value can be any of the punycode_status values defined above */
/* except punycode_bad_input; if not punycode_success, then */
/* output_size and output might contain garbage. */
enum punycode_status punycode_decode(punycode_uint input_length,
const char input[],
punycode_uint* output_length,
punycode_uint output[],
unsigned char case_flags[]);
/* punycode_decode() converts Punycode to Unicode. The input is */
/* represented as an array of ASCII code points, and the output */
/* will be represented as an array of Unicode code points. The */
/* input_length is the number of code points in the input. The */
/* output_length is an in/out argument: the caller passes in */
/* the maximum number of code points that it can receive, and */
/* on successful return it will contain the actual number of */
/* code points output. The case_flags array needs room for at */
/* least output_length values, or it can be a null pointer if the */
/* case information is not needed. A nonzero flag suggests that */
/* the corresponding Unicode character be forced to uppercase */
/* by the caller (if possible), while zero suggests that it be */
/* forced to lowercase (if possible). ASCII code points are */
/* output already in the proper case, but their flags will be set */
/* appropriately so that applying the flags would be harmless. */
/* The return value can be any of the punycode_status values */
/* defined above; if not punycode_success, then output_length, */
/* output, and case_flags might contain garbage. On success, the */
/* decoder will never need to write an output_length greater than */
/* input_length, because of how the encoding is defined. */
#ifdef __cplusplus
}
#endif /* __cplusplus */

Просмотреть файл

@ -33,6 +33,11 @@ function run_test() {
newURI = newURI.mutate().setSpec("http://example.com/foo").finalize();
Assert.equal(newURI.asciiHost, "example.com");
// Test an intuitively weird character that is not actually on the
// forbidden domain code point list in the WHATWG URL Standard.
newURI = newURI.mutate().setSpec("http://example.com%3bfoo").finalize();
Assert.equal(newURI.asciiHost, "example.com;foo");
// Characters that are invalid in the host
Assert.throws(
() => {
@ -48,13 +53,6 @@ function run_test() {
/NS_ERROR_MALFORMED_URI/,
"bad escaped character"
);
Assert.throws(
() => {
newURI = newURI.mutate().setSpec("http://example.com%3bfoo").finalize();
},
/NS_ERROR_MALFORMED_URI/,
"bad escaped character"
);
Assert.throws(
() => {
newURI = newURI.mutate().setSpec("http://example.com%3a80").finalize();

Просмотреть файл

@ -7,17 +7,15 @@
var idnService;
function expected_pass(inputIDN) {
var isASCII = {};
var displayIDN = idnService.convertToDisplayIDN(inputIDN, isASCII);
var displayIDN = idnService.convertToDisplayIDN(inputIDN);
Assert.equal(displayIDN, inputIDN);
}
function expected_fail(inputIDN) {
var isASCII = {};
var displayIDN = "";
try {
displayIDN = idnService.convertToDisplayIDN(inputIDN, isASCII);
displayIDN = idnService.convertToDisplayIDN(inputIDN);
} catch (e) {}
Assert.notEqual(displayIDN, inputIDN);

Просмотреть файл

@ -7,17 +7,15 @@
var idnService;
function expected_pass(inputIDN) {
var isASCII = {};
var displayIDN = idnService.convertToDisplayIDN(inputIDN, isASCII);
var displayIDN = idnService.convertToDisplayIDN(inputIDN);
Assert.equal(displayIDN, inputIDN);
}
function expected_fail(inputIDN) {
var isASCII = {};
var displayIDN = "";
try {
displayIDN = idnService.convertToDisplayIDN(inputIDN, isASCII);
displayIDN = idnService.convertToDisplayIDN(inputIDN);
} catch (e) {}
Assert.notEqual(displayIDN, inputIDN);
@ -38,9 +36,9 @@ function run_test() {
// unassigned code point
expected_fail("foo\u3040bar.com");
// unassigned code point in punycode. Should *pass* because the URL will not
// be converted to Unicode
expected_pass("xn--foobar-533e.com");
// unassigned code point in punycode. Should *fail* because Punycode
// is decoded and checked.
expected_fail("xn--foobar-533e.com");
// code point assigned since Unicode 3.0
// XXX This test will unexpectedly pass when we update to IDNAbis

Просмотреть файл

@ -5,6 +5,7 @@ function run_test() {
Ci.nsIIDNService
);
var isASCII = {};
Assert.equal(idnService.convertToDisplayIDN("xn--", isASCII), "xn--");
Assert.throws(() => {
idnService.convertToDisplayIDN("xn--");
}, /MALFORMED/);
}

Просмотреть файл

@ -39,7 +39,7 @@ add_setup(async function setup() {
});
// Test if IP hint addresses can be accessed as regular A/AAAA records.
add_task(async function testStoreIPHint() {
add_task(async function testStoreiphint() {
trrServer = new TRRServer();
registerCleanupFunction(async () => {
await trrServer.stop();
@ -52,16 +52,16 @@ add_task(async function testStoreIPHint() {
`https://foo.example.com:${trrServer.port()}/dns-query`
);
await trrServer.registerDoHAnswers("test.IPHint.com", "HTTPS", {
await trrServer.registerDoHAnswers("test.iphint.com", "HTTPS", {
answers: [
{
name: "test.IPHint.com",
name: "test.iphint.com",
ttl: 999,
type: "HTTPS",
flush: false,
data: {
priority: 1,
name: "test.IPHint.com",
name: "test.iphint.com",
values: [
{ key: "alpn", value: ["h2", "h3"] },
{ key: "port", value: 8888 },
@ -73,14 +73,14 @@ add_task(async function testStoreIPHint() {
],
});
let { inRecord } = await new TRRDNSListener("test.IPHint.com", {
let { inRecord } = await new TRRDNSListener("test.iphint.com", {
type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC,
});
let answer = inRecord.QueryInterface(Ci.nsIDNSHTTPSSVCRecord).records;
Assert.equal(inRecord.QueryInterface(Ci.nsIDNSHTTPSSVCRecord).ttl, 999);
Assert.equal(answer[0].priority, 1);
Assert.equal(answer[0].name, "test.IPHint.com");
Assert.equal(answer[0].name, "test.iphint.com");
Assert.equal(answer[0].values.length, 4);
Assert.deepEqual(
answer[0].values[0].QueryInterface(Ci.nsISVCParamAlpn).alpn,
@ -133,7 +133,7 @@ add_task(async function testStoreIPHint() {
Assert.equal(inRecord.ttl, 999);
}
await verifyAnswer("test.IPHint.com", Ci.nsIDNSService.RESOLVE_IP_HINT, [
await verifyAnswer("test.iphint.com", Ci.nsIDNSService.RESOLVE_IP_HINT, [
"1.2.3.4",
"5.6.7.8",
"::1",
@ -141,29 +141,29 @@ add_task(async function testStoreIPHint() {
]);
await verifyAnswer(
"test.IPHint.com",
"test.iphint.com",
Ci.nsIDNSService.RESOLVE_IP_HINT | Ci.nsIDNSService.RESOLVE_DISABLE_IPV4,
["::1", "fe80::794f:6d2c:3d5e:7836"]
);
await verifyAnswer(
"test.IPHint.com",
"test.iphint.com",
Ci.nsIDNSService.RESOLVE_IP_HINT | Ci.nsIDNSService.RESOLVE_DISABLE_IPV6,
["1.2.3.4", "5.6.7.8"]
);
info("checking that IPv6 hints are ignored when disableIPv6 is true");
Services.prefs.setBoolPref("network.dns.disableIPv6", true);
await trrServer.registerDoHAnswers("testv6.IPHint.com", "HTTPS", {
await trrServer.registerDoHAnswers("testv6.iphint.com", "HTTPS", {
answers: [
{
name: "testv6.IPHint.com",
name: "testv6.iphint.com",
ttl: 999,
type: "HTTPS",
flush: false,
data: {
priority: 1,
name: "testv6.IPHint.com",
name: "testv6.iphint.com",
values: [
{ key: "alpn", value: ["h2", "h3"] },
{ key: "port", value: 8888 },
@ -175,18 +175,18 @@ add_task(async function testStoreIPHint() {
],
});
({ inRecord } = await new TRRDNSListener("testv6.IPHint.com", {
({ inRecord } = await new TRRDNSListener("testv6.iphint.com", {
type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC,
}));
Services.prefs.setBoolPref("network.dns.disableIPv6", false);
await verifyAnswer("testv6.IPHint.com", Ci.nsIDNSService.RESOLVE_IP_HINT, [
await verifyAnswer("testv6.iphint.com", Ci.nsIDNSService.RESOLVE_IP_HINT, [
"1.2.3.4",
"5.6.7.8",
]);
await verifyAnswer(
"testv6.IPHint.com",
"testv6.iphint.com",
Ci.nsIDNSService.RESOLVE_IP_HINT | Ci.nsIDNSService.RESOLVE_DISABLE_IPV6,
["1.2.3.4", "5.6.7.8"]
);
@ -213,7 +213,7 @@ function channelOpenPromise(chan, flags) {
}
// Test if we can connect to the server with the IP hint address.
add_task(async function testConnectionWithIPHint() {
add_task(async function testConnectionWithiphint() {
Services.dns.clearCache(true);
Services.prefs.setIntPref("network.trr.mode", 3);
Services.prefs.setCharPref(
@ -254,7 +254,7 @@ add_task(async function testConnectionWithIPHint() {
// Test the case that we failed to use IP Hint address because DNS cache
// is bypassed.
add_task(async function testIPHintWithFreshDNS() {
add_task(async function testiphintWithFreshDNS() {
trrServer = new TRRServer();
await trrServer.start();
Services.prefs.setIntPref("network.trr.mode", 3);

Просмотреть файл

@ -142,26 +142,21 @@ function run_test() {
try {
result = idnService.convertToDisplayIDN(URL, isASCII);
} catch (e) {
result = ".com";
continue;
}
// If the punycode URL is equivalent to \ufffd.com (i.e. the
// blacklisted character has been replaced by a unicode
// REPLACEMENT CHARACTER, skip the test
if (result != "xn--zn7c.com") {
if (punycodeURL.substr(0, 4) == "xn--") {
// test convertToDisplayIDN with a Unicode URL and with a
// Punycode URL if we have one
equal(escape(result), escape(punycodeURL));
if (punycodeURL.substr(0, 4) == "xn--") {
// test convertToDisplayIDN with a Unicode URL and with a
// Punycode URL if we have one
equal(escape(result), escape(punycodeURL));
result = idnService.convertToDisplayIDN(punycodeURL, isASCII);
equal(escape(result), escape(punycodeURL));
} else {
// The "punycode" URL isn't punycode. This happens in testcases
// where the Unicode URL has become normalized to an ASCII URL,
// so, even though expectedUnicode is true, the expected result
// is equal to punycodeURL
equal(escape(result), escape(punycodeURL));
}
result = idnService.convertToDisplayIDN(punycodeURL, isASCII);
equal(escape(result), escape(punycodeURL));
} else {
// The "punycode" URL isn't punycode. This happens in testcases
// where the Unicode URL has become normalized to an ASCII URL,
// so, even though expectedUnicode is true, the expected result
// is equal to punycodeURL
equal(escape(result), escape(punycodeURL));
}
}
pbi.setCharPref("network.IDN.restriction_profile", oldProfile);

Просмотреть файл

@ -767,7 +767,7 @@ let testCases = [
// Georgian Capital Letter(U+10BD)
["xn--1nd.com", "\u10bd.com", kInvalid],
// 3rd and 4th characters are '-'.
["xn-----8kci4dhsd", "\u0440\u0443--\u0430\u0432\u0442\u043e", kInvalid],
["xn-----8kci4dhsd", "\u0440\u0443--\u0430\u0432\u0442\u043e", kInvalid, "DISABLED"],
// Leading combining mark
["xn--72b.com", "\u093e.com", kInvalid],
// BiDi check per IDNA 2008/UTS 46
@ -1023,8 +1023,10 @@ function checkEquals(a, b, message, expectedFail) {
add_task(async function test_chrome_spoofs() {
for (let test of testCases) {
let isAscii = {};
let result = idnService.convertToDisplayIDN(test[0], isAscii);
let result = "\uFFFD";
try {
result = idnService.convertToDisplayIDN(test[0]);
} catch (e) {}
let expectedFail = test.length == 4 && test[3] == "DISABLED";
if (test[2] == kSafe) {
checkEquals(
@ -1043,8 +1045,8 @@ add_task(async function test_chrome_spoofs() {
} else if (test[2] == kInvalid) {
checkEquals(
result,
test[0],
`kInvalid label ${test[0]} should stay the same`,
"\uFFFD",
`kInvalid label ${test[0]} should throw`,
expectedFail
);
}

Просмотреть файл

@ -171,7 +171,8 @@ const testcases = [
["\uD840\uDC00\uD840\uDC01\uD840\uDC02", "xn--j50icd", false, true, true],
// Same with a lone high surrogate at the end
["\uD840\uDC00\uD840\uDC01\uD840", "xn--zn7c0336bda", false, false, false],
// Throws due to unpaired surrogate
// ["\uD840\uDC00\uD840\uDC01\uD840", "xn--zn7c0336bda", false, false, false],
// Latin text and Bengali digits
["super", "xn--super-k2l", false, false, true],
@ -186,7 +187,8 @@ const testcases = [
["5াব", "xn--5-h3d7c", false, true, true],
// Mixed numbering systems
["٢٠۰٠", "xn--8hbae38c", false, false, false],
// Throws due to bidi rule violation
// ["٢٠۰٠", "xn--8hbae38c", false, false, false],
// Traditional Chinese
["萬城", "xn--uis754h", false, true, true],
@ -346,20 +348,21 @@ const testcases = [
["ascii.䕮䕵䕶䕱", "ascii.xn--google", false, true, true],
["中国123.䕮䕵䕶䕱", "xn--123-u68dy61b.xn--google", false, true, true],
["䕮䕵䕶䕱.中国123", "xn--google.xn--123-u68dy61b", false, true, true],
[
"xn--accountlogin.䕮䕵䕶䕱",
"xn--accountlogin.xn--google",
false,
true,
true,
],
[
"䕮䕵䕶䕱.xn--accountlogin",
"xn--google.xn--accountlogin",
false,
true,
true,
],
// Throw due to bogus Punycode
// [
// "xn--accountlogin.䕮䕵䕶䕱",
// "xn--accountlogin.xn--google",
// false,
// true,
// true,
// ],
// [
// "䕮䕵䕶䕱.xn--accountlogin",
// "xn--google.xn--accountlogin",
// false,
// true,
// true,
// ],
// Arabic diacritic not allowed in Latin text (bug 1370497)
["goo\u0650gle", "xn--google-yri", false, false, false],
@ -399,11 +402,10 @@ function run_test() {
var URL = test[0] + ".com";
var punycodeURL = test[1] + ".com";
var expectedUnicode = test[2 + i];
var isASCII = {};
var result;
try {
result = idnService.convertToDisplayIDN(URL, isASCII);
result = idnService.convertToDisplayIDN(URL);
} catch (e) {
result = ".com";
}
@ -418,7 +420,7 @@ function run_test() {
expectedUnicode ? escape(URL) : escape(punycodeURL)
);
result = idnService.convertToDisplayIDN(punycodeURL, isASCII);
result = idnService.convertToDisplayIDN(punycodeURL);
Assert.equal(
escape(result),
expectedUnicode ? escape(URL) : escape(punycodeURL)

Просмотреть файл

@ -28,12 +28,8 @@ add_task(async function test_simple() {
});
add_task(async function test_extra_blocked() {
let isAscii = {};
equal(idnService.convertToDisplayIDN("xn--gou-2lb.ro", isAscii), "goșu.ro");
equal(idnService.convertToDisplayIDN("xn--gou-2lb.ro"), "goșu.ro");
Services.prefs.setStringPref("network.IDN.extra_blocked_chars", "ș");
equal(
idnService.convertToDisplayIDN("xn--gou-2lb.ro", isAscii),
"xn--gou-2lb.ro"
);
equal(idnService.convertToDisplayIDN("xn--gou-2lb.ro"), "xn--gou-2lb.ro");
Services.prefs.clearUserPref("network.IDN.extra_blocked_chars");
});

Просмотреть файл

@ -135,7 +135,7 @@ add_task(async function test_trr_casing() {
});
await new TRRDNSListener("a.test.com", { expectedAnswer: "8.8.8.8" });
await trrServer.registerDoHAnswers("CAPITAL.COM", "A", {
await trrServer.registerDoHAnswers("capital.com", "A", {
answers: [
{
name: "capital.com",

Просмотреть файл

@ -680,6 +680,11 @@ who = "Mike Hommey <mh+mozilla@glandium.org>"
criteria = "safe-to-run"
delta = "1.2.0 -> 1.2.3"
[[audits.arraystring]]
who = "Henri Sivonen <hsivonen@hsivonen.fi>"
criteria = "safe-to-deploy"
version = "0.3.0"
[[audits.ash]]
who = "Jim Blandy <jimb@red-bean.com>"
criteria = "safe-to-deploy"

Просмотреть файл

@ -1,6 +1,7 @@
[a-element-origin-xhtml.xhtml]
[Parsing origin: <wss://!"$&'()*+,-.;=_`{}~/> against <about:blank>]
expected: FAIL
max-asserts: 3
[Parsing origin: <http://!"$&'()*+,-.;=_`{}~/> against <about:blank>]
expected: FAIL
[Parsing origin: <wss://!"$&'()*+,-.;=_`{}~/> against <about:blank>]
expected: FAIL

Просмотреть файл

@ -25,12 +25,6 @@
[url-setters-a-area.window.html?include=mailto]
[url-setters-a-area.window.html?exclude=(file|javascript|mailto)]
[<a>: Setting <foo://somehost/some/path>.pathname = '' Non-special URLs can have their paths erased]
expected: FAIL
[<area>: Setting <foo://somehost/some/path>.pathname = '' Non-special URLs can have their paths erased]
expected: FAIL
[<a>: Setting <foo:///some/path>.pathname = '' Non-special URLs with an empty host can have their paths erased]
expected: FAIL

Просмотреть файл

@ -426,9 +426,6 @@
[URL: Setting <non-spec:/.//p>.hostname = '']
expected: FAIL
[URL: Setting <foo://somehost/some/path>.pathname = '' Non-special URLs can have their paths erased]
expected: FAIL
[URL: Setting <foo:///some/path>.pathname = '' Non-special URLs with an empty host can have their paths erased]
expected: FAIL
@ -486,9 +483,6 @@
[URL: Setting <non-spec:/.//p>.hostname = '']
expected: FAIL
[URL: Setting <foo://somehost/some/path>.pathname = '' Non-special URLs can have their paths erased]
expected: FAIL
[URL: Setting <foo:///some/path>.pathname = '' Non-special URLs with an empty host can have their paths erased]
expected: FAIL

1
third_party/rust/arraystring/.cargo-checksum.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1 @@
{"files":{"Cargo.toml":"0876e89626cbad81a5dc14dc570f0a8143b423d9d5887fd7bfd7c84a04cdb292","README.md":"6e73b79a6025d33f02003470f0e8ff4f195c31fd5f81e08076339b0a796ef831","benches/string.rs":"5b232e1d9130e0d71548a27e1db67bb17a001935f432a9d3a74c6c639fe9cfb5","license/APACHE":"d7b3ad3a0671f9ba97f0ac7890ed55c82ab7e3d2abbee310ea75d6de3d05a6ca","license/MIT":"f7f8c47f3fc949cf8d037fcb876a3031346ff5e363d3c0eead23540547e11401","src/arraystring.rs":"d7d1029ec2f6a3ec7841a7123d88440bc0713b24ab137c14acc521e34ee7e9fe","src/drain.rs":"f6f4127cc15cf52a454b84a5dfd55433b1b922b0d11e59fe995effebaa4529d9","src/error.rs":"2e2738a6c978febea778c0df0007c701afa290cde0bbfc148aeab75d605ee75b","src/generic.rs":"694df6c6b06d3d9649b77c5234772801ed1f79d00062898cf0831b2b861fd2a8","src/implementations.rs":"496b8097e5f938a3fa9d19bc4061bac30519d51aa15e7653c9507804d1db83f8","src/integration.rs":"2254c261f55e40f2c2010227386541b5fac78a1e887dabc63f8b48b20d8ebfd9","src/lib.rs":"394fd8b3249f85ed02b9842fa04a1ed0c3694c283071479ca055e70bf659637a","src/utils.rs":"e1f5b12b1f5406f0935aec894b512fc75454f8bcd518a4980f924477815d5533","tests/string_parity.rs":"b8c199e7d255a40cc1c001d0a611b4e1f0e1111a95ba47a807c0f475d6e29726"},"package":"4d517c467117e1d8ca795bc8cc90857ff7f79790cca0e26f6e9462694ece0185"}

77
third_party/rust/arraystring/Cargo.toml поставляемый Normal file
Просмотреть файл

@ -0,0 +1,77 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g. crates.io) dependencies
#
# If you believe there's an error in this file please file an
# issue against the rust-lang/cargo repository. If you're
# editing this file be aware that the upstream Cargo.toml
# will likely look very different (and much more reasonable)
[package]
edition = "2018"
name = "arraystring"
version = "0.3.0"
authors = ["Paulo Cabral Sanz <paulosanz@poli.ufrj.br>"]
description = "Fixed capacity stack based generic string"
readme = "README.md"
keywords = ["string", "generic", "array", "stack", "no_std"]
categories = ["data-structures"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/paulocsanz/arraystring"
[package.metadata.docs.rs]
features = ["logs", "serde-traits", "std", "diesel-traits"]
rustdoc-args = ["--cfg", "docs_rs_workaraound"]
[lib]
name = "arraystring"
crate_type = ["lib"]
[[bench]]
name = "string"
harness = false
[dependencies.diesel]
version = "1"
optional = true
[dependencies.log]
version = "0.4"
optional = true
[dependencies.serde]
version = "1"
optional = true
[dependencies.typenum]
version = "1"
[dev-dependencies.criterion]
version = "0.2"
[dev-dependencies.diesel]
version = "1.3"
features = ["sqlite", "postgres", "mysql"]
[dev-dependencies.env_logger]
version = "0.5"
[dev-dependencies.inlinable_string]
version = "0.1"
[dev-dependencies.serde]
version = "1.0"
features = ["derive"]
[dev-dependencies.serde_json]
version = "1.0"
[dev-dependencies.smallstring]
version = "0.1"
[features]
default = ["std"]
diesel-traits = ["diesel"]
logs = ["log"]
serde-traits = ["serde"]
std = []

112
third_party/rust/arraystring/README.md поставляемый Normal file
Просмотреть файл

@ -0,0 +1,112 @@
# ArrayString
Fixed capacity stack based generic string
Since rust doesn't have constant generics yet `typenum` is used to allow for generic arrays (U1 to U255)
Can't outgrow initial capacity (defined at compile time), always occupies `capacity` `+ 1` bytes of memory
*Doesn't allocate memory on the heap and never panics in release (all panic branches are stripped at compile time - except `Index`/`IndexMut` traits, since they are supposed to)*
* [Documentation](https://docs.rs/arraystring/0.3.0/arraystring)
## Why
Data is generally bounded, you don't want a phone number with 30 characters, nor a username with 100. You probably don't even support it in your database.
Why pay the cost of heap allocations of strings with unlimited capacity if you have limited boundaries?
Stack based strings are generally faster to create, clone and append to than heap based strings (custom allocators and thread-locals may help with heap based ones).
But that becomes less true as you increase the array size, `CacheString` occuppies a full cache line, 255 bytes is the maximum we accept - `MaxString` (bigger will just wrap) and it's probably already slower than heap based strings of that size (like in `std::string::String`)
There are other stack based strings out there, they generally can have "unlimited" capacity (heap allocate), but the stack based size is defined by the library implementor, we go through a different route by implementing a string based in a generic array.
Array based strings always occupies the full space in memory, so they may use more memory (in the stack) than dynamic strings.
## Features
**default:** `std`
- `std` enabled by default, enables `std` compatibility - `impl Error` trait for errors (remove it to be `#[no_std]` compatible)
- `serde-traits` enables serde traits integration (`Serialize`/`Deserialize`)
Opperates like `String`, but truncates it if it's bigger than capacity
- `diesel-traits` enables diesel traits integration (`Insertable`/`Queryable`)
Opperates like `String`, but truncates it if it's bigger than capacity
- `logs` enables internal logging
You will probably only need this if you are debugging this library
## Examples
```rust
use arraystring::{Error, ArrayString, typenum::U5, typenum::U20};
type Username = ArrayString<U20>;
type Role = ArrayString<U5>;
#[derive(Debug)]
pub struct User {
pub username: Username,
pub role: Role,
}
fn main() -> Result<(), Error> {
let user = User {
username: Username::try_from_str("user")?,
role: Role::try_from_str("admin")?
};
println!("{:?}", user);
Ok(())
}
```
## Comparisons
*These benchmarks ran while I streamed video and used my computer (with* **non-disclosed specs**) *as usual, so don't take the actual times too seriously, just focus on the comparison*
```my_custom_benchmark
small-string (23 bytes) clone 4.837 ns
small-string (23 bytes) try_from_str 14.777 ns
small-string (23 bytes) from_str_truncate 11.360 ns
small-string (23 bytes) from_str_unchecked 11.291 ns
small-string (23 bytes) try_push_str 1.162 ns
small-string (23 bytes) push_str 3.490 ns
small-string (23 bytes) push_str_unchecked 1.098 ns
-------------------------------------------------------------
cache-string (63 bytes) clone 10.170 ns
cache-string (63 bytes) try_from_str 25.579 ns
cache-string (63 bytes) from_str_truncate 16.977 ns
cache-string (63 bytes) from_str_unchecked 17.201 ns
cache-string (63 bytes) try_push_str 1.160 ns
cache-string (63 bytes) push_str 3.486 ns
cache-string (63 bytes) push_str_unchecked 1.115 ns
-------------------------------------------------------------
max-string (255 bytes) clone 147.410 ns
max-string (255 bytes) try_from_str 157.340 ns
max-string (255 bytes) from_str_truncate 158.000 ns
max-string (255 bytes) from_str_unchecked 158.420 ns
max-string (255 bytes) try_push_str 1.167 ns
max-string (255 bytes) push_str 4.337 ns
max-string (255 bytes) push_str_unchecked 1.103 ns
-------------------------------------------------------------
string (19 bytes) clone 33.295 ns
string (19 bytes) from 32.512 ns
string (19 bytes) push str 28.128 ns
-------------------------------------------------------------
inlinable-string (30 bytes) clone 16.751 ns
inlinable-string (30 bytes) from_str 29.310 ns
inlinable-string (30 bytes) push_str 2.865 ns
-------------------------------------------------------------
smallstring crate (20 bytes) clone 60.988 ns
smallstring crate (20 bytes) from_str 50.233 ns
```
## Licenses
[MIT](master/license/MIT) and [Apache-2.0](master/license/APACHE)

279
third_party/rust/arraystring/benches/string.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,279 @@
use arraystring::{prelude::*, typenum::U20};
use inlinable_string::{InlinableString, StringExt};
use smallstring::SmallString as SmallVecString;
use criterion::{criterion_group, criterion_main, Criterion};
fn string_clone_benchmark(c: &mut Criterion) {
let string = String::from("abcdefghijklmnopqrst");
c.bench_function("string clone", move |b| b.iter(|| string.clone()));
}
fn string_from_benchmark(c: &mut Criterion) {
let string = String::from("uvwxyzaabbccddeeffgg");
c.bench_function("string from", move |b| {
b.iter(|| String::from(string.as_str()))
});
}
fn string_push_str_benchmark(c: &mut Criterion) {
let mut string = String::default();
c.bench_function("string push str", move |b| {
b.iter(|| {
string.push_str("0123456789123456789");
string.clear();
string.shrink_to_fit();
})
});
}
fn inlinable_clone_benchmark(c: &mut Criterion) {
let string = InlinableString::from("hcuahdaidshdaisuhda");
c.bench_function("inlinable clone", move |b| b.iter(|| string.clone()));
}
fn inlinable_from_benchmark(c: &mut Criterion) {
let string = "edauhefhiaw na na ";
c.bench_function("inlinable from", move |b| {
b.iter(|| InlinableString::from(string))
});
}
fn inlinable_push_str_benchmark(c: &mut Criterion) {
let mut string = InlinableString::default();
c.bench_function("inlinable push str", move |b| {
b.iter(|| {
string.push_str("ddauhifnaoe jaowijd");
string.clear();
string.shrink_to_fit();
})
});
}
fn smallvecstring_clone_benchmark(c: &mut Criterion) {
let string = SmallVecString::<<U20 as Capacity>::Array>::from("xhduibabicemlatdhue");
c.bench_function("smallvecstring clone", move |b| b.iter(|| string.clone()));
}
fn smallvecstring_from_benchmark(c: &mut Criterion) {
let string = "audshaisdhaisduo8";
c.bench_function("smallvecstring from", move |b| {
b.iter(|| SmallVecString::<<U20 as Capacity>::Array>::from(string))
});
}
fn small_clone_benchmark(c: &mut Criterion) {
let string = SmallString::from_str_truncate("hhiijjkkllmmneeeepqq");
c.bench_function("small clone", move |b| b.iter(|| string.clone()));
}
fn small_from_unchecked_benchmark(c: &mut Criterion) {
let string = "rrssttuuvvwwxxyyzza";
c.bench_function("small from unchecked", move |b| {
b.iter(|| unsafe { SmallString::from_str_unchecked(&string) })
});
}
fn small_from_truncate_benchmark(c: &mut Criterion) {
let string = "bbbcccdddeeefffgggh";
c.bench_function("small from truncate", move |b| {
b.iter(|| SmallString::from_str_truncate(&string))
});
}
fn small_try_from_benchmark(c: &mut Criterion) {
let string = "iiijjjkkklllmmmnnnoo";
c.bench_function("small try from", move |b| {
b.iter(|| SmallString::try_from_str(&string))
});
}
fn small_push_str_unchecked_benchmark(c: &mut Criterion) {
let mut string = SmallString::default();
c.bench_function("small push str unchecked", move |b| {
b.iter(|| unsafe {
string.push_str_unchecked("1413121110987654321");
string.clear();
})
});
}
fn small_push_str_benchmark(c: &mut Criterion) {
let mut string = SmallString::default();
c.bench_function("small push str truncate", move |b| {
b.iter(|| {
string.push_str("1413121110987654321");
string.clear();
})
});
}
fn small_try_push_str_benchmark(c: &mut Criterion) {
let mut string = SmallString::default();
c.bench_function("small try push str", move |b| {
b.iter(|| {
string.try_push_str("9897969594939291908").unwrap();
string.clear();
})
});
}
fn cache_clone_benchmark(c: &mut Criterion) {
let string = CacheString::from_str_truncate("opppqqqrrrssstttuuuv");
c.bench_function("cache clone", move |b| b.iter(|| string.clone()));
}
fn cache_from_unchecked_benchmark(c: &mut Criterion) {
let string = "wwwxxxyyyzzzaaaabbbb";
c.bench_function("cache from unchecked", move |b| {
b.iter(|| unsafe { CacheString::from_str_unchecked(&string) })
});
}
fn cache_from_truncate_benchmark(c: &mut Criterion) {
let string = "ccccddddeeeeffffggggh";
c.bench_function("cache from truncate", move |b| {
b.iter(|| CacheString::from_str_truncate(&string))
});
}
fn cache_try_from_benchmark(c: &mut Criterion) {
let string = "iiiijjjjkkkkllllmmmmn";
c.bench_function("cache try from", move |b| {
b.iter(|| CacheString::try_from_str(&string))
});
}
fn cache_push_str_unchecked_benchmark(c: &mut Criterion) {
let mut string = CacheString::default();
c.bench_function("cache push str unchecked", move |b| {
b.iter(|| unsafe {
string.push_str_unchecked("1413121110987654321");
string.clear();
})
});
}
fn cache_push_str_benchmark(c: &mut Criterion) {
let mut string = CacheString::default();
c.bench_function("cache push str truncate", move |b| {
b.iter(|| {
string.push_str("1413121110987654321");
string.clear();
})
});
}
fn cache_try_push_str_benchmark(c: &mut Criterion) {
let mut string = CacheString::default();
c.bench_function("cache try push str", move |b| {
b.iter(|| {
string.try_push_str("9897969594939291908").unwrap();
string.clear();
})
});
}
fn max_clone_benchmark(c: &mut Criterion) {
let string = MaxString::from_str_truncate("ooopppqqqrrrssstttuu");
c.bench_function("max clone", move |b| b.iter(|| string.clone()));
}
fn max_from_unchecked_benchmark(c: &mut Criterion) {
let string = "vvvvwwwwxxxxyyyzzzza";
c.bench_function("max from unchecked", move |b| {
b.iter(|| unsafe { MaxString::from_str_unchecked(&string) })
});
}
fn max_from_truncate_benchmark(c: &mut Criterion) {
let string = "bbbbccccddddeeeeffff";
c.bench_function("max from truncate", move |b| {
b.iter(|| MaxString::from_str_truncate(&string))
});
}
fn max_try_from_benchmark(c: &mut Criterion) {
let string = "gggghhhhiiiijjjjkkkk";
c.bench_function("max try from", move |b| {
b.iter(|| MaxString::try_from_str(&string).unwrap())
});
}
fn max_push_str_unchecked_benchmark(c: &mut Criterion) {
let mut string = MaxString::default();
c.bench_function("max push str unchecked", move |b| {
b.iter(|| unsafe {
string.push_str_unchecked("1413121110987654321");
string.clear();
})
});
}
fn max_push_str_benchmark(c: &mut Criterion) {
let mut string = MaxString::default();
c.bench_function("max push str truncate", move |b| {
b.iter(|| {
string.push_str("1413121110987654321");
string.clear();
})
});
}
fn max_try_push_str_benchmark(c: &mut Criterion) {
let mut string = MaxString::default();
c.bench_function("max try push str", move |b| {
b.iter(|| {
string.try_push_str("9897969594939291908").unwrap();
string.clear();
})
});
}
criterion_group!(
string,
string_clone_benchmark,
string_from_benchmark,
string_push_str_benchmark
);
criterion_group!(
inlinable,
inlinable_clone_benchmark,
inlinable_from_benchmark,
inlinable_push_str_benchmark
);
criterion_group!(
smallvecstring,
smallvecstring_clone_benchmark,
smallvecstring_from_benchmark,
);
criterion_group!(
small,
small_clone_benchmark,
small_try_from_benchmark,
small_from_unchecked_benchmark,
small_from_truncate_benchmark,
small_try_push_str_benchmark,
small_push_str_unchecked_benchmark,
small_push_str_benchmark,
);
criterion_group!(
cache,
cache_clone_benchmark,
cache_try_from_benchmark,
cache_from_unchecked_benchmark,
cache_from_truncate_benchmark,
cache_try_push_str_benchmark,
cache_push_str_unchecked_benchmark,
cache_push_str_benchmark,
);
criterion_group!(
max,
max_clone_benchmark,
max_try_from_benchmark,
max_from_unchecked_benchmark,
max_from_truncate_benchmark,
max_try_push_str_benchmark,
max_push_str_unchecked_benchmark,
max_push_str_benchmark,
);
criterion_main!(string, inlinable, smallvecstring, small, cache, max);

202
third_party/rust/arraystring/license/APACHE поставляемый Normal file
Просмотреть файл

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2018 Paulo Cabral Sanz
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

20
third_party/rust/arraystring/license/MIT поставляемый Normal file
Просмотреть файл

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2018 Paulo Cabral Sanz
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

1280
third_party/rust/arraystring/src/arraystring.rs поставляемый Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

105
third_party/rust/arraystring/src/drain.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,105 @@
//! Draining iterator for [`ArrayString`]
//!
//! [`ArrayString`]: ../struct.ArrayString.html
use crate::{prelude::*, utils::IntoLossy};
use core::fmt::{self, Debug, Formatter};
use core::{cmp::Ordering, hash::Hash, hash::Hasher, iter::FusedIterator};
/// A draining iterator for [`ArrayString`].
///
/// Created through [`drain`]
///
/// [`ArrayString`]: ../struct.ArrayString.html
/// [`drain`]: ../struct.ArrayString.html#method.drain
#[derive(Clone, Default)]
pub struct Drain<S: Capacity>(pub(crate) ArrayString<S>, pub(crate) u8);
impl<SIZE> Debug for Drain<SIZE>
where
SIZE: Capacity,
{
#[inline]
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.debug_tuple("Drain")
.field(&self.0)
.field(&self.1)
.finish()
}
}
impl<SIZE> PartialEq for Drain<SIZE>
where
SIZE: Capacity,
{
#[inline]
fn eq(&self, other: &Self) -> bool {
self.as_str().eq(other.as_str())
}
}
impl<SIZE: Capacity> Eq for Drain<SIZE> {}
impl<SIZE> Ord for Drain<SIZE>
where
SIZE: Capacity,
{
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
self.as_str().cmp(other.as_str())
}
}
impl<SIZE> PartialOrd for Drain<SIZE>
where
SIZE: Capacity,
{
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<SIZE> Hash for Drain<SIZE>
where
SIZE: Capacity,
{
#[inline]
fn hash<H: Hasher>(&self, hasher: &mut H) {
self.as_str().hash(hasher)
}
}
impl<SIZE: Capacity + Copy> Copy for Drain<SIZE> where SIZE::Array: Copy {}
impl<S: Capacity> Drain<S> {
/// Extracts string slice containing the remaining characters of `Drain`.
#[inline]
pub fn as_str(&self) -> &str {
unsafe { self.0.as_str().get_unchecked(self.1.into()..) }
}
}
impl<S: Capacity> Iterator for Drain<S> {
type Item = char;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.0
.as_str()
.get(self.1.into()..)
.and_then(|s| s.chars().next())
.map(|c| {
self.1 = self.1.saturating_add(c.len_utf8().into_lossy());
c
})
}
}
impl<S: Capacity> DoubleEndedIterator for Drain<S> {
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
self.0.pop()
}
}
impl<S: Capacity> FusedIterator for Drain<S> {}

199
third_party/rust/arraystring/src/error.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,199 @@
//! Contains all of this crate errors
use core::fmt::{self, Debug, Display, Formatter};
use core::{char::DecodeUtf16Error, hash::Hash, hash::Hasher, str::EncodeUtf16, str::Utf8Error};
#[cfg(feature = "logs")]
use log::trace;
/// Every error possible when using [`ArrayString`]
///
/// [`ArrayString`]: ../struct.ArrayString.html
#[derive(Copy, Clone)]
pub enum Error {
/// Conversion between available byte slice and UTF-8 failed (invalid data or invalid utf-8 character index)
Utf8,
/// Conversion between available `u16` slice and string failed
Utf16,
/// Out of boundaries access
OutOfBounds,
}
impl PartialEq for Error {
#[inline]
fn eq(&self, other: &Self) -> bool {
use self::Error::*;
match (self, other) {
(Utf8, Utf8) | (Utf16, Utf16) | (OutOfBounds, OutOfBounds) => true,
_ => false,
}
}
}
impl Eq for Error {}
impl Hash for Error {
#[inline]
fn hash<H: Hasher>(&self, hasher: &mut H) {
match self {
Error::Utf8 => "Utf8".hash(hasher),
Error::Utf16 => "Utf16".hash(hasher),
Error::OutOfBounds => "OutOfBounds".hash(hasher),
}
}
}
impl Display for Error {
#[inline]
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Error::Utf8 => write!(f, "Utf8"),
Error::Utf16 => write!(f, "Utf16"),
Error::OutOfBounds => write!(f, "OutOfBounds"),
}
}
}
impl Debug for Error {
#[inline]
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Error::Utf8 => write!(f, "Error::Utf8"),
Error::Utf16 => write!(f, "Error::Utf16"),
Error::OutOfBounds => write!(f, "Error::OutOfBounds"),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for Error {}
impl From<Utf8Error> for Error {
#[inline]
fn from(_: Utf8Error) -> Self {
Error::Utf8
}
}
impl From<DecodeUtf16Error> for Error {
#[inline]
fn from(_: DecodeUtf16Error) -> Self {
Error::Utf16
}
}
impl<'a> From<EncodeUtf16<'a>> for Error {
#[inline]
fn from(_: EncodeUtf16) -> Self {
Error::Utf16
}
}
/// Error caused by invalid UTF-8 data
#[derive(Copy, Clone, Default, PartialEq, Eq)]
pub struct Utf8;
impl Debug for Utf8 {
#[inline]
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "Utf8")
}
}
impl Display for Utf8 {
#[inline]
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "Utf8")
}
}
#[cfg(feature = "std")]
impl std::error::Error for Utf8 {}
impl From<Utf8Error> for Utf8 {
#[inline]
fn from(_: Utf8Error) -> Self {
Utf8
}
}
impl From<Utf8> for Error {
#[inline]
fn from(_: Utf8) -> Self {
trace!("From Utf8");
Error::Utf8
}
}
/// Error caused by invalid UTF-16 data
#[derive(Copy, Clone, Default, PartialEq, Eq)]
pub struct Utf16;
impl Debug for Utf16 {
#[inline]
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "Utf16")
}
}
#[cfg(feature = "std")]
impl std::error::Error for Utf16 {}
impl Display for Utf16 {
#[inline]
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "Utf16")
}
}
impl From<Utf16> for Error {
#[inline]
fn from(_: Utf16) -> Self {
trace!("From Utf16");
Error::Utf16
}
}
impl From<DecodeUtf16Error> for Utf16 {
#[inline]
fn from(_: DecodeUtf16Error) -> Self {
Utf16
}
}
impl<'a> From<EncodeUtf16<'a>> for Utf16 {
#[inline]
fn from(_: EncodeUtf16) -> Self {
Utf16
}
}
/// Error caused by out of bounds access to [`ArrayString`]
///
/// [`ArrayString`]: ../struct.ArrayString.html
#[derive(Copy, Clone, Default, PartialEq, Eq)]
pub struct OutOfBounds;
impl Debug for OutOfBounds {
#[inline]
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "OutOfBounds")
}
}
impl Display for OutOfBounds {
#[inline]
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "OutOfBounds")
}
}
#[cfg(feature = "std")]
impl std::error::Error for OutOfBounds {}
impl From<OutOfBounds> for Error {
#[inline]
fn from(_: OutOfBounds) -> Self {
trace!("From OutOfBounds");
Error::OutOfBounds
}
}

79
third_party/rust/arraystring/src/generic.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,79 @@
//! Implements actual generic array abstraction for all supported types from `typenum` (1 to 255)
use typenum::*;
macro_rules! impl_generic_array {
($($type: ty),*) => {
$(
impl private::Sealed for $type {}
impl Capacity for $type {
type Array = [u8; Self::USIZE];
}
impl private::Sealed for <$type as Capacity>::Array {}
impl ArraySlice for <$type as Capacity>::Array {
const CAPACITY: usize = <$type as Unsigned>::USIZE;
#[inline]
fn as_slice(&self) -> &[u8] {
self
}
#[inline]
unsafe fn as_mut_slice(&mut self) -> &mut [u8] {
self
}
#[inline]
fn zeroed() -> Self {
[0; Self::CAPACITY]
}
}
)*
}
}
/// Private module to hide access to sealed trait
mod private {
/// Trait impossible to be implemented outside of this crate, seals other traits
pub trait Sealed {}
}
/// Implements needed types for all types of arrays (bigger than 32 don't have the default traits)
#[doc(hidden)]
pub trait ArraySlice: private::Sealed {
/// Capacity represented by type
const CAPACITY: usize;
/// Returns slice of the entire array
fn as_slice(&self) -> &[u8];
/// Returns mutable slice of the entire array
unsafe fn as_mut_slice(&mut self) -> &mut [u8];
/// Returns array filled with zeroes
fn zeroed() -> Self;
}
/// Converts between `typenum` types and its corresponding array
#[doc(hidden)]
pub trait Capacity: Unsigned + private::Sealed {
/// Array with specified capacity
type Array: ArraySlice + Copy;
}
impl_generic_array!(
U1, U2, U3, U4, U5, U6, U7, U8, U9, U10, U11, U12, U13, U14, U15, U16, U17, U18, U19, U20, U21,
U22, U23, U24, U25, U26, U27, U28, U29, U30, U31, U32, U33, U34, U35, U36, U37, U38, U39, U40,
U41, U42, U43, U44, U45, U46, U47, U48, U49, U50, U51, U52, U53, U54, U55, U56, U57, U58, U59,
U60, U61, U62, U63, U64, U65, U66, U67, U68, U69, U70, U71, U72, U73, U74, U75, U76, U77, U78,
U79, U80, U81, U82, U83, U84, U85, U86, U87, U88, U89, U90, U91, U92, U93, U94, U95, U96, U97,
U98, U99, U100, U101, U102, U103, U104, U105, U106, U107, U108, U109, U110, U111, U112, U113,
U114, U115, U116, U117, U118, U119, U120, U121, U122, U123, U124, U125, U126, U127, U128, U129,
U130, U131, U132, U133, U134, U135, U136, U137, U138, U139, U140, U141, U142, U143, U144, U145,
U146, U147, U148, U149, U150, U151, U152, U153, U154, U155, U156, U157, U158, U159, U160, U161,
U162, U163, U164, U165, U166, U167, U168, U169, U170, U171, U172, U173, U174, U178, U179, U180,
U181, U182, U183, U184, U185, U186, U187, U188, U189, U190, U191, U192, U193, U194, U195, U196,
U197, U198, U199, U200, U201, U202, U203, U204, U205, U206, U207, U208, U209, U210, U211, U212,
U213, U214, U215, U216, U217, U218, U219, U220, U221, U222, U223, U224, U225, U226, U227, U228,
U229, U230, U231, U232, U233, U234, U235, U236, U237, U238, U239, U240, U241, U242, U243, U244,
U245, U246, U247, U248, U249, U250, U251, U252, U253, U254, U255
);

409
third_party/rust/arraystring/src/implementations.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,409 @@
//! Trait implementations for `ArrayString` (that aren't for integration)
use crate::{generic::ArraySlice, prelude::*};
use core::fmt::{self, Debug, Display, Formatter, Write};
use core::iter::FromIterator;
use core::ops::{Add, Deref, DerefMut, Index, IndexMut};
use core::ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive};
use core::str::{self, FromStr};
use core::{borrow::Borrow, borrow::BorrowMut, cmp::Ordering, hash::Hash, hash::Hasher};
impl<SIZE> Default for ArrayString<SIZE>
where
SIZE: Capacity,
{
#[inline]
fn default() -> Self {
Self {
array: SIZE::Array::zeroed(),
size: Default::default(),
}
}
}
impl<SIZE: Capacity + Copy> Copy for ArrayString<SIZE> where SIZE::Array: Copy {}
impl<SIZE> AsRef<str> for ArrayString<SIZE>
where
SIZE: Capacity,
{
#[inline]
fn as_ref(&self) -> &str {
unsafe { str::from_utf8_unchecked(self.as_ref()) }
}
}
impl<SIZE> AsMut<str> for ArrayString<SIZE>
where
SIZE: Capacity,
{
#[inline]
fn as_mut(&mut self) -> &mut str {
let len = self.size as usize;
let slice = unsafe { self.array.as_mut_slice().get_unchecked_mut(..len) };
unsafe { str::from_utf8_unchecked_mut(slice) }
}
}
impl<SIZE> AsRef<[u8]> for ArrayString<SIZE>
where
SIZE: Capacity,
{
#[inline]
fn as_ref(&self) -> &[u8] {
unsafe { self.array.as_slice().get_unchecked(..self.size.into()) }
}
}
impl<'a, SIZE> From<&'a str> for ArrayString<SIZE>
where
SIZE: Capacity,
{
#[inline]
fn from(s: &str) -> Self {
Self::from_str_truncate(s)
}
}
impl<SIZE> FromStr for ArrayString<SIZE>
where
SIZE: Capacity,
{
type Err = OutOfBounds;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::try_from_str(s)
}
}
impl<SIZE> Debug for ArrayString<SIZE>
where
SIZE: Capacity,
{
#[inline]
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.debug_struct("ArrayString")
.field("array", &self.as_str())
.field("size", &self.size)
.finish()
}
}
impl<'a, 'b, SIZE> PartialEq<str> for ArrayString<SIZE>
where
SIZE: Capacity,
{
#[inline]
fn eq(&self, other: &str) -> bool {
self.as_str().eq(other)
}
}
impl<SIZE> Borrow<str> for ArrayString<SIZE>
where
SIZE: Capacity,
{
#[inline]
fn borrow(&self) -> &str {
self.as_str()
}
}
impl<SIZE> BorrowMut<str> for ArrayString<SIZE>
where
SIZE: Capacity,
{
#[inline]
fn borrow_mut(&mut self) -> &mut str {
self.as_mut_str()
}
}
impl<SIZE> Hash for ArrayString<SIZE>
where
SIZE: Capacity,
{
#[inline]
fn hash<H: Hasher>(&self, hasher: &mut H) {
self.as_str().hash(hasher);
}
}
impl<SIZE> PartialEq for ArrayString<SIZE>
where
SIZE: Capacity,
{
#[inline]
fn eq(&self, other: &Self) -> bool {
self.as_str().eq(other.as_str())
}
}
impl<SIZE: Capacity> Eq for ArrayString<SIZE> {}
impl<SIZE> Ord for ArrayString<SIZE>
where
SIZE: Capacity,
{
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
self.as_str().cmp(other.as_str())
}
}
impl<SIZE> PartialOrd for ArrayString<SIZE>
where
SIZE: Capacity,
{
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<'a, SIZE> Add<&'a str> for ArrayString<SIZE>
where
SIZE: Capacity,
{
type Output = Self;
#[inline]
fn add(self, other: &str) -> Self::Output {
let mut out = unsafe { Self::from_str_unchecked(self) };
out.push_str(other);
out
}
}
impl<SIZE> Write for ArrayString<SIZE>
where
SIZE: Capacity,
{
#[inline]
fn write_str(&mut self, slice: &str) -> fmt::Result {
self.try_push_str(slice).map_err(|_| fmt::Error)
}
}
impl<SIZE> Display for ArrayString<SIZE>
where
SIZE: Capacity,
{
#[inline]
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl<SIZE> Deref for ArrayString<SIZE>
where
SIZE: Capacity,
{
type Target = str;
#[inline]
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}
impl<SIZE> DerefMut for ArrayString<SIZE>
where
SIZE: Capacity,
{
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
self.as_mut()
}
}
impl<SIZE> FromIterator<char> for ArrayString<SIZE>
where
SIZE: Capacity,
{
fn from_iter<I: IntoIterator<Item = char>>(iter: I) -> Self {
Self::from_chars(iter)
}
}
impl<'a, SIZE> FromIterator<&'a str> for ArrayString<SIZE>
where
SIZE: Capacity,
{
fn from_iter<I: IntoIterator<Item = &'a str>>(iter: I) -> Self {
Self::from_iterator(iter)
}
}
impl<SIZE> Extend<char> for ArrayString<SIZE>
where
SIZE: Capacity,
{
fn extend<I: IntoIterator<Item = char>>(&mut self, iterable: I) {
self.push_str(Self::from_chars(iterable))
}
}
impl<'a, SIZE> Extend<&'a char> for ArrayString<SIZE>
where
SIZE: Capacity,
{
fn extend<I: IntoIterator<Item = &'a char>>(&mut self, iter: I) {
self.extend(iter.into_iter().cloned());
}
}
impl<'a, SIZE> Extend<&'a str> for ArrayString<SIZE>
where
SIZE: Capacity,
{
fn extend<I: IntoIterator<Item = &'a str>>(&mut self, iterable: I) {
self.push_str(Self::from_iterator(iterable))
}
}
impl<SIZE> IndexMut<RangeFrom<u8>> for ArrayString<SIZE>
where
SIZE: Capacity,
{
#[inline]
fn index_mut(&mut self, index: RangeFrom<u8>) -> &mut str {
let start = index.start as usize;
self.as_mut_str().index_mut(RangeFrom { start })
}
}
impl<SIZE> IndexMut<RangeTo<u8>> for ArrayString<SIZE>
where
SIZE: Capacity,
{
#[inline]
fn index_mut(&mut self, index: RangeTo<u8>) -> &mut str {
let end = index.end as usize;
self.as_mut_str().index_mut(RangeTo { end })
}
}
impl<SIZE> IndexMut<RangeFull> for ArrayString<SIZE>
where
SIZE: Capacity,
{
#[inline]
fn index_mut(&mut self, index: RangeFull) -> &mut str {
self.as_mut_str().index_mut(index)
}
}
impl<SIZE> IndexMut<Range<u8>> for ArrayString<SIZE>
where
SIZE: Capacity,
{
#[inline]
fn index_mut(&mut self, index: Range<u8>) -> &mut str {
let (start, end) = (index.start as usize, index.end as usize);
let range = Range { start, end };
self.as_mut_str().index_mut(range)
}
}
impl<SIZE> IndexMut<RangeToInclusive<u8>> for ArrayString<SIZE>
where
SIZE: Capacity,
{
#[inline]
fn index_mut(&mut self, index: RangeToInclusive<u8>) -> &mut str {
let end = index.end as usize;
let range = RangeToInclusive { end };
self.as_mut_str().index_mut(range)
}
}
impl<SIZE> IndexMut<RangeInclusive<u8>> for ArrayString<SIZE>
where
SIZE: Capacity,
{
#[inline]
fn index_mut(&mut self, index: RangeInclusive<u8>) -> &mut str {
let (start, end) = (*index.start() as usize, *index.end() as usize);
let range = RangeInclusive::new(start, end);
self.as_mut_str().index_mut(range)
}
}
impl<SIZE> Index<RangeFrom<u8>> for ArrayString<SIZE>
where
SIZE: Capacity,
{
type Output = str;
#[inline]
fn index(&self, index: RangeFrom<u8>) -> &Self::Output {
let start = index.start as usize;
self.as_str().index(RangeFrom { start })
}
}
impl<SIZE> Index<RangeTo<u8>> for ArrayString<SIZE>
where
SIZE: Capacity,
{
type Output = str;
#[inline]
fn index(&self, index: RangeTo<u8>) -> &Self::Output {
let end = index.end as usize;
self.as_str().index(RangeTo { end })
}
}
impl<SIZE> Index<RangeFull> for ArrayString<SIZE>
where
SIZE: Capacity,
{
type Output = str;
#[inline]
fn index(&self, index: RangeFull) -> &Self::Output {
self.as_str().index(index)
}
}
impl<SIZE> Index<Range<u8>> for ArrayString<SIZE>
where
SIZE: Capacity,
{
type Output = str;
#[inline]
fn index(&self, index: Range<u8>) -> &Self::Output {
let (start, end) = (index.start as usize, index.end as usize);
self.as_str().index(Range { start, end })
}
}
impl<SIZE> Index<RangeToInclusive<u8>> for ArrayString<SIZE>
where
SIZE: Capacity,
{
type Output = str;
#[inline]
fn index(&self, index: RangeToInclusive<u8>) -> &Self::Output {
let end = index.end as usize;
self.as_str().index(RangeToInclusive { end })
}
}
impl<SIZE> Index<RangeInclusive<u8>> for ArrayString<SIZE>
where
SIZE: Capacity,
{
type Output = str;
#[inline]
fn index(&self, index: RangeInclusive<u8>) -> &Self::Output {
let (start, end) = (*index.start() as usize, *index.end() as usize);
let range = RangeInclusive::new(start, end);
self.as_str().index(range)
}
}

497
third_party/rust/arraystring/src/integration.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,497 @@
//! Integrates `ArrayString` with other crates' traits
use crate::prelude::*;
#[cfg(all(feature = "diesel-traits", feature = "std"))]
use std::io::Write;
#[cfg(feature = "diesel-traits")]
use diesel::{expression::*, prelude::*, query_builder::*, row::Row, sql_types::*};
#[cfg(feature = "diesel-traits")]
use diesel::backend::Backend;
#[cfg(feature = "diesel-traits")]
use diesel::deserialize::{self, FromSql, FromSqlRow, Queryable};
#[cfg(all(feature = "diesel-traits", feature = "std"))]
use diesel::serialize::{self, Output, ToSql};
#[cfg(feature = "serde-traits")]
use serde::{de::Deserializer, ser::Serializer, Deserialize, Serialize};
#[cfg_attr(docs_rs_workaround, doc(cfg(feature = "serde-traits")))]
#[cfg(feature = "serde-traits")]
impl<SIZE> Serialize for ArrayString<SIZE>
where
SIZE: Capacity,
{
#[inline]
fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
Serialize::serialize(self.as_str(), ser)
}
}
#[cfg_attr(docs_rs_workaround, doc(cfg(feature = "serde-traits")))]
#[cfg(feature = "serde-traits")]
impl<'a, SIZE> Deserialize<'a> for ArrayString<SIZE>
where
SIZE: Capacity,
{
#[inline]
fn deserialize<D: Deserializer<'a>>(des: D) -> Result<Self, D::Error> {
<&str>::deserialize(des).map(Self::from_str_truncate)
}
}
#[cfg_attr(docs_rs_workaround, doc(cfg(feature = "diesel-traits")))]
#[cfg(feature = "diesel-traits")]
impl<SIZE: Capacity> Expression for ArrayString<SIZE> {
type SqlType = VarChar;
}
#[cfg_attr(docs_rs_workaround, doc(cfg(feature = "diesel-traits")))]
#[cfg(feature = "diesel-traits")]
impl<SIZE: Capacity, QS> SelectableExpression<QS> for ArrayString<SIZE> {}
#[cfg_attr(docs_rs_workaround, doc(cfg(feature = "diesel-traits")))]
#[cfg(feature = "diesel-traits")]
impl<SIZE: Capacity, QS> AppearsOnTable<QS> for ArrayString<SIZE> {}
#[cfg_attr(docs_rs_workaround, doc(cfg(feature = "diesel-traits")))]
#[cfg(feature = "diesel-traits")]
impl<SIZE: Capacity> NonAggregate for ArrayString<SIZE> {}
#[cfg_attr(docs_rs_workaround, doc(cfg(feature = "diesel-traits")))]
#[cfg(feature = "diesel-traits")]
impl<SIZE, DB> QueryFragment<DB> for ArrayString<SIZE>
where
SIZE: Capacity,
DB: Backend + HasSqlType<VarChar>,
{
#[inline]
fn walk_ast(&self, mut pass: AstPass<DB>) -> QueryResult<()> {
pass.push_bind_param::<Varchar, _>(&self.as_str())?;
Ok(())
}
}
#[cfg_attr(docs_rs_workaround, doc(cfg(feature = "diesel-traits")))]
#[cfg(feature = "diesel-traits")]
impl<SIZE, ST, DB> FromSql<ST, DB> for ArrayString<SIZE>
where
SIZE: Capacity,
DB: Backend,
*const str: FromSql<ST, DB>,
{
#[inline]
fn from_sql(bytes: Option<&DB::RawValue>) -> deserialize::Result<Self> {
let ptr: *const str = FromSql::<ST, DB>::from_sql(bytes)?;
// We know that the pointer impl will never return null
Ok(Self::from_str_truncate(unsafe { &*ptr }))
}
}
#[cfg_attr(docs_rs_workaround, doc(cfg(feature = "diesel-traits")))]
#[cfg(feature = "diesel-traits")]
impl<SIZE, ST, DB> FromSqlRow<ST, DB> for ArrayString<SIZE>
where
SIZE: Capacity,
DB: Backend,
*const str: FromSql<ST, DB>,
{
const FIELDS_NEEDED: usize = 1;
#[inline]
fn build_from_row<T: Row<DB>>(row: &mut T) -> deserialize::Result<Self> {
FromSql::<ST, DB>::from_sql(row.take())
}
}
#[cfg_attr(docs_rs_workaround, doc(cfg(feature = "diesel-traits")))]
#[cfg(feature = "diesel-traits")]
impl<SIZE, ST, DB> Queryable<ST, DB> for ArrayString<SIZE>
where
SIZE: Capacity,
DB: Backend,
*const str: FromSql<ST, DB>,
{
type Row = Self;
#[inline]
fn build(row: Self::Row) -> Self {
row
}
}
#[cfg_attr(
docs_rs_workaround,
doc(cfg(all(feature = "diesel-traits", feature = "std")))
)]
#[cfg(all(feature = "diesel-traits", feature = "std"))]
impl<SIZE, DB> ToSql<VarChar, DB> for ArrayString<SIZE>
where
SIZE: Capacity,
DB: Backend,
{
#[inline]
fn to_sql<W: Write>(&self, out: &mut Output<W, DB>) -> serialize::Result {
ToSql::<VarChar, DB>::to_sql(self.as_str(), out)
}
}
#[cfg_attr(docs_rs_workaround, doc(cfg(feature = "diesel-traits")))]
#[cfg(feature = "diesel-traits")]
impl Expression for CacheString {
type SqlType = VarChar;
}
#[cfg_attr(docs_rs_workaround, doc(cfg(feature = "diesel-traits")))]
#[cfg(feature = "diesel-traits")]
impl<QS> SelectableExpression<QS> for CacheString {}
#[cfg_attr(docs_rs_workaround, doc(cfg(feature = "diesel-traits")))]
#[cfg(feature = "diesel-traits")]
impl<QS> AppearsOnTable<QS> for CacheString {}
#[cfg_attr(docs_rs_workaround, doc(cfg(feature = "diesel-traits")))]
#[cfg(feature = "diesel-traits")]
impl NonAggregate for CacheString {}
#[cfg_attr(docs_rs_workaround, doc(cfg(feature = "diesel-traits")))]
#[cfg(feature = "diesel-traits")]
impl<DB> QueryFragment<DB> for CacheString
where
DB: Backend + HasSqlType<VarChar>,
{
#[inline]
fn walk_ast(&self, pass: AstPass<DB>) -> QueryResult<()> {
self.0.walk_ast(pass)
}
}
#[cfg_attr(docs_rs_workaround, doc(cfg(feature = "diesel-traits")))]
#[cfg(feature = "diesel-traits")]
impl<ST, DB> FromSql<ST, DB> for CacheString
where
DB: Backend,
*const str: FromSql<ST, DB>,
{
#[inline]
fn from_sql(bytes: Option<&DB::RawValue>) -> deserialize::Result<Self> {
Ok(CacheString(FromSql::from_sql(bytes)?))
}
}
#[cfg_attr(docs_rs_workaround, doc(cfg(feature = "diesel-traits")))]
#[cfg(feature = "diesel-traits")]
impl<ST, DB> FromSqlRow<ST, DB> for CacheString
where
DB: Backend,
*const str: FromSql<ST, DB>,
{
const FIELDS_NEEDED: usize = 1;
#[inline]
fn build_from_row<T: Row<DB>>(row: &mut T) -> deserialize::Result<Self> {
Ok(CacheString(FromSqlRow::build_from_row(row)?))
}
}
#[cfg_attr(docs_rs_workaround, doc(cfg(feature = "diesel-traits")))]
#[cfg(feature = "diesel-traits")]
impl<ST, DB> Queryable<ST, DB> for CacheString
where
DB: Backend,
*const str: FromSql<ST, DB>,
{
type Row = Self;
#[inline]
fn build(row: Self::Row) -> Self {
row
}
}
#[cfg_attr(
docs_rs_workaround,
doc(cfg(all(feature = "diesel-traits", feature = "std")))
)]
#[cfg(all(feature = "diesel-traits", feature = "std"))]
impl<DB> ToSql<VarChar, DB> for CacheString
where
DB: Backend,
{
#[inline]
fn to_sql<W: Write>(&self, out: &mut Output<W, DB>) -> serialize::Result {
ToSql::to_sql(&self.0, out)
}
}
#[cfg_attr(docs_rs_workaround, doc(cfg(feature = "serde-traits")))]
#[cfg(feature = "serde-traits")]
impl Serialize for CacheString {
#[inline]
fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
self.0.serialize(ser)
}
}
#[cfg_attr(docs_rs_workaround, doc(cfg(feature = "serde-traits")))]
#[cfg(feature = "serde-traits")]
impl<'a> Deserialize<'a> for CacheString {
#[inline]
fn deserialize<D: Deserializer<'a>>(des: D) -> Result<Self, D::Error> {
Ok(CacheString(Deserialize::deserialize(des)?))
}
}
#[cfg(test)]
mod tests {
#![allow(proc_macro_derive_resolution_fallback)]
#![allow(unused_import_braces)]
use super::*;
use crate::ArrayString;
#[cfg(feature = "serde-traits")]
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct DeriveSerde(pub ArrayString<typenum::U8>);
#[cfg(feature = "serde-traits")]
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Derive2Serde(pub CacheString);
#[test]
#[cfg(feature = "serde-traits")]
fn serde_derive_json() {
let string =
serde_json::to_string(&DeriveSerde(ArrayString::try_from_str("abcdefg").unwrap()))
.unwrap();
let s: DeriveSerde = serde_json::from_str(&string).unwrap();
assert_eq!(
s,
DeriveSerde(ArrayString::try_from_str("abcdefg").unwrap())
);
}
#[test]
#[cfg(feature = "serde-traits")]
fn serde_derive2_json() {
let string = serde_json::to_string(&Derive2Serde(CacheString(
ArrayString::try_from_str("abcdefg").unwrap(),
)))
.unwrap();
let s: DeriveSerde = serde_json::from_str(&string).unwrap();
assert_eq!(
s,
DeriveSerde(ArrayString::try_from_str("abcdefg").unwrap())
);
}
#[test]
#[cfg(feature = "serde-traits")]
fn serde_json() {
let string =
serde_json::to_string(&ArrayString::<typenum::U8>::try_from_str("abcdefg").unwrap())
.unwrap();
let s: ArrayString<typenum::U8> = serde_json::from_str(&string).unwrap();
assert_eq!(
s,
ArrayString::<typenum::U8>::try_from_str("abcdefg").unwrap()
);
}
#[cfg(all(feature = "diesel-traits", feature = "std"))]
use diesel::{debug_query, insert_into, mysql, pg, sqlite, update};
#[cfg(all(feature = "diesel-traits", feature = "std"))]
#[macro_use]
table! {
derives (name) {
name -> VarChar,
}
}
#[cfg(all(feature = "diesel-traits", feature = "std"))]
#[derive(Queryable, Insertable, Clone, Debug)]
#[table_name = "derives"]
struct DeriveDiesel {
pub name: ArrayString<typenum::U32>,
}
#[cfg(all(feature = "diesel-traits", feature = "std"))]
#[derive(Queryable, Insertable, Clone, Debug)]
#[table_name = "derives"]
struct Derive2Diesel {
pub name: CacheString,
}
#[cfg(all(feature = "diesel-traits", feature = "std"))]
#[derive(Queryable, Insertable, Clone, Debug)]
#[table_name = "derives"]
struct Derive3Diesel<'a> {
pub name: &'a str,
}
#[cfg(all(feature = "diesel-traits", feature = "std"))]
#[test]
fn diesel_derive_query_compare_insert() {
let array = DeriveDiesel {
name: ArrayString::try_from_str("Name1").unwrap(),
};
let cache = Derive2Diesel {
name: CacheString(ArrayString::try_from_str("Name1").unwrap()),
};
let string = Derive3Diesel { name: "Name1" };
let insert_array = insert_into(derives::table).values(&array);
let insert_cache = insert_into(derives::table).values(&cache);
let insert_string = insert_into(derives::table).values(&string);
assert_eq!(
debug_query::<pg::Pg, _>(&insert_array).to_string(),
debug_query::<pg::Pg, _>(&insert_string).to_string()
);
assert_eq!(
debug_query::<pg::Pg, _>(&insert_cache).to_string(),
debug_query::<pg::Pg, _>(&insert_string).to_string()
);
assert_eq!(
debug_query::<mysql::Mysql, _>(&insert_array).to_string(),
debug_query::<mysql::Mysql, _>(&insert_string).to_string()
);
assert_eq!(
debug_query::<mysql::Mysql, _>(&insert_cache).to_string(),
debug_query::<mysql::Mysql, _>(&insert_string).to_string()
);
assert_eq!(
debug_query::<sqlite::Sqlite, _>(&insert_array).to_string(),
debug_query::<sqlite::Sqlite, _>(&insert_string).to_string()
);
assert_eq!(
debug_query::<sqlite::Sqlite, _>(&insert_cache).to_string(),
debug_query::<sqlite::Sqlite, _>(&insert_string).to_string()
);
}
#[test]
fn diesel_derive_query_compare_update() {
let array = DeriveDiesel {
name: ArrayString::try_from_str("Name1").unwrap(),
};
let cache = Derive2Diesel {
name: CacheString(ArrayString::try_from_str("Name1").unwrap()),
};
let string = Derive3Diesel { name: "Name1" };
let update_array = update(derives::table).set(derives::name.eq(&array.name));
let update_cache = update(derives::table).set(derives::name.eq(&cache.name));
let update_string = update(derives::table).set(derives::name.eq(&string.name));
assert_eq!(
debug_query::<pg::Pg, _>(&update_array).to_string(),
debug_query::<pg::Pg, _>(&update_string).to_string()
);
assert_eq!(
debug_query::<pg::Pg, _>(&update_cache).to_string(),
debug_query::<pg::Pg, _>(&update_string).to_string()
);
assert_eq!(
debug_query::<mysql::Mysql, _>(&update_array).to_string(),
debug_query::<mysql::Mysql, _>(&update_string).to_string()
);
assert_eq!(
debug_query::<mysql::Mysql, _>(&update_cache).to_string(),
debug_query::<mysql::Mysql, _>(&update_string).to_string()
);
assert_eq!(
debug_query::<sqlite::Sqlite, _>(&update_array).to_string(),
debug_query::<sqlite::Sqlite, _>(&update_string).to_string()
);
assert_eq!(
debug_query::<sqlite::Sqlite, _>(&update_cache).to_string(),
debug_query::<sqlite::Sqlite, _>(&update_string).to_string()
);
}
#[test]
#[ignore]
#[cfg(feature = "std")]
fn diesel_select_query_compiles() {
let conn = pg::PgConnection::establish("").unwrap();
let select_array: Vec<DeriveDiesel> = derives::table
.select(derives::all_columns)
.load(&conn)
.unwrap();
let select_cache: Vec<Derive2Diesel> = derives::table
.select(derives::all_columns)
.load(&conn)
.unwrap();
assert_eq!(
select_cache
.into_iter()
.map(|d| d.name.to_string())
.collect::<Vec<_>>(),
select_array
.into_iter()
.map(|d| d.name.to_string())
.collect::<Vec<_>>()
);
let _: std::time::SystemTime = derives::table.select(dsl::now).first(&conn).unwrap();
let _: std::time::SystemTime = derives::table.select(dsl::now).first(&conn).unwrap();
let conn = mysql::MysqlConnection::establish("").unwrap();
let select_array: Vec<DeriveDiesel> = derives::table
.select(derives::all_columns)
.load(&conn)
.unwrap();
let select_cache: Vec<Derive2Diesel> = derives::table
.select(derives::all_columns)
.load(&conn)
.unwrap();
assert_eq!(
select_array
.into_iter()
.map(|d| d.name.to_string())
.collect::<Vec<_>>(),
select_cache
.into_iter()
.map(|d| d.name.to_string())
.collect::<Vec<_>>()
);
}
#[cfg(all(feature = "diesel-traits", feature = "std"))]
#[test]
fn diesel_derive_query_sqlite() {
let conn = diesel::sqlite::SqliteConnection::establish(":memory:").unwrap();
let _ = diesel::sql_query("CREATE TABLE derives (name VARCHAR(32));")
.execute(&conn)
.unwrap();
let string = DeriveDiesel {
name: ArrayString::try_from_str("Name1").unwrap(),
};
let _ = insert_into(derives::table)
.values(&string)
.execute(&conn)
.unwrap();
let queried: DeriveDiesel = derives::table.first(&conn).unwrap();
assert_eq!(queried.name.as_str(), "Name1");
}
#[cfg(all(feature = "diesel-traits", feature = "std"))]
#[test]
fn diesel_derive2_query_sqlite() {
let conn = diesel::sqlite::SqliteConnection::establish(":memory:").unwrap();
let _ = diesel::sql_query("CREATE TABLE derives (name VARCHAR(32));")
.execute(&conn)
.unwrap();
let string = Derive2Diesel {
name: CacheString(ArrayString::try_from_str("Name1").unwrap()),
};
let _ = insert_into(derives::table)
.values(&string)
.execute(&conn)
.unwrap();
let queried: Derive2Diesel = derives::table.first(&conn).unwrap();
assert_eq!(queried.name.as_str(), "Name1");
}
}

811
third_party/rust/arraystring/src/lib.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,811 @@
//! Fixed capacity stack based generic string
//!
//! Since rust doesn't have constant generics yet `typenum` is used to allow for generic arrays (`U1` to `U255`)
//!
//! Can't outgrow initial capacity (defined at compile time), always occupies [`capacity`] `+ 1` bytes of memory
//!
//! *Doesn't allocate memory on the heap and never panics in release (all panic branches are stripped at compile time - except `Index`/`IndexMut` traits, since they are supposed to)*
//!
//! ## Why
//!
//! Data is generally bounded, you don't want a phone number with 30 characters, nor a username with 100. You probably don't even support it in your database.
//!
//! Why pay the cost of heap allocations of strings with unlimited capacity if you have limited boundaries?
//!
//! Stack based strings are generally faster to create, clone and append to than heap based strings (custom allocators and thread-locals may help with heap based ones).
//!
//! But that becomes less true as you increase the array size, 255 bytes is the maximum we accept (bigger will just wrap) and it's probably already slower than heap based strings of that size (like in `std::string::String`)
//!
//! There are other stack based strings out there, they generally can have "unlimited" capacity (heap allocate), but the stack based size is defined by the library implementor, we go through a different route by implementing a string based in a generic array.
//!
//! Array based strings always occupies the full space in memory, so they may use more memory (in the stack) than dynamic strings.
//!
//! [`capacity`]: ./struct.ArrayString.html#method.capacity
//!
//! ## Features
//!
//! **default:** `std`
//!
//! - `std` enabled by default, enables `std` compatibility - `impl Error` trait for errors (remove it to be `#[no_std]` compatible)
//! - `serde-traits` enables serde traits integration (`Serialize`/`Deserialize`)
//!
//! Opperates like `String`, but truncates it if it's bigger than capacity
//!
//! - `diesel-traits` enables diesel traits integration (`Insertable`/`Queryable`)
//!
//! Opperates like `String`, but truncates it if it's bigger than capacity
//!
//! - `logs` enables internal logging
//!
//! You will probably only need this if you are debugging this library
//!
//! ## Examples
//!
//! ```rust
//! use arraystring::{Error, ArrayString, typenum::U5, typenum::U20};
//!
//! type Username = ArrayString<U20>;
//! type Role = ArrayString<U5>;
//!
//! #[derive(Debug)]
//! pub struct User {
//! pub username: Username,
//! pub role: Role,
//! }
//!
//! fn main() -> Result<(), Error> {
//! let user = User {
//! username: Username::try_from_str("user")?,
//! role: Role::try_from_str("admin")?
//! };
//! println!("{:?}", user);
//!
//! Ok(())
//! }
//! ```
//!
//! ## Comparisons
//!
//! *These benchmarks ran while I streamed video and used my computer (with* **non-disclosed specs**) *as usual, so don't take the actual times too seriously, just focus on the comparison*
//!
//! ```my_custom_benchmark
//! small-string (23 bytes) clone 4.837 ns
//! small-string (23 bytes) try_from_str 14.777 ns
//! small-string (23 bytes) from_str_truncate 11.360 ns
//! small-string (23 bytes) from_str_unchecked 11.291 ns
//! small-string (23 bytes) try_push_str 1.162 ns
//! small-string (23 bytes) push_str 3.490 ns
//! small-string (23 bytes) push_str_unchecked 1.098 ns
//! -------------------------------------------------------------
//! cache-string (63 bytes) clone 10.170 ns
//! cache-string (63 bytes) try_from_str 25.579 ns
//! cache-string (63 bytes) from_str_truncate 16.977 ns
//! cache-string (63 bytes) from_str_unchecked 17.201 ns
//! cache-string (63 bytes) try_push_str 1.160 ns
//! cache-string (63 bytes) push_str 3.486 ns
//! cache-string (63 bytes) push_str_unchecked 1.115 ns
//! -------------------------------------------------------------
//! max-string (255 bytes) clone 147.410 ns
//! max-string (255 bytes) try_from_str 157.340 ns
//! max-string (255 bytes) from_str_truncate 158.000 ns
//! max-string (255 bytes) from_str_unchecked 158.420 ns
//! max-string (255 bytes) try_push_str 1.167 ns
//! max-string (255 bytes) push_str 4.337 ns
//! max-string (255 bytes) push_str_unchecked 1.103 ns
//! -------------------------------------------------------------
//! string clone 33.295 ns
//! string from 32.512 ns
//! string push str 28.128 ns
//! -------------------------------------------------------------
//! inlinable-string (30 bytes) clone 16.751 ns
//! inlinable-string (30 bytes) from_str 29.310 ns
//! inlinable-string (30 bytes) push_str 2.865 ns
//! -------------------------------------------------------------
//! smallstring crate (20 bytes) clone 60.988 ns
//! smallstring crate (20 bytes) from_str 50.233 ns
//! ```
//!
//! ## Licenses
//!
//! `MIT` and `Apache-2.0`
#![doc(html_root_url = "https://docs.rs/arraystring/0.3.0/arraystring")]
#![cfg_attr(docs_rs_workaround, feature(doc_cfg))]
#![cfg_attr(not(feature = "std"), no_std)]
#![warn(
missing_docs,
missing_debug_implementations,
trivial_numeric_casts,
unused_extern_crates,
unused_import_braces,
unused_qualifications,
unused_results,
bad_style,
const_err,
dead_code,
improper_ctypes,
legacy_directory_ownership,
non_shorthand_field_patterns,
no_mangle_generic_items,
overflowing_literals,
path_statements,
patterns_in_fns_without_body,
plugin_as_library,
private_in_public,
safe_extern_statics,
unconditional_recursion,
unions_with_drop_fields,
unused_allocation,
unused_comparisons,
unused_parens,
while_true
)]
#![doc(test(attr(deny(warnings))))]
pub use typenum;
/// Remove logging macros when they are disabled (at compile time)
#[macro_use]
#[cfg(not(feature = "logs"))]
#[allow(unused)]
mod mock {
macro_rules! trace(($($x:tt)*) => ());
macro_rules! debug(($($x:tt)*) => ());
macro_rules! info(($($x:tt)*) => ());
macro_rules! warn(($($x:tt)*) => ());
macro_rules! error(($($x:tt)*) => ());
}
#[cfg(all(feature = "diesel-traits", test))]
#[macro_use]
extern crate diesel;
mod arraystring;
pub mod drain;
pub mod error;
mod generic;
mod implementations;
#[cfg(any(feature = "serde-traits", feature = "diesel-traits"))]
mod integration;
#[doc(hidden)]
pub mod utils;
/// Most used traits and data-strucutres
pub mod prelude {
pub use crate::arraystring::ArrayString;
pub use crate::drain::Drain;
pub use crate::error::{OutOfBounds, Utf16, Utf8};
pub use crate::{generic::Capacity, CacheString, MaxString, SmallString};
}
pub use crate::arraystring::ArrayString;
pub use crate::error::Error;
use crate::prelude::*;
use core::fmt::{self, Debug, Display, Formatter, Write};
use core::{borrow::Borrow, borrow::BorrowMut, ops::*};
use core::{cmp::Ordering, hash::Hash, hash::Hasher, str::FromStr};
#[cfg(feature = "logs")]
use log::trace;
use typenum::{Unsigned, U255, U63};
#[cfg(target_pointer_width="64")]
use typenum::U23;
#[cfg(target_pointer_width="32")]
use typenum::U11;
/// String with the same `mem::size_of` of a `String`
///
/// 24 bytes in 64 bits architecture
///
/// 12 bytes in 32 bits architecture (or others)
#[cfg(target_pointer_width="64")]
pub type SmallString = ArrayString<U23>;
/// String with the same `mem::size_of` of a `String`
///
/// 24 bytes in 64 bits architecture
///
/// 12 bytes in 32 bits architecture (or others)
#[cfg(not(target_pointer_width="64"))]
pub type SmallString = ArrayString<U11>;
/// Biggest array based string (255 bytes of string)
pub type MaxString = ArrayString<U255>;
/// Newtype string that occupies 64 bytes in memory and is 64 bytes aligned (full cache line)
///
/// 63 bytes of string
#[repr(align(64))]
#[derive(Copy, Clone, Default)]
pub struct CacheString(pub ArrayString<U63>);
impl CacheString {
/// Creates new empty `CacheString`.
///
/// ```rust
/// # use arraystring::prelude::*;
/// # let _ = env_logger::try_init();
/// let string = CacheString::new();
/// assert!(string.is_empty());
/// ```
#[inline]
pub fn new() -> Self {
trace!("New empty CacheString");
Self::default()
}
/// Creates new `CacheString` from string slice if length is lower or equal to [`capacity`], otherwise returns an error.
///
/// [`capacity`]: ./struct.CacheString.html#method.capacity
/// ```rust
/// # use arraystring::{error::Error, prelude::*};
/// # fn main() -> Result<(), Error> {
/// # let _ = env_logger::try_init();
/// let string = CacheString::try_from_str("My String")?;
/// assert_eq!(string.as_str(), "My String");
///
/// assert_eq!(CacheString::try_from_str("")?.as_str(), "");
///
/// let out_of_bounds = "0".repeat(CacheString::capacity() as usize + 1);
/// assert!(CacheString::try_from_str(out_of_bounds).is_err());
/// # Ok(())
/// # }
/// ```
#[inline]
pub fn try_from_str<S>(s: S) -> Result<Self, OutOfBounds>
where
S: AsRef<str>,
{
Ok(CacheString(ArrayString::try_from_str(s)?))
}
/// Creates new `CacheString` from string slice truncating size if bigger than [`capacity`].
///
/// [`capacity`]: ./struct.CacheString.html#method.capacity
///
/// ```rust
/// # use arraystring::prelude::*;
/// # let _ = env_logger::try_init();
/// let string = CacheString::from_str_truncate("My String");
/// # assert_eq!(string.as_str(), "My String");
/// println!("{}", string);
///
/// let truncate = "0".repeat(CacheString::capacity() as usize + 1);
/// let truncated = "0".repeat(CacheString::capacity().into());
/// let string = CacheString::from_str_truncate(&truncate);
/// assert_eq!(string.as_str(), truncated);
/// ```
#[inline]
pub fn from_str_truncate<S>(string: S) -> Self
where
S: AsRef<str>,
{
CacheString(ArrayString::from_str_truncate(string))
}
/// Creates new `CacheString` from string slice assuming length is appropriate.
///
/// # Safety
///
/// It's UB if `string.len()` > [`capacity`].
///
/// [`capacity`]: ./struct.CacheString.html#method.capacity
///
/// ```rust
/// # use arraystring::prelude::*;
/// let filled = "0".repeat(CacheString::capacity().into());
/// let string = unsafe {
/// CacheString::from_str_unchecked(&filled)
/// };
/// assert_eq!(string.as_str(), filled.as_str());
///
/// // Undefined behavior, don't do it
/// // let out_of_bounds = "0".repeat(CacheString::capacity().into() + 1);
/// // let ub = unsafe { CacheString::from_str_unchecked(out_of_bounds) };
/// ```
#[inline]
pub unsafe fn from_str_unchecked<S>(string: S) -> Self
where
S: AsRef<str>,
{
CacheString(ArrayString::from_str_unchecked(string))
}
/// Creates new `CacheString` from string slice iterator if total length is lower or equal to [`capacity`], otherwise returns an error.
///
/// [`capacity`]: ./struct.CacheString.html#method.capacity
///
/// ```rust
/// # use arraystring::prelude::*;
/// # fn main() -> Result<(), OutOfBounds> {
/// let string = CacheString::try_from_iterator(&["My String", " My Other String"][..])?;
/// assert_eq!(string.as_str(), "My String My Other String");
///
/// let out_of_bounds = (0..100).map(|_| "000");
/// assert!(CacheString::try_from_iterator(out_of_bounds).is_err());
/// # Ok(())
/// # }
/// ```
#[inline]
pub fn try_from_iterator<U, I>(iter: I) -> Result<Self, OutOfBounds>
where
U: AsRef<str>,
I: IntoIterator<Item = U>,
{
Ok(CacheString(ArrayString::try_from_iterator(iter)?))
}
/// Creates new `CacheString` from string slice iterator truncating size if bigger than [`capacity`].
///
/// [`capacity`]: ./struct.CacheString.html#method.capacity
///
/// ```rust
/// # use arraystring::prelude::*;
/// # fn main() -> Result<(), OutOfBounds> {
/// # let _ = env_logger::try_init();
/// let string = CacheString::from_iterator(&["My String", " Other String"][..]);
/// assert_eq!(string.as_str(), "My String Other String");
///
/// let out_of_bounds = (0..400).map(|_| "000");
/// let truncated = "0".repeat(CacheString::capacity().into());
///
/// let truncate = CacheString::from_iterator(out_of_bounds);
/// assert_eq!(truncate.as_str(), truncated.as_str());
/// # Ok(())
/// # }
/// ```
#[inline]
pub fn from_iterator<U, I>(iter: I) -> Self
where
U: AsRef<str>,
I: IntoIterator<Item = U>,
{
CacheString(ArrayString::from_iterator(iter))
}
/// Creates new `CacheString` from string slice iterator assuming length is appropriate.
///
/// # Safety
///
/// It's UB if `iter.map(|c| c.len()).sum()` > [`capacity`].
///
/// [`capacity`]: ./struct.CacheString.html#method.capacity
///
/// ```rust
/// # use arraystring::prelude::*;
/// let string = unsafe {
/// CacheString::from_iterator_unchecked(&["My String", " My Other String"][..])
/// };
/// assert_eq!(string.as_str(), "My String My Other String");
///
/// // Undefined behavior, don't do it
/// // let out_of_bounds = (0..400).map(|_| "000");
/// // let undefined_behavior = unsafe {
/// // CacheString::from_iterator_unchecked(out_of_bounds)
/// // };
/// ```
#[inline]
pub unsafe fn from_iterator_unchecked<U, I>(iter: I) -> Self
where
U: AsRef<str>,
I: IntoIterator<Item = U>,
{
CacheString(ArrayString::from_iterator_unchecked(iter))
}
/// Creates new `CacheString` from char iterator if total length is lower or equal to [`capacity`], otherwise returns an error.
///
/// [`capacity`]: ./struct.CacheString.html#method.capacity
///
/// ```rust
/// # use arraystring::{error::Error, prelude::*};
/// # fn main() -> Result<(), Error> {
/// # let _ = env_logger::try_init();
/// let string = CacheString::try_from_chars("My String".chars())?;
/// assert_eq!(string.as_str(), "My String");
///
/// let out_of_bounds = "0".repeat(CacheString::capacity() as usize + 1);
/// assert!(CacheString::try_from_chars(out_of_bounds.chars()).is_err());
/// # Ok(())
/// # }
/// ```
#[inline]
pub fn try_from_chars<I>(iter: I) -> Result<Self, OutOfBounds>
where
I: IntoIterator<Item = char>,
{
Ok(CacheString(ArrayString::try_from_chars(iter)?))
}
/// Creates new `CacheString` from char iterator truncating size if bigger than [`capacity`].
///
/// [`capacity`]: ./struct.CacheString.html#method.capacity
///
/// ```rust
/// # use arraystring::prelude::*;
/// # let _ = env_logger::try_init();
/// let string = CacheString::from_chars("My String".chars());
/// assert_eq!(string.as_str(), "My String");
///
/// let out_of_bounds = "0".repeat(CacheString::capacity() as usize + 1);
/// let truncated = "0".repeat(CacheString::capacity().into());
///
/// let truncate = CacheString::from_chars(out_of_bounds.chars());
/// assert_eq!(truncate.as_str(), truncated.as_str());
/// ```
#[inline]
pub fn from_chars<I>(iter: I) -> Self
where
I: IntoIterator<Item = char>,
{
CacheString(ArrayString::from_chars(iter))
}
/// Creates new `CacheString` from char iterator assuming length is appropriate.
///
/// # Safety
///
/// It's UB if `iter.map(|c| c.len_utf8()).sum()` > [`capacity`].
///
/// [`capacity`]: ./struct.CacheString.html#method.capacity
///
/// ```rust
/// # use arraystring::prelude::*;
/// let string = unsafe { CacheString::from_chars_unchecked("My String".chars()) };
/// assert_eq!(string.as_str(), "My String");
///
/// // Undefined behavior, don't do it
/// // let out_of_bounds = "000".repeat(400);
/// // let undefined_behavior = unsafe { CacheString::from_chars_unchecked(out_of_bounds.chars()) };
/// ```
#[inline]
pub unsafe fn from_chars_unchecked<I>(iter: I) -> Self
where
I: IntoIterator<Item = char>,
{
CacheString(ArrayString::from_chars_unchecked(iter))
}
/// Creates new `CacheString` from byte slice, returning [`Utf8`] on invalid utf-8 data or [`OutOfBounds`] if bigger than [`capacity`]
///
/// [`Utf8`]: ./error/enum.Error.html#variant.Utf8
/// [`OutOfBounds`]: ./error/enum.Error.html#variant.OutOfBounds
/// [`capacity`]: ./struct.CacheString.html#method.capacity
///
/// ```rust
/// # use arraystring::{error::Error, prelude::*};
/// # fn main() -> Result<(), Error> {
/// # let _ = env_logger::try_init();
/// let string = CacheString::try_from_utf8("My String")?;
/// assert_eq!(string.as_str(), "My String");
///
/// let invalid_utf8 = [0, 159, 146, 150];
/// assert_eq!(CacheString::try_from_utf8(invalid_utf8), Err(Error::Utf8));
///
/// let out_of_bounds = "0000".repeat(400);
/// assert_eq!(CacheString::try_from_utf8(out_of_bounds.as_bytes()), Err(Error::OutOfBounds));
/// # Ok(())
/// # }
/// ```
#[inline]
pub fn try_from_utf8<B>(slice: B) -> Result<Self, Error>
where
B: AsRef<[u8]>,
{
Ok(CacheString(ArrayString::try_from_utf8(slice)?))
}
/// Creates new `CacheString` from byte slice, returning [`Utf8`] on invalid utf-8 data, truncating if bigger than [`capacity`].
///
/// [`Utf8`]: ./error/struct.Utf8.html
/// [`capacity`]: ./struct.CacheString.html#method.capacity
///
/// ```rust
/// # use arraystring::{error::Error, prelude::*};
/// # fn main() -> Result<(), Error> {
/// # let _ = env_logger::try_init();
/// let string = CacheString::from_utf8("My String")?;
/// assert_eq!(string.as_str(), "My String");
///
/// let invalid_utf8 = [0, 159, 146, 150];
/// assert_eq!(CacheString::from_utf8(invalid_utf8), Err(Utf8));
///
/// let out_of_bounds = "0".repeat(300);
/// assert_eq!(CacheString::from_utf8(out_of_bounds.as_bytes())?.as_str(),
/// "0".repeat(CacheString::capacity().into()).as_str());
/// # Ok(())
/// # }
/// ```
#[inline]
pub fn from_utf8<B>(slice: B) -> Result<Self, Utf8>
where
B: AsRef<[u8]>,
{
Ok(CacheString(ArrayString::from_utf8(slice)?))
}
/// Creates new `CacheString` from byte slice assuming it's utf-8 and of a appropriate size.
///
/// # Safety
///
/// It's UB if `slice` is not a valid utf-8 string or `slice.len()` > [`capacity`].
///
/// [`capacity`]: ./struct.CacheString.html#method.capacity
///
/// ```rust
/// # use arraystring::prelude::*;
/// let string = unsafe { CacheString::from_utf8_unchecked("My String") };
/// assert_eq!(string.as_str(), "My String");
///
/// // Undefined behavior, don't do it
/// // let out_of_bounds = "0".repeat(300);
/// // let ub = unsafe { CacheString::from_utf8_unchecked(out_of_bounds)) };
/// ```
#[inline]
pub unsafe fn from_utf8_unchecked<B>(slice: B) -> Self
where
B: AsRef<[u8]>,
{
CacheString(ArrayString::from_utf8_unchecked(slice))
}
/// Creates new `CacheString` from `u16` slice, returning [`Utf16`] on invalid utf-16 data or [`OutOfBounds`] if bigger than [`capacity`]
///
/// [`Utf16`]: ./error/enum.Error.html#variant.Utf16
/// [`OutOfBounds`]: ./error/enum.Error.html#variant.OutOfBounds
/// [`capacity`]: ./struct.CacheString.html#method.capacity
///
/// ```rust
/// # use arraystring::{error::Error, prelude::*};
/// # fn main() -> Result<(), Error> {
/// # let _ = env_logger::try_init();
/// let music = [0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0x0069, 0x0063];
/// let string = CacheString::try_from_utf16(music)?;
/// assert_eq!(string.as_str(), "𝄞music");
///
/// let invalid_utf16 = [0xD834, 0xDD1E, 0x006d, 0x0075, 0xD800, 0x0069, 0x0063];
/// assert_eq!(CacheString::try_from_utf16(invalid_utf16), Err(Error::Utf16));
///
/// let out_of_bounds: Vec<_> = (0..300).map(|_| 0).collect();
/// assert_eq!(CacheString::try_from_utf16(out_of_bounds), Err(Error::OutOfBounds));
/// # Ok(())
/// # }
/// ```
#[inline]
pub fn try_from_utf16<B>(slice: B) -> Result<Self, Error>
where
B: AsRef<[u16]>,
{
Ok(CacheString(ArrayString::try_from_utf16(slice)?))
}
/// Creates new `CacheString` from `u16` slice, returning [`Utf16`] on invalid utf-16 data, truncating if bigger than [`capacity`].
///
/// [`Utf16`]: ./error/struct.Utf16.html
/// [`capacity`]: ./struct.CacheString.html#method.capacity
///
/// ```rust
/// # use arraystring::{error::Error, prelude::*};
/// # fn main() -> Result<(), Error> {
/// # let _ = env_logger::try_init();
/// let music = [0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0x0069, 0x0063];
/// let string = CacheString::from_utf16(music)?;
/// assert_eq!(string.as_str(), "𝄞music");
///
/// let invalid_utf16 = [0xD834, 0xDD1E, 0x006d, 0x0075, 0xD800, 0x0069, 0x0063];
/// assert_eq!(CacheString::from_utf16(invalid_utf16), Err(Utf16));
///
/// let out_of_bounds: Vec<u16> = (0..300).map(|_| 0).collect();
/// assert_eq!(CacheString::from_utf16(out_of_bounds)?.as_str(),
/// "\0".repeat(CacheString::capacity().into()).as_str());
/// # Ok(())
/// # }
/// ```
#[inline]
pub fn from_utf16<B>(slice: B) -> Result<Self, Utf16>
where
B: AsRef<[u16]>,
{
Ok(CacheString(ArrayString::from_utf16(slice)?))
}
/// Creates new `CacheString` from `u16` slice, replacing invalid utf-16 data with `REPLACEMENT_CHARACTER` (\u{FFFD}) and truncating size if bigger than [`capacity`]
///
/// [`capacity`]: ./struct.CacheString.html#method.capacity
///
/// ```rust
/// # use arraystring::{error::Error, prelude::*};
/// # fn main() -> Result<(), Error> {
/// # let _ = env_logger::try_init();
/// let music = [0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0x0069, 0x0063];
/// let string = CacheString::from_utf16_lossy(music);
/// assert_eq!(string.as_str(), "𝄞music");
///
/// let invalid_utf16 = [0xD834, 0xDD1E, 0x006d, 0x0075, 0xD800, 0x0069, 0x0063];
/// assert_eq!(CacheString::from_utf16_lossy(invalid_utf16).as_str(), "𝄞mu\u{FFFD}ic");
///
/// let out_of_bounds: Vec<u16> = (0..300).map(|_| 0).collect();
/// assert_eq!(CacheString::from_utf16_lossy(&out_of_bounds).as_str(),
/// "\0".repeat(CacheString::capacity().into()).as_str());
/// # Ok(())
/// # }
/// ```
#[inline]
pub fn from_utf16_lossy<B>(slice: B) -> Self
where
B: AsRef<[u16]>,
{
CacheString(ArrayString::from_utf16_lossy(slice))
}
/// Returns maximum string capacity, defined at compile time, it will never change
///
/// Should always return 63 bytes
///
/// ```rust
/// # use arraystring::prelude::*;
/// # let _ = env_logger::try_init();
/// assert_eq!(CacheString::capacity(), 63);
/// ```
#[inline]
pub fn capacity() -> u8 {
<U63 as Unsigned>::to_u8()
}
/// Splits `CacheString` in two if `at` is smaller than `self.len()`.
///
/// Returns [`Utf8`] if `at` does not lie at a valid utf-8 char boundary and [`OutOfBounds`] if it's out of bounds
///
/// [`OutOfBounds`]: ./error/enum.Error.html#variant.OutOfBounds
/// [`Utf8`]: ./error/enum.Error.html#variant.Utf8
///
/// ```rust
/// # use arraystring::{error::Error, prelude::*};
/// # fn main() -> Result<(), Error> {
/// # let _ = env_logger::try_init();
/// let mut s = CacheString::try_from_str("AB🤔CD")?;
/// assert_eq!(s.split_off(6)?.as_str(), "CD");
/// assert_eq!(s.as_str(), "AB🤔");
/// assert_eq!(s.split_off(20), Err(Error::OutOfBounds));
/// assert_eq!(s.split_off(4), Err(Error::Utf8));
/// # Ok(())
/// # }
/// ```
#[inline]
pub fn split_off(&mut self, at: u8) -> Result<Self, Error> {
Ok(CacheString(self.0.split_off(at)?))
}
}
impl Debug for CacheString {
#[inline]
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.debug_tuple("CacheString").field(&self.0).finish()
}
}
impl Hash for CacheString {
#[inline]
fn hash<H: Hasher>(&self, hasher: &mut H) {
self.0.hash(hasher);
}
}
impl PartialEq for CacheString {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.0.eq(&other.0)
}
}
impl Eq for CacheString {}
impl Ord for CacheString {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
self.0.cmp(&other.0)
}
}
impl PartialOrd for CacheString {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Deref for CacheString {
type Target = ArrayString<U63>;
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for CacheString {
#[inline]
fn deref_mut(&mut self) -> &mut ArrayString<U63> {
&mut self.0
}
}
impl Display for CacheString {
#[inline]
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(&self.0, f)
}
}
impl AsRef<str> for CacheString {
#[inline]
fn as_ref(&self) -> &str {
self.0.as_ref()
}
}
impl AsMut<str> for CacheString {
#[inline]
fn as_mut(&mut self) -> &mut str {
self.0.as_mut()
}
}
impl AsRef<[u8]> for CacheString {
#[inline]
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}
impl FromStr for CacheString {
type Err = OutOfBounds;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(CacheString(ArrayString::try_from_str(s)?))
}
}
impl<'a, 'b> PartialEq<str> for CacheString {
#[inline]
fn eq(&self, other: &str) -> bool {
self.0.eq(other)
}
}
impl Borrow<str> for CacheString {
#[inline]
fn borrow(&self) -> &str {
self.0.borrow()
}
}
impl BorrowMut<str> for CacheString {
#[inline]
fn borrow_mut(&mut self) -> &mut str {
self.0.borrow_mut()
}
}
impl<'a> Add<&'a str> for CacheString {
type Output = Self;
#[inline]
fn add(self, other: &str) -> Self::Output {
CacheString(self.0.add(other))
}
}
impl Write for CacheString {
#[inline]
fn write_str(&mut self, slice: &str) -> fmt::Result {
self.0.write_str(slice)
}
}
impl From<ArrayString<U63>> for CacheString {
fn from(array: ArrayString<U63>) -> Self {
CacheString(array)
}
}

246
third_party/rust/arraystring/src/utils.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,246 @@
//! Misc functions to improve readability
use crate::{generic::ArraySlice, prelude::*};
use core::ptr::copy;
#[cfg(feature = "logs")]
use log::{debug, trace};
pub(crate) trait IntoLossy<T>: Sized {
fn into_lossy(self) -> T;
}
/// Marks branch as impossible, UB if taken in prod, panics in debug
///
/// This function should never be used lightly, it will cause UB if used wrong
#[inline]
#[allow(unused_variables)]
pub(crate) unsafe fn never(s: &str) -> ! {
#[cfg(debug_assertions)]
panic!("{}", s);
#[cfg(not(debug_assertions))]
core::hint::unreachable_unchecked()
}
/// Encodes `char` into `ArrayString` at specified position, heavily unsafe
///
/// We reimplement the `core` function to avoid panicking (UB instead, be careful)
///
/// Reimplemented from:
///
/// `https://github.com/rust-lang/rust/blob/7843e2792dce0f20d23b3c1cca51652013bef0ea/src/libcore/char/methods.rs#L447`
/// # Safety
///
/// - It's UB if index is outside of buffer's boundaries (buffer needs at most 4 bytes)
/// - It's UB if index is inside a character (like a index 3 for "a🤔")
#[inline]
pub(crate) unsafe fn encode_char_utf8_unchecked<S: Capacity>(
s: &mut ArrayString<S>,
ch: char,
index: u8,
) {
// UTF-8 ranges and tags for encoding characters
#[allow(clippy::missing_docs_in_private_items)]
const TAG_CONT: u8 = 0b1000_0000;
#[allow(clippy::missing_docs_in_private_items)]
const TAG_TWO_B: u8 = 0b1100_0000;
#[allow(clippy::missing_docs_in_private_items)]
const TAG_THREE_B: u8 = 0b1110_0000;
#[allow(clippy::missing_docs_in_private_items)]
const TAG_FOUR_B: u8 = 0b1111_0000;
#[allow(clippy::missing_docs_in_private_items)]
const MAX_ONE_B: u32 = 0x80;
#[allow(clippy::missing_docs_in_private_items)]
const MAX_TWO_B: u32 = 0x800;
#[allow(clippy::missing_docs_in_private_items)]
const MAX_THREE_B: u32 = 0x10000;
trace!("Encode char: {} to {}", ch, index);
debug_assert!(ch.len_utf8().saturating_add(index.into()) <= S::to_usize());
debug_assert!(ch.len_utf8().saturating_add(s.len().into()) <= S::to_usize());
let dst = s.array.as_mut_slice().get_unchecked_mut(index.into()..);
let code = ch as u32;
if code < MAX_ONE_B {
debug_assert!(!dst.is_empty());
*dst.get_unchecked_mut(0) = code.into_lossy();
} else if code < MAX_TWO_B {
debug_assert!(dst.len() >= 2);
*dst.get_unchecked_mut(0) = (code >> 6 & 0x1F).into_lossy() | TAG_TWO_B;
*dst.get_unchecked_mut(1) = (code & 0x3F).into_lossy() | TAG_CONT;
} else if code < MAX_THREE_B {
debug_assert!(dst.len() >= 3);
*dst.get_unchecked_mut(0) = (code >> 12 & 0x0F).into_lossy() | TAG_THREE_B;
*dst.get_unchecked_mut(1) = (code >> 6 & 0x3F).into_lossy() | TAG_CONT;
*dst.get_unchecked_mut(2) = (code & 0x3F).into_lossy() | TAG_CONT;
} else {
debug_assert!(dst.len() >= 4);
*dst.get_unchecked_mut(0) = (code >> 18 & 0x07).into_lossy() | TAG_FOUR_B;
*dst.get_unchecked_mut(1) = (code >> 12 & 0x3F).into_lossy() | TAG_CONT;
*dst.get_unchecked_mut(2) = (code >> 6 & 0x3F).into_lossy() | TAG_CONT;
*dst.get_unchecked_mut(3) = (code & 0x3F).into_lossy() | TAG_CONT;
}
}
/// Copies part of slice to another part (`mem::copy`, basically `memmove`)
#[inline]
unsafe fn shift_unchecked(s: &mut [u8], from: usize, to: usize, len: usize) {
debug!(
"Shift {:?} {}-{}",
&s.get(from..).map(|s| s.get(..len)),
from,
to
);
debug_assert!(to.saturating_add(len) <= s.len() && from.saturating_add(len) <= s.len());
let (f, t) = (s.as_ptr().add(from), s.as_mut_ptr().add(to));
copy(f, t, len);
}
/// Shifts string right
///
/// # Safety
///
/// It's UB if `to + (s.len() - from)` is bigger than [`S::to_u8()`]
///
/// [`<S as Unsigned>::to_u8()`]: ../struct.ArrayString.html#CAPACITY
#[inline]
pub(crate) unsafe fn shift_right_unchecked<S, F, T>(s: &mut ArrayString<S>, from: F, to: T)
where
S: Capacity,
F: Into<usize> + Copy,
T: Into<usize> + Copy,
{
let len = (s.len() as usize).saturating_sub(from.into());
debug_assert!(from.into() <= to.into() && to.into().saturating_add(len) <= S::to_usize());
debug_assert!(s.as_str().is_char_boundary(from.into()));
shift_unchecked(s.array.as_mut_slice(), from.into(), to.into(), len);
}
/// Shifts string left
#[inline]
pub(crate) unsafe fn shift_left_unchecked<S, F, T>(s: &mut ArrayString<S>, from: F, to: T)
where
S: Capacity,
F: Into<usize> + Copy,
T: Into<usize> + Copy,
{
debug_assert!(to.into() <= from.into() && from.into() <= s.len().into());
debug_assert!(s.as_str().is_char_boundary(from.into()));
let len = (s.len() as usize).saturating_sub(to.into());
shift_unchecked(s.array.as_mut_slice(), from.into(), to.into(), len);
}
/// Returns error if size is outside of specified boundary
#[inline]
pub fn is_inside_boundary<S, L>(size: S, limit: L) -> Result<(), OutOfBounds>
where
S: Into<usize>,
L: Into<usize>,
{
let (s, l) = (size.into(), limit.into());
trace!("Out of bounds: ensures {} < {}", s, l);
Some(()).filter(|_| s <= l).ok_or(OutOfBounds)
}
/// Returns error if index is not at a valid utf-8 char boundary
#[inline]
pub fn is_char_boundary<S: Capacity>(s: &ArrayString<S>, idx: u8) -> Result<(), Utf8> {
trace!("Is char boundary: {} at {}", s.as_str(), idx);
if s.as_str().is_char_boundary(idx.into()) {
return Ok(());
}
Err(Utf8)
}
/// Truncates string to specified size (ignoring last bytes if they form a partial `char`)
#[inline]
pub(crate) fn truncate_str(slice: &str, size: u8) -> &str {
trace!("Truncate str: {} at {}", slice, size);
if slice.is_char_boundary(size.into()) {
unsafe { slice.get_unchecked(..size.into()) }
} else if (size as usize) < slice.len() {
let mut index = size.saturating_sub(1) as usize;
while !slice.is_char_boundary(index) {
index = index.saturating_sub(1);
}
unsafe { slice.get_unchecked(..index) }
} else {
slice
}
}
impl IntoLossy<u8> for usize {
#[allow(clippy::cast_possible_truncation)]
#[inline]
fn into_lossy(self) -> u8 {
self as u8
}
}
impl IntoLossy<u8> for u32 {
#[allow(clippy::cast_possible_truncation)]
#[inline]
fn into_lossy(self) -> u8 {
self as u8
}
}
#[cfg(test)]
mod tests {
use super::*;
use core::str::from_utf8;
#[test]
fn truncate() {
assert_eq!(truncate_str("i", 10), "i");
assert_eq!(truncate_str("iiiiii", 3), "iii");
assert_eq!(truncate_str("🤔🤔🤔", 5), "🤔");
}
#[test]
fn shift_right() {
let _ = env_logger::try_init();
let mut ls = SmallString::try_from_str("abcdefg").unwrap();
unsafe { shift_right_unchecked(&mut ls, 0u8, 4u8) };
ls.size += 4;
assert_eq!(ls.as_str(), "abcdabcdefg");
}
#[test]
fn shift_left() {
let _ = env_logger::try_init();
let mut ls = SmallString::try_from_str("abcdefg").unwrap();
unsafe { shift_left_unchecked(&mut ls, 1u8, 0u8) };
ls.size -= 1;
assert_eq!(ls.as_str(), "bcdefg");
}
#[test]
fn shift_nop() {
let _ = env_logger::try_init();
let mut ls = SmallString::try_from_str("abcdefg").unwrap();
unsafe { shift_right_unchecked(&mut ls, 0u8, 0u8) };
assert_eq!(ls.as_str(), "abcdefg");
unsafe { shift_left_unchecked(&mut ls, 0u8, 0u8) };
assert_eq!(ls.as_str(), "abcdefg");
}
#[test]
fn encode_char_utf8() {
let _ = env_logger::try_init();
let mut string = SmallString::default();
unsafe {
encode_char_utf8_unchecked(&mut string, 'a', 0);
assert_eq!(from_utf8(&string.array.as_mut_slice()[..1]).unwrap(), "a");
let mut string = SmallString::try_from_str("a").unwrap();
encode_char_utf8_unchecked(&mut string, '🤔', 1);
assert_eq!(
from_utf8(&string.array.as_mut_slice()[..5]).unwrap(),
"a🤔"
);
}
}
}

655
third_party/rust/arraystring/tests/string_parity.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,655 @@
use arraystring::{error::Error, prelude::*, utils::is_char_boundary, utils::is_inside_boundary};
use std::panic::{catch_unwind, AssertUnwindSafe, RefUnwindSafe};
use std::{fmt::Debug, iter::FromIterator};
fn unwind<R, F>(func: F) -> Result<R, ()>
where
F: FnOnce() -> R,
{
catch_unwind(AssertUnwindSafe(func)).map_err(|_| ())
}
static STRINGS: [&'static str; 8] = [
"🤔🤔🤔🤔🤔🤔🤔",
"ABCDEFGHIJKLMNOPQRSASHUDAHSDIUASH ",
"iejueueheuheuheu 0",
"",
"1",
"ab",
" ",
" 899saH(8hadhaiuhsidnkandu",
];
#[test]
fn try_from_str() {
assert(String::from, MaxString::try_from_str);
}
#[test]
fn from_str_truncate() {
assert(String::from, MaxString::from_str_truncate);
}
#[test]
fn from_str_unchecked() {
assert(String::from, |s| unsafe {
MaxString::from_str_unchecked(s)
});
}
#[test]
fn try_from_chars() {
assert(
|s| String::from_iter(s.chars()),
|s| MaxString::try_from_chars(s.chars()),
);
}
#[test]
fn from_chars() {
assert(
|s| String::from_iter(s.chars()),
|s| MaxString::from_chars(s.chars()),
);
}
#[test]
fn from_chars_unchecked() {
assert(
|s| String::from_iter(s.chars()),
|s| unsafe { MaxString::from_chars_unchecked(s.chars()) },
);
}
#[test]
fn try_from_iter() {
assert(
|s| String::from_iter(vec![s]),
|s| MaxString::try_from_iterator(vec![s]),
);
}
#[test]
fn from_iter() {
assert(
|s| String::from_iter(vec![s]),
|s| MaxString::from_iterator(vec![s]),
);
}
#[test]
fn from_iter_unchecked() {
assert(
|s| String::from_iter(vec![s]),
|s| unsafe { MaxString::from_iterator_unchecked(vec![s]) },
);
}
#[test]
fn try_from_utf8() {
assert(
|s| String::from_utf8(s.as_bytes().to_vec()),
|s| MaxString::try_from_utf8(s.as_bytes()),
);
}
#[test]
fn from_utf8() {
assert(
|s| String::from_utf8(s.as_bytes().to_vec()),
|s| MaxString::from_utf8(s.as_bytes()),
);
}
#[inline]
fn invalidate_utf8(buf: &mut [u8]) -> &mut [u8] {
if buf.len() >= 4 {
buf[0] = 0;
buf[1] = 159;
buf[2] = 146;
buf[3] = 150;
}
buf
}
#[test]
fn try_from_utf8_invalid() {
unsafe {
assert(
|s| String::from_utf8(invalidate_utf8(s.to_owned().as_bytes_mut()).to_vec()),
|s| MaxString::try_from_utf8(invalidate_utf8(s.to_owned().as_bytes_mut())),
);
}
}
#[test]
fn from_utf8_invalid() {
unsafe {
assert(
|s| String::from_utf8(invalidate_utf8(s.to_owned().as_bytes_mut()).to_vec()),
|s| MaxString::from_utf8(invalidate_utf8(s.to_owned().as_bytes_mut())),
);
}
}
#[test]
fn try_from_utf16() {
let utf16 = |s: &str| s.encode_utf16().collect::<Vec<_>>();
assert(
|s| String::from_utf16(&utf16(s)),
|s| MaxString::try_from_utf16(&utf16(s)),
);
}
#[test]
fn from_utf16() {
let utf16 = |s: &str| s.encode_utf16().collect::<Vec<_>>();
assert(
|s| String::from_utf16(&utf16(s)),
|s| MaxString::from_utf16(&utf16(s)),
);
}
#[test]
fn from_utf16_lossy() {
let utf16 = |s: &str| s.encode_utf16().collect::<Vec<_>>();
assert(
|s| String::from_utf16(&utf16(s)),
|s| MaxString::from_utf16(&utf16(s)),
);
}
fn invalidate_utf16(buf: &mut [u16]) -> &mut [u16] {
if buf.len() >= 7 {
buf[0] = 0xD834;
buf[1] = 0xDD1E;
buf[2] = 0x006D;
buf[3] = 0x0075;
buf[4] = 0xD800;
buf[5] = 0x0069;
buf[6] = 0x0063;
}
buf
}
#[test]
fn try_from_utf16_invalid() {
let utf16 = |s: &str| s.encode_utf16().collect::<Vec<_>>();
assert(
|s| String::from_utf16(invalidate_utf16(&mut utf16(s))),
|s| MaxString::try_from_utf16(invalidate_utf16(&mut utf16(s))),
);
}
#[test]
fn from_utf16_invalid() {
let utf16 = |s: &str| s.encode_utf16().collect::<Vec<_>>();
assert(
|s| String::from_utf16(invalidate_utf16(&mut utf16(s))),
|s| MaxString::from_utf16(invalidate_utf16(&mut utf16(s))),
);
}
#[test]
fn from_utf16_lossy_invalid() {
let utf16 = |s: &str| s.encode_utf16().collect::<Vec<_>>();
assert(
|s| String::from_utf16(invalidate_utf16(&mut utf16(s))),
|s| MaxString::from_utf16(invalidate_utf16(&mut utf16(s))),
);
}
#[test]
fn from_utf8_unchecked() {
unsafe {
assert(
|s| String::from_utf8_unchecked(s.as_bytes().to_vec()),
|s| MaxString::from_utf8_unchecked(s.as_bytes()),
);
}
}
#[test]
fn try_push_str() {
assert(
|s| {
let mut st = String::from(s);
st.push_str(s);
st
},
|s| {
let mut ms = MaxString::try_from_str(s).unwrap();
ms.try_push_str(s).map(|()| ms)
},
);
}
#[test]
fn push_str() {
assert(
|s| {
let mut st = String::from(s);
st.push_str(s);
st
},
|s| {
let mut ms = MaxString::try_from_str(s).unwrap();
ms.push_str(s);
ms
},
);
}
#[test]
fn add_str() {
assert(
|s| String::from(s) + s,
|s| MaxString::try_from_str(s).unwrap() + s,
);
}
#[test]
fn push_str_unchecked() {
assert(
|s| {
let mut st = String::from(s);
st.push_str(s);
st
},
|s| {
let mut ms = MaxString::try_from_str(s).unwrap();
unsafe { ms.push_str_unchecked(s) };
ms
},
);
}
#[test]
fn push() {
assert(
|s| {
let mut s = String::from(s);
s.push('🤔');
s
},
|s| {
let mut ms = MaxString::try_from_str(s).unwrap();
ms.try_push('🤔').map(|()| ms)
},
);
}
#[test]
fn push_unchecked() {
assert(
|s| {
let mut s = String::from(s);
s.push('🤔');
s
},
|s| {
let mut ms = MaxString::try_from_str(s).unwrap();
unsafe { ms.push_unchecked('🤔') };
ms
},
);
}
#[test]
fn truncate() {
assert(
|s| {
unwind(move || {
let mut s = String::from(s);
s.truncate(2);
s
})
},
|s| {
let mut ms = MaxString::try_from_str(s).unwrap();
ms.truncate(2).map(|()| ms)
},
);
}
#[test]
fn pop() {
assert(
|s| {
let mut s = String::from(s);
let old = s.pop();
(s, old)
},
|s| {
let mut ms = MaxString::try_from_str(s).unwrap();
let old = ms.pop();
(ms, old)
},
);
}
#[test]
fn trim() {
assert(
|s| String::from(s).trim().to_owned(),
|s| {
let mut ms = MaxString::try_from_str(s).unwrap();
ms.trim();
ms
},
);
}
#[test]
fn remove() {
assert(
|s| {
unwind(move || {
let mut s = String::from(s);
let removed = s.remove(2);
(removed, s)
})
},
|s| {
let mut ms = MaxString::try_from_str(s).unwrap();
ms.remove(2).map(|r| (r, ms))
},
);
}
#[test]
fn retain() {
assert(
|s| {
let mut s = String::from(s);
s.retain(|c| c == 'a');
s
},
|s| {
let mut ms = MaxString::try_from_str(s).unwrap();
ms.retain(|c| c == 'a');
ms
},
);
}
#[test]
fn try_insert() {
assert(
|s| {
unwind(move || {
let mut s = String::from(s);
s.insert(2, 'a');
s
})
},
|s| {
let mut ms = MaxString::try_from_str(s).unwrap();
ms.try_insert(2, 'a').map(|()| ms)
},
);
}
#[test]
fn insert_unchecked() {
assert(
|s| {
unwind(move || {
let mut s = String::from(s);
s.insert(2, 'a');
s
})
},
|s| {
let mut ms = MaxString::try_from_str(s).unwrap();
is_inside_boundary(2u8, ms.len())
.map_err(Error::from)
.and_then(|()| Ok(is_char_boundary(&ms, 2)?))
.map(|()| unsafe { ms.insert_unchecked(2, 'a') })
.map(|()| ms)
},
);
}
#[test]
fn try_insert_str() {
assert(
|s| {
unwind(move || {
let mut st = String::from(s);
st.insert_str(2, s);
st
})
},
|s| {
let mut ms = MaxString::try_from_str(s).unwrap();
ms.try_insert_str(2, s).map(|()| ms)
},
);
}
#[test]
fn insert_str() {
assert(
|s| {
unwind(move || {
let mut st = String::from(s);
st.insert_str(2, s);
(st, ())
})
},
|s| {
let mut ms = MaxString::try_from_str(s).unwrap();
let res = ms.insert_str(2, s);
res.map(|()| (ms, ()))
},
);
}
#[test]
fn insert_str_unchecked() {
assert(
|s| {
unwind(move || {
let mut st = String::from(s);
st.insert_str(2, s);
st
})
},
|s| {
let mut ms = MaxString::try_from_str(s).unwrap();
is_inside_boundary(2u8, ms.len())
.map_err(Error::from)
.and_then(|()| Ok(is_char_boundary(&ms, 2)?))
.map(|()| unsafe { ms.insert_str_unchecked(2, s) })
.map(|()| ms)
},
);
}
#[test]
fn clear() {
assert(
|s| {
let mut st = String::from(s);
st.clear();
st
},
|s| {
let mut ms = MaxString::try_from_str(s).unwrap();
ms.clear();
ms
},
);
}
#[test]
fn split_off() {
assert(
|s| {
unwind(move || {
let mut st = String::from(s);
let split = st.split_off(2);
(st, split)
})
},
|s| {
let mut ms = MaxString::try_from_str(s).unwrap();
ms.split_off(2).map(|s| (ms, s))
},
);
}
#[test]
fn drain() {
assert(
|s| {
unwind(move || {
let mut st = String::from(s);
let drained: String = st.drain(..2).collect();
(st, drained)
})
},
|s| {
let mut ms = MaxString::try_from_str(s).unwrap();
let drained = ms.drain(..2).map(|d| d.collect::<String>());
drained.map(|d| (ms, d))
},
);
}
#[test]
fn replace_range() {
assert(
|s| {
unwind(move || {
let mut st = String::from(s);
st.replace_range(..2, s);
(st, ())
})
},
|s| {
let mut ms = MaxString::try_from_str(s).unwrap();
ms.replace_range(..2, s).map(|()| (ms, ()))
},
);
}
#[test]
fn len() {
assert(
|s| {
let st = String::from(s);
st.len().to_string()
},
|s| {
let ms = MaxString::try_from_str(s).unwrap();
ms.len().to_string()
},
);
}
#[test]
fn is_empty() {
assert(
|s| {
let st = String::from(s);
st.is_empty().to_string()
},
|s| {
let ms = MaxString::try_from_str(s).unwrap();
ms.is_empty().to_string()
},
);
}
#[test]
fn new() {
assert_eq!(String::new().as_str(), MaxString::new().as_str());
}
// Internal hackery to make the function `assert` possible
trait Normalize<EQ: PartialEq> {
fn normalize(&self) -> EQ;
}
impl Normalize<Result<String, ()>> for () {
fn normalize(&self) -> Result<String, ()> {
Ok("".to_owned())
}
}
impl Normalize<Result<String, ()>> for MaxString {
fn normalize(&self) -> Result<String, ()> {
Ok(self.as_str().to_owned())
}
}
impl Normalize<Result<String, ()>> for String {
fn normalize(&self) -> Result<String, ()> {
Ok(self.as_str().to_owned())
}
}
impl<'a> Normalize<Result<String, ()>> for &'a str {
fn normalize(&self) -> Result<String, ()> {
Ok(self.to_string())
}
}
impl Normalize<Result<String, ()>> for char {
fn normalize(&self) -> Result<String, ()> {
Ok(self.to_string())
}
}
impl<N: Normalize<Result<String, ()>>> Normalize<Result<String, ()>> for Option<N> {
fn normalize(&self) -> Result<String, ()> {
self.as_ref().ok_or(()).and_then(|n| n.normalize())
}
}
impl<E, K: PartialEq, N: Normalize<Result<K, ()>>> Normalize<Result<K, ()>> for Result<N, E> {
fn normalize(&self) -> Result<K, ()> {
self.as_ref().map_err(|_| ()).and_then(|n| n.normalize())
}
}
impl<K: PartialEq, L: PartialEq, M: Normalize<K>, N: Normalize<L>> Normalize<Result<(K, L), ()>>
for (M, N)
{
fn normalize(&self) -> Result<(K, L), ()> {
Ok((self.0.normalize(), self.1.normalize()))
}
}
impl<J, K, L, M, N, O> Normalize<Result<(J, K, L), ()>> for (M, N, O)
where
J: PartialEq,
K: PartialEq,
L: PartialEq,
M: Normalize<J>,
N: Normalize<K>,
O: Normalize<L>,
{
fn normalize(&self) -> Result<(J, K, L), ()> {
Ok((self.0.normalize(), self.1.normalize(), self.2.normalize()))
}
}
fn assert<Q, F, G, T, U>(f: F, g: G)
where
Q: PartialEq + Debug,
T: Normalize<Q>,
U: Normalize<Q>,
F: Fn(&'static str) -> T + RefUnwindSafe,
G: Fn(&'static str) -> U + RefUnwindSafe,
{
let _ = env_logger::try_init();
for string in STRINGS.into_iter() {
let f = f(string).normalize();
let g = g(string).normalize();
assert_eq!(f, g);
}
}

Просмотреть файл

@ -149,8 +149,7 @@ export class IdentityCredentialPromptService {
for (const [providerIndex, provider] of identityProviders.entries()) {
let providerURL = new URL(provider.configURL);
let displayDomain = lazy.IDNService.convertToDisplayIDN(
providerURL.host,
{}
providerURL.host
);
let iconResult = iconResults[providerIndex];
@ -204,10 +203,7 @@ export class IdentityCredentialPromptService {
);
for (const [providerIndex, provider] of identityProviders.entries()) {
let providerURL = new URL(provider.configURL);
let displayDomain = lazy.IDNService.convertToDisplayIDN(
providerURL.host,
{}
);
let displayDomain = lazy.IDNService.convertToDisplayIDN(providerURL.host);
let newItem = itemTemplate.content.firstElementChild.cloneNode(true);
// Create the radio button,
@ -358,8 +354,7 @@ export class IdentityCredentialPromptService {
let providerURL = new URL(identityProvider.configURL);
let providerDisplayDomain = lazy.IDNService.convertToDisplayIDN(
providerURL.host,
{}
providerURL.host
);
let currentBaseDomain =
browsingContext.currentWindowContext.documentPrincipal.baseDomain;
@ -540,10 +535,7 @@ export class IdentityCredentialPromptService {
);
const providerName = providerManifest?.branding?.name;
let providerURL = new URL(provider.configURL);
let displayDomain = lazy.IDNService.convertToDisplayIDN(
providerURL.host,
{}
);
let displayDomain = lazy.IDNService.convertToDisplayIDN(providerURL.host);
let headerIconResult = await this.loadIconFromManifest(
providerManifest,

Просмотреть файл

@ -990,7 +990,7 @@ LoginManagerAuthPrompter.prototype = {
try {
var uri = Services.io.newURI(aURIString);
var baseDomain = Services.eTLD.getBaseDomain(uri);
displayHost = idnService.convertToDisplayIDN(baseDomain, {});
displayHost = idnService.convertToDisplayIDN(baseDomain);
} catch (e) {
this.log(`Couldn't process supplied URIString ${aURIString}.`);
}

Просмотреть файл

@ -994,7 +994,7 @@ export class LoginManagerPrompter {
try {
const uri = Services.io.newURI(aURIString);
const baseDomain = Services.eTLD.getBaseDomain(uri);
displayHost = idnService.convertToDisplayIDN(baseDomain, {});
displayHost = idnService.convertToDisplayIDN(baseDomain);
} catch (e) {
lazy.log.warn(`Couldn't process supplied URIString: ${aURIString}`);
}

Просмотреть файл

@ -53,6 +53,7 @@ mapped_hyph = { git = "https://github.com/jfkthame/mapped_hyph.git", rev = "c765
fog_control = { path = "../../../components/glean" }
app_services_logger = { path = "../../../../services/common/app_services_logger" }
http_sfv = { path = "../../../../netwerk/base/http-sfv" }
idna_glue = { path = "../../../../netwerk/base/idna_glue" }
unic-langid = { version = "0.9", features = ["likelysubtags"] }
unic-langid-ffi = { path = "../../../../intl/locale/rust/unic-langid-ffi" }
fluent-langneg = { version = "0.13", features = ["cldr"] }

Просмотреть файл

@ -31,6 +31,7 @@ extern crate fog_control;
extern crate gecko_profiler;
extern crate gkrust_utils;
extern crate http_sfv;
extern crate idna_glue;
extern crate jog;
extern crate jsrust_shared;
extern crate kvstore;

Просмотреть файл

@ -430,8 +430,8 @@ export var DownloadUtils = {
// This might fail if it's an IP address or doesn't have more than 1 part
let baseDomain = Services.eTLD.getBaseDomain(uri);
// Convert base domain for display; ignore the isAscii out param
displayHost = idnService.convertToDisplayIDN(baseDomain, {});
// Convert base domain for display
displayHost = idnService.convertToDisplayIDN(baseDomain);
} catch (e) {
// Default to the host name
displayHost = fullHost;