Update googleurl to the version from Sat Nov 5 08:06:16 2022 This is corresponding to the Chromium commit 9880a0d69c8b70d4cd18f6705afcc4f73aba0b9b. In this CL, I have also finally mocked out raw_ptr with a typedef, since it keeps breaking every roll, and supporting it seems untenable. Change-Id: Ia9b0c01d961a9586e1642df3dc27ce4573f6d484
diff --git a/AUTHORS b/AUTHORS index 59ecce0..b32867a 100644 --- a/AUTHORS +++ b/AUTHORS
@@ -134,6 +134,7 @@ Arnaud Renevier <a.renevier@samsung.com> Arpita Bahuguna <a.bah@samsung.com> Arthur Lussos <developer0420@gmail.com> +Artur Akerberg <artur.aker@gmail.com> Arun Kulkarni <kulkarni.a@samsung.com> Arun Kumar <arun87.kumar@samsung.com> Arun Mankuzhi <arun.m@samsung.com> @@ -514,6 +515,7 @@ Jan Keitel <jan.keitel@gmail.com> Jan Rucka <ruckajan10@gmail.com> Jan Sauer <jan@jansauer.de> +Jan-Michael Brummer <jan.brummer@tabos.org> Janusz Majnert <jmajnert@gmail.com> Janwar Dinata <j.dinata@gmail.com> Jared Shumway <jaredshumway94@gmail.com> @@ -598,6 +600,7 @@ Jongheon Kim <sapzape@gmail.com> JongKwon Lee <jongkwon.lee@navercorp.com> Jongmok Kim <jongmok.kim@navercorp.com> +Jongmok Kim <johny.kimc@gmail.com> Jongsoo Lee <leejongsoo@gmail.com> Joone Hur <joone.hur@intel.com> Joonghun Park <pjh0718@gmail.com> @@ -1319,6 +1322,7 @@ Yizhou Jiang <yizhou.jiang@intel.com> Yoav Weiss <yoav@yoav.ws> Yoav Zilberberg <yoav.zilberberg@gmail.com> +Yoichiro Hibara <hibarayoichiro871@gmail.com> Yong Ling <yongling@tencent.com> Yong Shin <sy3620@gmail.com> Yong Wang <ccyongwang@tencent.com> @@ -1403,8 +1407,9 @@ Endless Mobile, Inc. <*@endlessm.com> EngFlow, Inc. <*@engflow.com> Estimote, Inc. <*@estimote.com> -Facebook, Inc. <*@fb.com> -Facebook, Inc. <*@oculus.com> +Meta Platforms, Inc. <*@fb.com> +Meta Platforms, Inc. <*@meta.com> +Meta Platforms, Inc. <*@oculus.com> Google Inc. <*@google.com> Grammarly, Inc. <*@grammarly.com> Hewlett-Packard Development Company, L.P. <*@hp.com> @@ -1446,6 +1451,7 @@ Tableau Software <*@tableau.com> Talon Cyber Security Ltd. <*@talon-sec.com> TeamSpeak Systems GmbH <*@teamspeak.com> +The Browser Company <*@thebrowser.company> The Chromium Authors <*@chromium.org> The MathWorks, Inc. <binod.pant@mathworks.com> THEO Technologies <*@theoplayer.com>
diff --git a/README.md b/README.md index 40fd7f1..5965c48 100644 --- a/README.md +++ b/README.md
@@ -17,8 +17,8 @@ the following commands in the root of the checkout: 1. `copybara copy.bara.sky import <path-to-chrome>/src --folder-dir .` -1. `bazel test --cxxopt="-std=c++14" //...` - (C++14 is replacible with later C++ versions) +1. `bazel test --cxxopt="-std=c++17" //...` + (C++17 is replacible with later C++ versions) 1. Fix all of the compilation errors, potentially modifying the BUILD files and the polyfill headers in `polyfill/` as appropriate. 1. Check the new version into Git.
diff --git a/base/BUILD b/base/BUILD index 32c3b9c..a1410fb 100644 --- a/base/BUILD +++ b/base/BUILD
@@ -32,7 +32,6 @@ "functional/identity.h", "functional/invoke.h", "functional/not_fn.h", - "memory/raw_ptr.h", "memory/raw_ptr_exclusion.h", "no_destructor.h", "numerics/checked_math.h", @@ -53,6 +52,7 @@ "strings/string_piece_forward.h", "strings/string_piece.h", "strings/string_util.h", + "strings/string_util_impl_helpers.h", "strings/string_util_internal.h", "strings/string_number_conversions.h", "strings/utf_string_conversions.h",
diff --git a/base/cxx20_to_address.h b/base/cxx20_to_address.h index ef67a9b..04d0f71 100644 --- a/base/cxx20_to_address.h +++ b/base/cxx20_to_address.h
@@ -5,10 +5,24 @@ #ifndef BASE_CXX20_TO_ADDRESS_H_ #define BASE_CXX20_TO_ADDRESS_H_ +#include <memory> #include <type_traits> namespace gurl_base { +namespace { + +template <typename Ptr, typename = void> +struct has_std_to_address : std::false_type {}; + +template <typename Ptr> +struct has_std_to_address< + Ptr, + std::void_t<decltype(std::pointer_traits<Ptr>::to_address( + std::declval<Ptr>()))>> : std::true_type {}; + +} // namespace + // Implementation of C++20's std::to_address. // Note: This does consider specializations of pointer_traits<>::to_address, // even though it's a C++20 member function, because CheckedContiguousIterator @@ -23,14 +37,12 @@ } template <typename Ptr> -constexpr auto to_address(const Ptr& p) noexcept - -> decltype(std::pointer_traits<Ptr>::to_address(p)) { - return std::pointer_traits<Ptr>::to_address(p); -} - -template <typename Ptr, typename... None> -constexpr auto to_address(const Ptr& p, None...) noexcept { - return gurl_base::to_address(p.operator->()); +constexpr auto to_address(const Ptr& p) noexcept { + if constexpr (has_std_to_address<Ptr>::value) { + return std::pointer_traits<Ptr>::to_address(p); + } else { + return gurl_base::to_address(p.operator->()); + } } } // namespace base
diff --git a/base/debug/crash_logging.cc b/base/debug/crash_logging.cc index 157a79f..68d1c96 100644 --- a/base/debug/crash_logging.cc +++ b/base/debug/crash_logging.cc
@@ -4,6 +4,8 @@ #include "base/debug/crash_logging.h" +#include <ostream> + #include "base/strings/string_piece.h" #include "build/build_config.h"
diff --git a/base/debug/crash_logging.h b/base/debug/crash_logging.h index b4cf7bc..ec1df77 100644 --- a/base/debug/crash_logging.h +++ b/base/debug/crash_logging.h
@@ -12,7 +12,7 @@ #include <type_traits> #include "polyfills/base/base_export.h" -#include "base/memory/raw_ptr.h" +#include "polyfills/base/memory/raw_ptr.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_piece.h" @@ -48,7 +48,7 @@ // // static auto* const crash_key = gurl_base::debug::AllocateCrashKeyString( // "name", gurl_base::debug::CrashKeySize::Size32); -// gurl_base::debug::SetCrashKeyString(crash_key); +// gurl_base::debug::SetCrashKeyString(crash_key, "value"); // // // Do other work before calling `gurl_base::debug::DumpWithoutCrashing()` later. //
diff --git a/base/memory/raw_ptr.h b/base/memory/raw_ptr.h deleted file mode 100644 index d65ab44..0000000 --- a/base/memory/raw_ptr.h +++ /dev/null
@@ -1,1510 +0,0 @@ -// Copyright 2020 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_MEMORY_RAW_PTR_H_ -#define BASE_MEMORY_RAW_PTR_H_ - -#include <stddef.h> -#include <stdint.h> - -#include <climits> -#include <cstddef> -#include <functional> -#include <type_traits> -#include <utility> - -#include "polyfills/base/allocator/buildflags.h" - -#include "polyfills/base/check.h" -#include "base/compiler_specific.h" -#include "polyfills/base/dcheck_is_on.h" -#include "polyfills/third_party/perfetto/include/perfetto/tracing/traced_value.h" -#include "build/build_config.h" -#include "build/buildflag.h" - -#if BUILDFLAG(USE_BACKUP_REF_PTR) || \ - defined(PA_ENABLE_MTE_CHECKED_PTR_SUPPORT_WITH_64_BITS_POINTERS) -// USE_BACKUP_REF_PTR implies USE_PARTITION_ALLOC, needed for code under -// allocator/partition_allocator/ to be built. -#include "base/allocator/partition_allocator/address_pool_manager_bitmap.h" -#include "base/allocator/partition_allocator/partition_address_space.h" -#include "base/allocator/partition_allocator/partition_alloc_constants.h" -#include "polyfills/base/base_export.h" -#endif // BUILDFLAG(USE_BACKUP_REF_PTR) || - // defined(PA_ENABLE_MTE_CHECKED_PTR_SUPPORT_WITH_64_BITS_POINTERS) - -#if defined(PA_ENABLE_MTE_CHECKED_PTR_SUPPORT_WITH_64_BITS_POINTERS) -#include "base/allocator/partition_allocator/partition_tag.h" -#include "base/allocator/partition_allocator/partition_tag_types.h" -#include "base/allocator/partition_allocator/tagging.h" -#include "polyfills/base/check_op.h" -#endif // defined(PA_ENABLE_MTE_CHECKED_PTR_SUPPORT_WITH_64_BITS_POINTERS) - -#if BUILDFLAG(IS_WIN) -#include "base/win/win_handle_types.h" -#endif - -namespace cc { -class Scheduler; -} -namespace gurl_base::internal { -class DelayTimerBase; -} -namespace content::responsiveness { -class Calculator; -} - -namespace gurl_base { - -// NOTE: All methods should be `ALWAYS_INLINE`. raw_ptr is meant to be a -// lightweight replacement of a raw pointer, hence performance is critical. - -// The following types are the different RawPtrType template option possible for -// a `raw_ptr`: -// - RawPtrMayDangle disables dangling pointers check when the object is -// released. -// - RawPtrBanDanglingIfSupported may enable dangling pointers check on object -// destruction. -// -// We describe those types here so that they can be used outside of `raw_ptr` as -// object markers, and their meaning might vary depending on where those markers -// are being used. For instance, we are using those in `UnretainedWrapper` to -// change behavior depending on RawPtrType. -struct RawPtrMayDangle {}; -struct RawPtrBanDanglingIfSupported {}; - -namespace raw_ptr_traits { -template <typename T> -struct RawPtrTypeToImpl; -} - -namespace internal { -// These classes/structures are part of the raw_ptr implementation. -// DO NOT USE THESE CLASSES DIRECTLY YOURSELF. - -// This type trait verifies a type can be used as a pointer offset. -// -// We support pointer offsets in signed (ptrdiff_t) or unsigned (size_t) values. -// Smaller types are also allowed. -template <typename Z> -static constexpr bool offset_type = - std::is_integral_v<Z> && sizeof(Z) <= sizeof(ptrdiff_t); - -struct RawPtrNoOpImpl { - // Wraps a pointer. - template <typename T> - static ALWAYS_INLINE T* WrapRawPtr(T* ptr) { - return ptr; - } - - // Notifies the allocator when a wrapped pointer is being removed or replaced. - template <typename T> - static ALWAYS_INLINE void ReleaseWrappedPtr(T*) {} - - // Unwraps the pointer, while asserting that memory hasn't been freed. The - // function is allowed to crash on nullptr. - template <typename T> - static ALWAYS_INLINE T* SafelyUnwrapPtrForDereference(T* wrapped_ptr) { - return wrapped_ptr; - } - - // Unwraps the pointer, while asserting that memory hasn't been freed. The - // function must handle nullptr gracefully. - template <typename T> - static ALWAYS_INLINE T* SafelyUnwrapPtrForExtraction(T* wrapped_ptr) { - return wrapped_ptr; - } - - // Unwraps the pointer, without making an assertion on whether memory was - // freed or not. - template <typename T> - static ALWAYS_INLINE T* UnsafelyUnwrapPtrForComparison(T* wrapped_ptr) { - return wrapped_ptr; - } - - // Upcasts the wrapped pointer. - template <typename To, typename From> - static ALWAYS_INLINE constexpr To* Upcast(From* wrapped_ptr) { - static_assert(std::is_convertible<From*, To*>::value, - "From must be convertible to To."); - // Note, this cast may change the address if upcasting to base that lies in - // the middle of the derived object. - return wrapped_ptr; - } - - // Advance the wrapped pointer by `delta_elems`. - template <typename T, - typename Z, - typename = std::enable_if_t<offset_type<Z>, void>> - static ALWAYS_INLINE T* Advance(T* wrapped_ptr, Z delta_elems) { - return wrapped_ptr + delta_elems; - } - - template <typename T> - static ALWAYS_INLINE ptrdiff_t GetDeltaElems(T* wrapped_ptr1, - T* wrapped_ptr2) { - return wrapped_ptr1 - wrapped_ptr2; - } - - // Returns a copy of a wrapped pointer, without making an assertion on whether - // memory was freed or not. - template <typename T> - static ALWAYS_INLINE T* Duplicate(T* wrapped_ptr) { - return wrapped_ptr; - } - - // This is for accounting only, used by unit tests. - static ALWAYS_INLINE void IncrementSwapCountForTest() {} - static ALWAYS_INLINE void IncrementLessCountForTest() {} - static ALWAYS_INLINE void IncrementPointerToMemberOperatorCountForTest() {} -}; - -#if defined(PA_ENABLE_MTE_CHECKED_PTR_SUPPORT_WITH_64_BITS_POINTERS) - -constexpr int kValidAddressBits = 48; -constexpr uintptr_t kAddressMask = (1ull << kValidAddressBits) - 1; -constexpr int kTagBits = sizeof(uintptr_t) * 8 - kValidAddressBits; - -// MTECheckedPtr has no business with the topmost bits reserved for the -// tag used by true ARM MTE, so we strip it out here. -constexpr uintptr_t kTagMask = - ~kAddressMask & partition_alloc::internal::kPtrUntagMask; - -constexpr int kTopBitShift = 63; -constexpr uintptr_t kTopBit = 1ull << kTopBitShift; -static_assert(kTopBit << 1 == 0, "kTopBit should really be the top bit"); -static_assert((kTopBit & kTagMask) > 0, - "kTopBit bit must be inside the tag region"); - -// This functionality is outside of MTECheckedPtrImpl, so that it can be -// overridden by tests. -struct MTECheckedPtrImplPartitionAllocSupport { - // Checks if the necessary support is enabled in PartitionAlloc for `ptr`. - template <typename T> - static ALWAYS_INLINE bool EnabledForPtr(T* ptr) { - // Disambiguation: UntagPtr removes the hardware MTE tag, whereas this class - // is responsible for handling the software MTE tag. - auto addr = partition_alloc::UntagPtr(ptr); - return partition_alloc::IsManagedByPartitionAlloc(addr); - } - - // Returns pointer to the tag that protects are pointed by |addr|. - static ALWAYS_INLINE void* TagPointer(uintptr_t addr) { - return partition_alloc::PartitionTagPointer(addr); - } -}; - -template <typename PartitionAllocSupport> -struct MTECheckedPtrImpl { - // This implementation assumes that pointers are 64 bits long and at least 16 - // top bits are unused. The latter is harder to verify statically, but this is - // true for all currently supported 64-bit architectures (GURL_DCHECK when wrapping - // will verify that). - static_assert(sizeof(void*) >= 8, "Need 64-bit pointers"); - - // Wraps a pointer, and returns its uintptr_t representation. - template <typename T> - static ALWAYS_INLINE T* WrapRawPtr(T* ptr) { - // Disambiguation: UntagPtr removes the hardware MTE tag, whereas this - // function is responsible for adding the software MTE tag. - uintptr_t addr = partition_alloc::UntagPtr(ptr); - GURL_DCHECK_EQ(ExtractTag(addr), 0ull); - - // Return a not-wrapped |addr|, if it's either nullptr or if the protection - // for this pointer is disabled. - if (!PartitionAllocSupport::EnabledForPtr(ptr)) { - return ptr; - } - - // Read the tag and place it in the top bits of the address. - // Even if PartitionAlloc's tag has less than kTagBits, we'll read - // what's given and pad the rest with 0s. - static_assert(sizeof(partition_alloc::PartitionTag) * 8 <= kTagBits, ""); - uintptr_t tag = *(static_cast<volatile partition_alloc::PartitionTag*>( - PartitionAllocSupport::TagPointer(addr))); - GURL_DCHECK(tag); - - tag <<= kValidAddressBits; - addr |= tag; - // See the disambiguation comment above. - // TODO(kdlee): Ensure that ptr's hardware MTE tag is preserved. - // TODO(kdlee): Ensure that hardware and software MTE tags don't conflict. - return static_cast<T*>(partition_alloc::internal::TagAddr(addr)); - } - - // Notifies the allocator when a wrapped pointer is being removed or replaced. - // No-op for MTECheckedPtrImpl. - template <typename T> - static ALWAYS_INLINE void ReleaseWrappedPtr(T*) {} - - // Unwraps the pointer's uintptr_t representation, while asserting that memory - // hasn't been freed. The function is allowed to crash on nullptr. - template <typename T> - static ALWAYS_INLINE T* SafelyUnwrapPtrForDereference(T* wrapped_ptr) { - // Disambiguation: UntagPtr removes the hardware MTE tag, whereas this - // function is responsible for removing the software MTE tag. - uintptr_t wrapped_addr = partition_alloc::UntagPtr(wrapped_ptr); - uintptr_t tag = ExtractTag(wrapped_addr); - if (tag > 0) { - // Read the tag provided by PartitionAlloc. - // - // Cast to volatile to ensure memory is read. E.g. in a tight loop, the - // compiler could cache the value in a register and thus could miss that - // another thread freed memory and changed tag. - uintptr_t read_tag = - *static_cast<volatile partition_alloc::PartitionTag*>( - PartitionAllocSupport::TagPointer(ExtractAddress(wrapped_addr))); - if (UNLIKELY(tag != read_tag)) - IMMEDIATE_CRASH(); - // See the disambiguation comment above. - // TODO(kdlee): Ensure that ptr's hardware MTE tag is preserved. - // TODO(kdlee): Ensure that hardware and software MTE tags don't conflict. - return static_cast<T*>( - partition_alloc::internal::TagAddr(ExtractAddress(wrapped_addr))); - } - return wrapped_ptr; - } - - // Unwraps the pointer's uintptr_t representation, while asserting that memory - // hasn't been freed. The function must handle nullptr gracefully. - template <typename T> - static ALWAYS_INLINE T* SafelyUnwrapPtrForExtraction(T* wrapped_ptr) { - // SafelyUnwrapPtrForDereference handles nullptr case well. - return SafelyUnwrapPtrForDereference(wrapped_ptr); - } - - // Unwraps the pointer's uintptr_t representation, without making an assertion - // on whether memory was freed or not. - template <typename T> - static ALWAYS_INLINE T* UnsafelyUnwrapPtrForComparison(T* wrapped_ptr) { - return ExtractPtr(wrapped_ptr); - } - - // Upcasts the wrapped pointer. - template <typename To, typename From> - static ALWAYS_INLINE constexpr To* Upcast(From* wrapped_ptr) { - static_assert(std::is_convertible<From*, To*>::value, - "From must be convertible to To."); - - // The top-bit tag must not affect the result of upcast. - return static_cast<To*>(wrapped_ptr); - } - - // Advance the wrapped pointer by `delta_elems`. - template <typename T, - typename Z, - typename = std::enable_if_t<offset_type<Z>, void>> - static ALWAYS_INLINE T* Advance(T* wrapped_ptr, Z delta_elems) { - return wrapped_ptr + delta_elems; - } - - template <typename T> - static ALWAYS_INLINE ptrdiff_t GetDeltaElems(T* wrapped_ptr1, - T* wrapped_ptr2) { - // Ensure that both pointers come from the same allocation. - // - // Disambiguation: UntagPtr removes the hardware MTE tag, whereas this - // class is responsible for handling the software MTE tag. - // - // MTECheckedPtr doesn't use 0 as a valid tag; depending on which - // subtraction operator is called, we may be getting the actual - // untagged T* or the wrapped pointer (passed as a T*) in one or - // both args. We can only check slot cohabitation when both args - // come with tags. - const uintptr_t tag1 = ExtractTag(partition_alloc::UntagPtr(wrapped_ptr1)); - const uintptr_t tag2 = ExtractTag(partition_alloc::UntagPtr(wrapped_ptr2)); - if (tag1 && tag2) { - GURL_CHECK(tag1 == tag2); - return wrapped_ptr1 - wrapped_ptr2; - } - - // If one or the other arg come untagged, we have to perform the - // subtraction entirely without tags. - return reinterpret_cast<T*>( - ExtractAddress(partition_alloc::UntagPtr(wrapped_ptr1))) - - reinterpret_cast<T*>( - ExtractAddress(partition_alloc::UntagPtr(wrapped_ptr2))); - } - - // Returns a copy of a wrapped pointer, without making an assertion - // on whether memory was freed or not. - template <typename T> - static ALWAYS_INLINE T* Duplicate(T* wrapped_ptr) { - return wrapped_ptr; - } - - // This is for accounting only, used by unit tests. - static ALWAYS_INLINE void IncrementSwapCountForTest() {} - static ALWAYS_INLINE void IncrementLessCountForTest() {} - static ALWAYS_INLINE void IncrementPointerToMemberOperatorCountForTest() {} - - private: - static ALWAYS_INLINE uintptr_t ExtractAddress(uintptr_t wrapped_ptr) { - return wrapped_ptr & kAddressMask; - } - - template <typename T> - static ALWAYS_INLINE T* ExtractPtr(T* wrapped_ptr) { - // Disambiguation: UntagPtr/TagAddr handle the hardware MTE tag, whereas - // this function is responsible for removing the software MTE tag. - // TODO(kdlee): Ensure that wrapped_ptr's hardware MTE tag is preserved. - // TODO(kdlee): Ensure that hardware and software MTE tags don't conflict. - return static_cast<T*>(partition_alloc::internal::TagAddr( - ExtractAddress(partition_alloc::UntagPtr(wrapped_ptr)))); - } - - static ALWAYS_INLINE uintptr_t ExtractTag(uintptr_t wrapped_ptr) { - return (wrapped_ptr & kTagMask) >> kValidAddressBits; - } -}; - -#endif // defined(PA_ENABLE_MTE_CHECKED_PTR_SUPPORT_WITH_64_BITS_POINTERS) - -#if BUILDFLAG(USE_BACKUP_REF_PTR) - -#if GURL_DCHECK_IS_ON() || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS) -BASE_EXPORT void CheckThatAddressIsntWithinFirstPartitionPage( - uintptr_t address); -#endif - -template <bool AllowDangling = false> -struct BackupRefPtrImpl { - // Note that `BackupRefPtrImpl` itself is not thread-safe. If multiple threads - // modify the same smart pointer object without synchronization, a data race - // will occur. - - static ALWAYS_INLINE bool IsSupportedAndNotNull(uintptr_t address) { - // This covers the nullptr case, as address 0 is never in GigaCage. - bool is_in_brp_pool = - partition_alloc::IsManagedByPartitionAllocBRPPool(address); - - // There are many situations where the compiler can prove that - // ReleaseWrappedPtr is called on a value that is always nullptr, but the - // way the check above is written, the compiler can't prove that nullptr is - // not managed by PartitionAlloc; and so the compiler has to emit a useless - // check and dead code. - // To avoid that without making the runtime check slower, explicitly promise - // to the compiler that is_in_brp_pool will always be false for nullptr. - // - // This condition would look nicer and might also theoretically be nicer for - // the optimizer if it was written as "if (!address) { ... }", but - // LLVM currently has issues with optimizing that away properly; see: - // https://bugs.llvm.org/show_bug.cgi?id=49403 - // https://reviews.llvm.org/D97848 - // https://chromium-review.googlesource.com/c/chromium/src/+/2727400/2/base/memory/checked_ptr.h#120 -#if GURL_DCHECK_IS_ON() || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS) - GURL_CHECK(address || !is_in_brp_pool); -#endif -#if HAS_BUILTIN(__builtin_assume) - __builtin_assume(address || !is_in_brp_pool); -#endif - - // There may be pointers immediately after the allocation, e.g. - // { - // // Assume this allocation happens outside of PartitionAlloc. - // raw_ptr<T> ptr = new T[20]; - // for (size_t i = 0; i < 20; i ++) { ptr++; } - // } - // - // Such pointers are *not* at risk of accidentally falling into BRP pool, - // because: - // 1) On 64-bit systems, BRP pool is preceded by a forbidden region. - // 2) On 32-bit systems, the guard pages and metadata of super pages in BRP - // pool aren't considered to be part of that pool. - // - // This allows us to make a stronger assertion that if - // IsManagedByPartitionAllocBRPPool returns true for a valid pointer, - // it must be at least partition page away from the beginning of a super - // page. -#if GURL_DCHECK_IS_ON() || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS) - if (is_in_brp_pool) { - CheckThatAddressIsntWithinFirstPartitionPage(address); - } -#endif - - return is_in_brp_pool; - } - - // Wraps a pointer. - template <typename T> - static ALWAYS_INLINE T* WrapRawPtr(T* ptr) { - uintptr_t address = partition_alloc::UntagPtr(ptr); - if (IsSupportedAndNotNull(address)) { -#if GURL_DCHECK_IS_ON() || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS) - GURL_CHECK(ptr != nullptr); -#endif - AcquireInternal(address); - } -#if !defined(PA_HAS_64_BITS_POINTERS) - else { - partition_alloc::internal::AddressPoolManagerBitmap:: - BanSuperPageFromBRPPool(address); - } -#endif - - return ptr; - } - - // Notifies the allocator when a wrapped pointer is being removed or replaced. - template <typename T> - static ALWAYS_INLINE void ReleaseWrappedPtr(T* wrapped_ptr) { - uintptr_t address = partition_alloc::UntagPtr(wrapped_ptr); - if (IsSupportedAndNotNull(address)) { -#if GURL_DCHECK_IS_ON() || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS) - GURL_CHECK(wrapped_ptr != nullptr); -#endif - ReleaseInternal(address); - } - // We are unable to counteract BanSuperPageFromBRPPool(), called from - // WrapRawPtr(). We only use one bit per super-page and, thus can't tell if - // there's more than one associated raw_ptr<T> at a given time. The risk of - // exhausting the entire address space is minuscule, therefore, we couldn't - // resist the perf gain of a single relaxed store (in the above mentioned - // function) over much more expensive two CAS operations, which we'd have to - // use if we were to un-ban a super-page. - } - - // Unwraps the pointer, while asserting that memory hasn't been freed. The - // function is allowed to crash on nullptr. - template <typename T> - static ALWAYS_INLINE T* SafelyUnwrapPtrForDereference(T* wrapped_ptr) { -#if GURL_DCHECK_IS_ON() || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS) - uintptr_t address = partition_alloc::UntagPtr(wrapped_ptr); - if (IsSupportedAndNotNull(address)) { - GURL_CHECK(wrapped_ptr != nullptr); - GURL_CHECK(IsPointeeAlive(address)); - } -#endif - return wrapped_ptr; - } - - // Unwraps the pointer, while asserting that memory hasn't been freed. The - // function must handle nullptr gracefully. - template <typename T> - static ALWAYS_INLINE T* SafelyUnwrapPtrForExtraction(T* wrapped_ptr) { - return wrapped_ptr; - } - - // Unwraps the pointer, without making an assertion on whether memory was - // freed or not. - template <typename T> - static ALWAYS_INLINE T* UnsafelyUnwrapPtrForComparison(T* wrapped_ptr) { - return wrapped_ptr; - } - - // Upcasts the wrapped pointer. - template <typename To, typename From> - static ALWAYS_INLINE constexpr To* Upcast(From* wrapped_ptr) { - static_assert(std::is_convertible<From*, To*>::value, - "From must be convertible to To."); - // Note, this cast may change the address if upcasting to base that lies in - // the middle of the derived object. - return wrapped_ptr; - } - - // Advance the wrapped pointer by `delta_elems`. - template <typename T, - typename Z, - typename = std::enable_if_t<offset_type<Z>, void>> - static ALWAYS_INLINE T* Advance(T* wrapped_ptr, Z delta_elems) { -#if BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) - // First check if the new address lands within the same allocation - // (end-of-allocation address is ok too). It has a non-trivial cost, but - // it's cheaper and more secure than the previous implementation that - // rewrapped the pointer (wrapped the new pointer and unwrapped the old - // one). - uintptr_t address = partition_alloc::UntagPtr(wrapped_ptr); - // TODO(bartekn): Consider adding support for non-BRP pool too. - if (IsSupportedAndNotNull(address)) - GURL_CHECK(IsValidDelta(address, delta_elems * static_cast<Z>(sizeof(T)))); - return wrapped_ptr + delta_elems; -#else - // In the "before allocation" mode, on 32-bit, we can run into a problem - // that the end-of-allocation address could fall out of "GigaCage", if this - // is the last slot of the super page, thus pointing to the guard page. This - // mean the ref-count won't be decreased when the pointer is released - // (leak). - // - // We could possibly solve it in a few different ways: - // - Add the trailing guard page to "GigaCage", but we'd have to think very - // hard if this doesn't create another hole. - // - Add an address adjustment to "GigaCage" check, similar as the one in - // PartitionAllocGetSlotStartInBRPPool(), but that seems fragile, not to - // mention adding an extra instruction to an inlined hot path. - // - Let the leak happen, since it should a very rare condition. - // - Go back to the previous solution of rewrapping the pointer, but that - // had an issue of losing protection in case the pointer ever gets shifter - // before the end of allocation. - // - // We decided to cross that bridge once we get there... if we ever get - // there. Currently there are no plans to switch back to the "before - // allocation" mode. - // - // This problem doesn't exist in the "previous slot" mode, or any mode that - // involves putting extras after the allocation, because the - // end-of-allocation address belongs to the same slot. - static_assert(false); -#endif - } - - template <typename T> - static ALWAYS_INLINE ptrdiff_t GetDeltaElems(T* wrapped_ptr1, - T* wrapped_ptr2) { - uintptr_t address1 = partition_alloc::UntagPtr(wrapped_ptr1); - uintptr_t address2 = partition_alloc::UntagPtr(wrapped_ptr2); - // Ensure that both pointers are within the same slot, and pool! - // TODO(bartekn): Consider adding support for non-BRP pool too. - if (IsSupportedAndNotNull(address1)) { - GURL_CHECK(IsSupportedAndNotNull(address2)); - GURL_CHECK(IsValidDelta(address2, address1 - address2)); - } else { - GURL_CHECK(!IsSupportedAndNotNull(address2)); - } - return wrapped_ptr1 - wrapped_ptr2; - } - - // Returns a copy of a wrapped pointer, without making an assertion on whether - // memory was freed or not. - // This method increments the reference count of the allocation slot. - template <typename T> - static ALWAYS_INLINE T* Duplicate(T* wrapped_ptr) { - return WrapRawPtr(wrapped_ptr); - } - - // Report the current wrapped pointer if pointee isn't alive anymore. - template <typename T> - static ALWAYS_INLINE void ReportIfDangling(T* wrapped_ptr) { - ReportIfDanglingInternal(partition_alloc::UntagPtr(wrapped_ptr)); - } - - // This is for accounting only, used by unit tests. - static ALWAYS_INLINE void IncrementSwapCountForTest() {} - static ALWAYS_INLINE void IncrementLessCountForTest() {} - static ALWAYS_INLINE void IncrementPointerToMemberOperatorCountForTest() {} - - private: - // We've evaluated several strategies (inline nothing, various parts, or - // everything in |Wrap()| and |Release()|) using the Speedometer2 benchmark - // to measure performance. The best results were obtained when only the - // lightweight |IsManagedByPartitionAllocBRPPool()| check was inlined. - // Therefore, we've extracted the rest into the functions below and marked - // them as NOINLINE to prevent unintended LTO effects. - static BASE_EXPORT NOINLINE void AcquireInternal(uintptr_t address); - static BASE_EXPORT NOINLINE void ReleaseInternal(uintptr_t address); - static BASE_EXPORT NOINLINE bool IsPointeeAlive(uintptr_t address); - static BASE_EXPORT NOINLINE void ReportIfDanglingInternal(uintptr_t address); - template <typename Z, typename = std::enable_if_t<offset_type<Z>, void>> - static ALWAYS_INLINE bool IsValidDelta(uintptr_t address, Z delta_in_bytes) { - if constexpr (std::is_signed_v<Z>) - return IsValidSignedDelta(address, ptrdiff_t{delta_in_bytes}); - else - return IsValidUnsignedDelta(address, size_t{delta_in_bytes}); - } - static BASE_EXPORT NOINLINE bool IsValidSignedDelta(uintptr_t address, - ptrdiff_t delta_in_bytes); - static BASE_EXPORT NOINLINE bool IsValidUnsignedDelta(uintptr_t address, - size_t delta_in_bytes); -}; - -#endif // BUILDFLAG(USE_BACKUP_REF_PTR) - -// Implementation that allows us to detect BackupRefPtr problems in ASan builds. -struct AsanBackupRefPtrImpl { - // Wraps a pointer. - template <typename T> - static ALWAYS_INLINE T* WrapRawPtr(T* ptr) { - AsanCheckIfValidInstantiation(ptr); - return ptr; - } - - // Notifies the allocator when a wrapped pointer is being removed or replaced. - template <typename T> - static ALWAYS_INLINE void ReleaseWrappedPtr(T*) {} - - // Unwraps the pointer, while asserting that memory hasn't been freed. The - // function is allowed to crash on nullptr. - template <typename T> - static ALWAYS_INLINE T* SafelyUnwrapPtrForDereference(T* wrapped_ptr) { - AsanCheckIfValidDereference(wrapped_ptr); - return wrapped_ptr; - } - - // Unwraps the pointer, while asserting that memory hasn't been freed. The - // function must handle nullptr gracefully. - template <typename T> - static ALWAYS_INLINE T* SafelyUnwrapPtrForExtraction(T* wrapped_ptr) { - AsanCheckIfValidExtraction(wrapped_ptr); - return wrapped_ptr; - } - - // Unwraps the pointer, without making an assertion on whether memory was - // freed or not. - template <typename T> - static ALWAYS_INLINE T* UnsafelyUnwrapPtrForComparison(T* wrapped_ptr) { - return wrapped_ptr; - } - - // Upcasts the wrapped pointer. - template <typename To, typename From> - static ALWAYS_INLINE constexpr To* Upcast(From* wrapped_ptr) { - static_assert(std::is_convertible<From*, To*>::value, - "From must be convertible to To."); - // Note, this cast may change the address if upcasting to base that lies in - // the middle of the derived object. - return wrapped_ptr; - } - - // Advance the wrapped pointer by `delta_elems`. - template <typename T, - typename Z, - typename = std::enable_if_t<offset_type<Z>, void>> - static ALWAYS_INLINE T* Advance(T* wrapped_ptr, Z delta_elems) { - return wrapped_ptr + delta_elems; - } - - template <typename T> - static ALWAYS_INLINE ptrdiff_t GetDeltaElems(T* wrapped_ptr1, - T* wrapped_ptr2) { - return wrapped_ptr1 - wrapped_ptr2; - } - - // Returns a copy of a wrapped pointer, without making an assertion on whether - // memory was freed or not. - template <typename T> - static ALWAYS_INLINE T* Duplicate(T* wrapped_ptr) { - return wrapped_ptr; - } - - // This is for accounting only, used by unit tests. - static ALWAYS_INLINE void IncrementSwapCountForTest() {} - static ALWAYS_INLINE void IncrementLessCountForTest() {} - static ALWAYS_INLINE void IncrementPointerToMemberOperatorCountForTest() {} - - private: - static BASE_EXPORT NOINLINE void AsanCheckIfValidInstantiation( - void const volatile* ptr); - static BASE_EXPORT NOINLINE void AsanCheckIfValidDereference( - void const volatile* ptr); - static BASE_EXPORT NOINLINE void AsanCheckIfValidExtraction( - void const volatile* ptr); -}; - -template <class Super> -struct RawPtrCountingImplWrapperForTest - : public raw_ptr_traits::RawPtrTypeToImpl<Super>::Impl { - using SuperImpl = typename raw_ptr_traits::RawPtrTypeToImpl<Super>::Impl; - template <typename T> - static ALWAYS_INLINE T* WrapRawPtr(T* ptr) { - ++wrap_raw_ptr_cnt; - return SuperImpl::WrapRawPtr(ptr); - } - - template <typename T> - static ALWAYS_INLINE void ReleaseWrappedPtr(T* ptr) { - ++release_wrapped_ptr_cnt; - SuperImpl::ReleaseWrappedPtr(ptr); - } - - template <typename T> - static ALWAYS_INLINE T* SafelyUnwrapPtrForDereference(T* wrapped_ptr) { - ++get_for_dereference_cnt; - return SuperImpl::SafelyUnwrapPtrForDereference(wrapped_ptr); - } - - template <typename T> - static ALWAYS_INLINE T* SafelyUnwrapPtrForExtraction(T* wrapped_ptr) { - ++get_for_extraction_cnt; - return SuperImpl::SafelyUnwrapPtrForExtraction(wrapped_ptr); - } - - template <typename T> - static ALWAYS_INLINE T* UnsafelyUnwrapPtrForComparison(T* wrapped_ptr) { - ++get_for_comparison_cnt; - return SuperImpl::UnsafelyUnwrapPtrForComparison(wrapped_ptr); - } - - static ALWAYS_INLINE void IncrementSwapCountForTest() { - ++wrapped_ptr_swap_cnt; - } - - static ALWAYS_INLINE void IncrementLessCountForTest() { - ++wrapped_ptr_less_cnt; - } - - static ALWAYS_INLINE void IncrementPointerToMemberOperatorCountForTest() { - ++pointer_to_member_operator_cnt; - } - - static void ClearCounters() { - wrap_raw_ptr_cnt = 0; - release_wrapped_ptr_cnt = 0; - get_for_dereference_cnt = 0; - get_for_extraction_cnt = 0; - get_for_comparison_cnt = 0; - wrapped_ptr_swap_cnt = 0; - wrapped_ptr_less_cnt = 0; - pointer_to_member_operator_cnt = 0; - } - - static inline int wrap_raw_ptr_cnt = INT_MIN; - static inline int release_wrapped_ptr_cnt = INT_MIN; - static inline int get_for_dereference_cnt = INT_MIN; - static inline int get_for_extraction_cnt = INT_MIN; - static inline int get_for_comparison_cnt = INT_MIN; - static inline int wrapped_ptr_swap_cnt = INT_MIN; - static inline int wrapped_ptr_less_cnt = INT_MIN; - static inline int pointer_to_member_operator_cnt = INT_MIN; -}; - -} // namespace internal - -namespace raw_ptr_traits { - -// IsSupportedType<T>::value answers whether raw_ptr<T> 1) compiles and 2) is -// always safe at runtime. Templates that may end up using `raw_ptr<T>` should -// use IsSupportedType to ensure that raw_ptr is not used with unsupported -// types. As an example, see how gurl_base::internal::StorageTraits uses -// IsSupportedType as a condition for using gurl_base::internal::UnretainedWrapper -// (which has a `ptr_` field that will become `raw_ptr<T>` after the Big -// Rewrite). -template <typename T, typename SFINAE = void> -struct IsSupportedType { - static constexpr bool value = true; -}; - -// raw_ptr<T> is not compatible with function pointer types. Also, they don't -// even need the raw_ptr protection, because they don't point on heap. -template <typename T> -struct IsSupportedType<T, std::enable_if_t<std::is_function<T>::value>> { - static constexpr bool value = false; -}; - -// This section excludes some types from raw_ptr<T> to avoid them from being -// used inside gurl_base::Unretained in performance sensitive places. These were -// identified from sampling profiler data. See crbug.com/1287151 for more info. -template <> -struct IsSupportedType<cc::Scheduler> { - static constexpr bool value = false; -}; -template <> -struct IsSupportedType<gurl_base::internal::DelayTimerBase> { - static constexpr bool value = false; -}; -template <> -struct IsSupportedType<content::responsiveness::Calculator> { - static constexpr bool value = false; -}; - -// IsRawPtrCountingImpl<T>::value answers whether T is a specialization of -// RawPtrCountingImplWrapperForTest, to know whether Impl is for testing -// purposes. -template <typename T> -struct IsRawPtrCountingImpl : std::false_type {}; - -template <typename T> -struct IsRawPtrCountingImpl<internal::RawPtrCountingImplWrapperForTest<T>> - : std::true_type {}; - -#if __OBJC__ -// raw_ptr<T> is not compatible with pointers to Objective-C classes for a -// multitude of reasons. They may fail to compile in many cases, and wouldn't -// work well with tagged pointers. Anyway, Objective-C objects have their own -// way of tracking lifespan, hence don't need the raw_ptr protection as much. -// -// Such pointers are detected by checking if they're convertible to |id| type. -template <typename T> -struct IsSupportedType<T, - std::enable_if_t<std::is_convertible<T*, id>::value>> { - static constexpr bool value = false; -}; -#endif // __OBJC__ - -#if BUILDFLAG(IS_WIN) -// raw_ptr<HWND__> is unsafe at runtime - if the handle happens to also -// represent a valid pointer into a PartitionAlloc-managed region then it can -// lead to manipulating random memory when treating it as BackupRefPtr -// ref-count. See also https://crbug.com/1262017. -// -// TODO(https://crbug.com/1262017): Cover other handle types like HANDLE, -// HLOCAL, HINTERNET, or HDEVINFO. Maybe we should avoid using raw_ptr<T> when -// T=void (as is the case in these handle types). OTOH, explicit, -// non-template-based raw_ptr<void> should be allowed. Maybe this can be solved -// by having 2 traits: IsPointeeAlwaysSafe (to be used in templates) and -// IsPointeeUsuallySafe (to be used in the static_assert in raw_ptr). The -// upside of this approach is that it will safely handle gurl_base::Bind closing over -// HANDLE. The downside of this approach is that gurl_base::Bind closing over a -// void* pointer will not get UaF protection. -#define CHROME_WINDOWS_HANDLE_TYPE(name) \ - template <> \ - struct IsSupportedType<name##__, void> { \ - static constexpr bool value = false; \ - }; -#include "base/win/win_handle_types_list.inc" -#undef CHROME_WINDOWS_HANDLE_TYPE -#endif - -template <typename T> -struct RawPtrTypeToImpl {}; - -template <typename T> -struct RawPtrTypeToImpl<internal::RawPtrCountingImplWrapperForTest<T>> { - using Impl = internal::RawPtrCountingImplWrapperForTest<T>; -}; - -template <> -struct RawPtrTypeToImpl<RawPtrMayDangle> { -#if BUILDFLAG(USE_BACKUP_REF_PTR) - using Impl = internal::BackupRefPtrImpl</*AllowDangling=*/true>; -#elif BUILDFLAG(USE_ASAN_BACKUP_REF_PTR) - using Impl = internal::AsanBackupRefPtrImpl; -#elif defined(PA_ENABLE_MTE_CHECKED_PTR_SUPPORT_WITH_64_BITS_POINTERS) - using Impl = internal::MTECheckedPtrImpl< - internal::MTECheckedPtrImplPartitionAllocSupport>; -#else - using Impl = internal::RawPtrNoOpImpl; -#endif -}; - -template <> -struct RawPtrTypeToImpl<RawPtrBanDanglingIfSupported> { -#if BUILDFLAG(USE_BACKUP_REF_PTR) - using Impl = internal::BackupRefPtrImpl</*AllowDangling=*/false>; -#elif BUILDFLAG(USE_ASAN_BACKUP_REF_PTR) - using Impl = internal::AsanBackupRefPtrImpl; -#elif defined(PA_ENABLE_MTE_CHECKED_PTR_SUPPORT_WITH_64_BITS_POINTERS) - using Impl = internal::MTECheckedPtrImpl< - internal::MTECheckedPtrImplPartitionAllocSupport>; -#else - using Impl = internal::RawPtrNoOpImpl; -#endif -}; - -} // namespace raw_ptr_traits - -// `raw_ptr<T>` is a non-owning smart pointer that has improved memory-safety -// over raw pointers. It behaves just like a raw pointer on platforms where -// USE_BACKUP_REF_PTR is off, and almost like one when it's on (the main -// difference is that it's zero-initialized and cleared on destruction and -// move). Unlike `std::unique_ptr<T>`, `gurl_base::scoped_refptr<T>`, etc., it -// doesn’t manage ownership or lifetime of an allocated object - you are still -// responsible for freeing the object when no longer used, just as you would -// with a raw C++ pointer. -// -// Compared to a raw C++ pointer, on platforms where USE_BACKUP_REF_PTR is on, -// `raw_ptr<T>` incurs additional performance overhead for initialization, -// destruction, and assignment (including `ptr++` and `ptr += ...`). There is -// no overhead when dereferencing a pointer. -// -// `raw_ptr<T>` is beneficial for security, because it can prevent a significant -// percentage of Use-after-Free (UaF) bugs from being exploitable. `raw_ptr<T>` -// has limited impact on stability - dereferencing a dangling pointer remains -// Undefined Behavior. Note that the security protection is not yet enabled by -// default. -// -// raw_ptr<T> is marked as [[gsl::Pointer]] which allows the compiler to catch -// some bugs where the raw_ptr holds a dangling pointer to a temporary object. -// However the [[gsl::Pointer]] analysis expects that such types do not have a -// non-default move constructor/assignment. Thus, it's possible to get an error -// where the pointer is not actually dangling, and have to work around the -// compiler. We have not managed to construct such an example in Chromium yet. - -using DefaultRawPtrType = RawPtrBanDanglingIfSupported; - -template <typename T, typename RawPtrType = DefaultRawPtrType> -class TRIVIAL_ABI GSL_POINTER raw_ptr { - using Impl = typename raw_ptr_traits::RawPtrTypeToImpl<RawPtrType>::Impl; - using DanglingRawPtr = std::conditional_t< - raw_ptr_traits::IsRawPtrCountingImpl<Impl>::value, - raw_ptr<T, internal::RawPtrCountingImplWrapperForTest<RawPtrMayDangle>>, - raw_ptr<T, RawPtrMayDangle>>; - - public: - static_assert(raw_ptr_traits::IsSupportedType<T>::value, - "raw_ptr<T> doesn't work with this kind of pointee type T"); - -#if BUILDFLAG(USE_BACKUP_REF_PTR) - // BackupRefPtr requires a non-trivial default constructor, destructor, etc. - constexpr ALWAYS_INLINE raw_ptr() noexcept : wrapped_ptr_(nullptr) {} - - ALWAYS_INLINE raw_ptr(const raw_ptr& p) noexcept - : wrapped_ptr_(Impl::Duplicate(p.wrapped_ptr_)) {} - - ALWAYS_INLINE raw_ptr(raw_ptr&& p) noexcept { - wrapped_ptr_ = p.wrapped_ptr_; - p.wrapped_ptr_ = nullptr; - } - - ALWAYS_INLINE raw_ptr& operator=(const raw_ptr& p) noexcept { - // Duplicate before releasing, in case the pointer is assigned to itself. - // - // Unlike the move version of this operator, don't add |this != &p| branch, - // for performance reasons. Even though Duplicate() is not cheap, we - // practically never assign a raw_ptr<T> to itself. We suspect that a - // cumulative cost of a conditional branch, even if always correctly - // predicted, would exceed that. - T* new_ptr = Impl::Duplicate(p.wrapped_ptr_); - Impl::ReleaseWrappedPtr(wrapped_ptr_); - wrapped_ptr_ = new_ptr; - return *this; - } - - ALWAYS_INLINE raw_ptr& operator=(raw_ptr&& p) noexcept { - // Unlike the the copy version of this operator, this branch is necessaty - // for correctness. - if (LIKELY(this != &p)) { - Impl::ReleaseWrappedPtr(wrapped_ptr_); - wrapped_ptr_ = p.wrapped_ptr_; - p.wrapped_ptr_ = nullptr; - } - return *this; - } - - ALWAYS_INLINE ~raw_ptr() noexcept { - Impl::ReleaseWrappedPtr(wrapped_ptr_); - // Work around external issues where raw_ptr is used after destruction. - wrapped_ptr_ = nullptr; - } - -#else // BUILDFLAG(USE_BACKUP_REF_PTR) - - // raw_ptr can be trivially default constructed (leaving |wrapped_ptr_| - // uninitialized). This is needed for compatibility with raw pointers. - // - // TODO(lukasza): Always initialize |wrapped_ptr_|. Fix resulting build - // errors. Analyze performance impact. - constexpr ALWAYS_INLINE raw_ptr() noexcept = default; - - // In addition to nullptr_t ctor above, raw_ptr needs to have these - // as |=default| or |constexpr| to avoid hitting -Wglobal-constructors in - // cases like this: - // struct SomeStruct { int int_field; raw_ptr<int> ptr_field; }; - // SomeStruct g_global_var = { 123, nullptr }; - ALWAYS_INLINE raw_ptr(const raw_ptr&) noexcept = default; - ALWAYS_INLINE raw_ptr(raw_ptr&&) noexcept = default; - ALWAYS_INLINE raw_ptr& operator=(const raw_ptr&) noexcept = default; - ALWAYS_INLINE raw_ptr& operator=(raw_ptr&&) noexcept = default; - - ALWAYS_INLINE ~raw_ptr() noexcept = default; - -#endif // BUILDFLAG(USE_BACKUP_REF_PTR) - - // Deliberately implicit, because raw_ptr is supposed to resemble raw ptr. - // NOLINTNEXTLINE(google-explicit-constructor) - constexpr ALWAYS_INLINE raw_ptr(std::nullptr_t) noexcept - : wrapped_ptr_(nullptr) {} - - // Deliberately implicit, because raw_ptr is supposed to resemble raw ptr. - // NOLINTNEXTLINE(google-explicit-constructor) - ALWAYS_INLINE raw_ptr(T* p) noexcept : wrapped_ptr_(Impl::WrapRawPtr(p)) {} - - // Deliberately implicit in order to support implicit upcast. - template <typename U, - typename Unused = std::enable_if_t< - std::is_convertible<U*, T*>::value && - !std::is_void<typename std::remove_cv<T>::type>::value>> - // NOLINTNEXTLINE(google-explicit-constructor) - ALWAYS_INLINE raw_ptr(const raw_ptr<U, RawPtrType>& ptr) noexcept - : wrapped_ptr_( - Impl::Duplicate(Impl::template Upcast<T, U>(ptr.wrapped_ptr_))) {} - // Deliberately implicit in order to support implicit upcast. - template <typename U, - typename Unused = std::enable_if_t< - std::is_convertible<U*, T*>::value && - !std::is_void<typename std::remove_cv<T>::type>::value>> - // NOLINTNEXTLINE(google-explicit-constructor) - ALWAYS_INLINE raw_ptr(raw_ptr<U, RawPtrType>&& ptr) noexcept - : wrapped_ptr_(Impl::template Upcast<T, U>(ptr.wrapped_ptr_)) { -#if BUILDFLAG(USE_BACKUP_REF_PTR) - ptr.wrapped_ptr_ = nullptr; -#endif - } - - ALWAYS_INLINE raw_ptr& operator=(std::nullptr_t) noexcept { - Impl::ReleaseWrappedPtr(wrapped_ptr_); - wrapped_ptr_ = nullptr; - return *this; - } - ALWAYS_INLINE raw_ptr& operator=(T* p) noexcept { - Impl::ReleaseWrappedPtr(wrapped_ptr_); - wrapped_ptr_ = Impl::WrapRawPtr(p); - return *this; - } - - // Upcast assignment - template <typename U, - typename Unused = std::enable_if_t< - std::is_convertible<U*, T*>::value && - !std::is_void<typename std::remove_cv<T>::type>::value>> - ALWAYS_INLINE raw_ptr& operator=(const raw_ptr<U, RawPtrType>& ptr) noexcept { - // Make sure that pointer isn't assigned to itself (look at pointer address, - // not its value). -#if GURL_DCHECK_IS_ON() || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS) - GURL_CHECK(reinterpret_cast<uintptr_t>(this) != - reinterpret_cast<uintptr_t>(&ptr)); -#endif - Impl::ReleaseWrappedPtr(wrapped_ptr_); - wrapped_ptr_ = - Impl::Duplicate(Impl::template Upcast<T, U>(ptr.wrapped_ptr_)); - return *this; - } - template <typename U, - typename Unused = std::enable_if_t< - std::is_convertible<U*, T*>::value && - !std::is_void<typename std::remove_cv<T>::type>::value>> - ALWAYS_INLINE raw_ptr& operator=(raw_ptr<U, RawPtrType>&& ptr) noexcept { - // Make sure that pointer isn't assigned to itself (look at pointer address, - // not its value). -#if GURL_DCHECK_IS_ON() || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS) - GURL_CHECK(reinterpret_cast<uintptr_t>(this) != - reinterpret_cast<uintptr_t>(&ptr)); -#endif - Impl::ReleaseWrappedPtr(wrapped_ptr_); - wrapped_ptr_ = Impl::template Upcast<T, U>(ptr.wrapped_ptr_); -#if BUILDFLAG(USE_BACKUP_REF_PTR) - ptr.wrapped_ptr_ = nullptr; -#endif - return *this; - } - - // Avoid using. The goal of raw_ptr is to be as close to raw pointer as - // possible, so use it only if absolutely necessary (e.g. for const_cast). - ALWAYS_INLINE T* get() const { return GetForExtraction(); } - - explicit ALWAYS_INLINE operator bool() const { return !!wrapped_ptr_; } - - template <typename U = T, - typename Unused = std::enable_if_t< - !std::is_void<typename std::remove_cv<U>::type>::value>> - ALWAYS_INLINE U& operator*() const { - return *GetForDereference(); - } - ALWAYS_INLINE T* operator->() const { return GetForDereference(); } - - // Disables `(my_raw_ptr->*pmf)(...)` as a workaround for - // the ICE in GCC parsing the code, reported at - // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103455 - template <typename PMF> - void operator->*(PMF) const = delete; - - // Deliberately implicit, because raw_ptr is supposed to resemble raw ptr. - // NOLINTNEXTLINE(runtime/explicit) - ALWAYS_INLINE operator T*() const { return GetForExtraction(); } - template <typename U> - explicit ALWAYS_INLINE operator U*() const { - // This operator may be invoked from static_cast, meaning the types may not - // be implicitly convertible, hence the need for static_cast here. - return static_cast<U*>(GetForExtraction()); - } - - ALWAYS_INLINE raw_ptr& operator++() { - wrapped_ptr_ = Impl::Advance(wrapped_ptr_, 1); - return *this; - } - ALWAYS_INLINE raw_ptr& operator--() { - wrapped_ptr_ = Impl::Advance(wrapped_ptr_, -1); - return *this; - } - ALWAYS_INLINE raw_ptr operator++(int /* post_increment */) { - raw_ptr result = *this; - ++(*this); - return result; - } - ALWAYS_INLINE raw_ptr operator--(int /* post_decrement */) { - raw_ptr result = *this; - --(*this); - return result; - } - template <typename Z, typename = std::enable_if_t<internal::offset_type<Z>>> - ALWAYS_INLINE raw_ptr& operator+=(Z delta_elems) { - wrapped_ptr_ = Impl::Advance(wrapped_ptr_, delta_elems); - return *this; - } - template <typename Z, typename = std::enable_if_t<internal::offset_type<Z>>> - ALWAYS_INLINE raw_ptr& operator-=(Z delta_elems) { - return *this += -delta_elems; - } - - template <typename Z, typename = std::enable_if_t<internal::offset_type<Z>>> - friend ALWAYS_INLINE raw_ptr operator+(const raw_ptr& p, Z delta_elems) { - raw_ptr result = p; - return result += delta_elems; - } - template <typename Z, typename = std::enable_if_t<internal::offset_type<Z>>> - friend ALWAYS_INLINE raw_ptr operator-(const raw_ptr& p, Z delta_elems) { - raw_ptr result = p; - return result -= delta_elems; - } - friend ALWAYS_INLINE ptrdiff_t operator-(const raw_ptr& p1, - const raw_ptr& p2) { - return Impl::GetDeltaElems(p1.wrapped_ptr_, p2.wrapped_ptr_); - } - friend ALWAYS_INLINE ptrdiff_t operator-(T* p1, const raw_ptr& p2) { - return Impl::GetDeltaElems(p1, p2.wrapped_ptr_); - } - friend ALWAYS_INLINE ptrdiff_t operator-(const raw_ptr& p1, T* p2) { - return Impl::GetDeltaElems(p1.wrapped_ptr_, p2); - } - - // Stop referencing the underlying pointer and free its memory. Compared to - // raw delete calls, this avoids the raw_ptr to be temporarily dangling - // during the free operation, which will lead to taking the slower path that - // involves quarantine. - ALWAYS_INLINE void ClearAndDelete() noexcept { - delete GetForExtractionAndReset(); - } - ALWAYS_INLINE void ClearAndDeleteArray() noexcept { - delete[] GetForExtractionAndReset(); - } - - // Clear the underlying pointer and return another raw_ptr instance - // that is allowed to dangle. - // This can be useful in cases such as: - // ``` - // ptr.ExtractAsDangling()->SelfDestroy(); - // ``` - // ``` - // c_style_api_do_something_and_destroy(ptr.ExtractAsDangling()); - // ``` - // NOTE, avoid using this method as it indicates an error-prone memory - // ownership pattern. If possible, use smart pointers like std::unique_ptr<> - // instead of raw_ptr<>. - // If you have to use it, avoid saving the return value in a long-lived - // variable (or worse, a field)! It's meant to be used as a temporary, to be - // passed into a cleanup & freeing function, and destructed at the end of the - // statement. - ALWAYS_INLINE DanglingRawPtr ExtractAsDangling() noexcept { - if constexpr (std::is_same_v< - typename std::remove_reference<decltype(*this)>::type, - DanglingRawPtr>) { - DanglingRawPtr res(std::move(*this)); - // Not all implementation clear the source pointer on move, so do it - // here just in case. Should be cheap. - operator=(nullptr); - return res; - } else { - T* ptr = GetForExtraction(); - DanglingRawPtr res(ptr); - operator=(nullptr); - return res; - } - } - - // Comparison operators between raw_ptr and raw_ptr<U>/U*/std::nullptr_t. - // Strictly speaking, it is not necessary to provide these: the compiler can - // use the conversion operator implicitly to allow comparisons to fall back to - // comparisons between raw pointers. However, `operator T*`/`operator U*` may - // perform safety checks with a higher runtime cost, so to avoid this, provide - // explicit comparison operators for all combinations of parameters. - - // Comparisons between `raw_ptr`s. This unusual declaration and separate - // definition below is because `GetForComparison()` is a private method. The - // more conventional approach of defining a comparison operator between - // `raw_ptr` and `raw_ptr<U>` in the friend declaration itself does not work, - // because a comparison operator defined inline would not be allowed to call - // `raw_ptr<U>`'s private `GetForComparison()` method. - template <typename U, typename V, typename I> - friend ALWAYS_INLINE bool operator==(const raw_ptr<U, I>& lhs, - const raw_ptr<V, I>& rhs); - template <typename U> - friend ALWAYS_INLINE bool operator!=(const raw_ptr& lhs, - const raw_ptr<U, Impl>& rhs) { - return !(lhs == rhs); - } - template <typename U, typename V, typename I> - friend ALWAYS_INLINE bool operator<(const raw_ptr<U, I>& lhs, - const raw_ptr<V, I>& rhs); - template <typename U, typename V, typename I> - friend ALWAYS_INLINE bool operator>(const raw_ptr<U, I>& lhs, - const raw_ptr<V, I>& rhs); - template <typename U, typename V, typename I> - friend ALWAYS_INLINE bool operator<=(const raw_ptr<U, I>& lhs, - const raw_ptr<V, I>& rhs); - template <typename U, typename V, typename I> - friend ALWAYS_INLINE bool operator>=(const raw_ptr<U, I>& lhs, - const raw_ptr<V, I>& rhs); - - // Comparisons with U*. These operators also handle the case where the RHS is - // T*. - template <typename U> - friend ALWAYS_INLINE bool operator==(const raw_ptr& lhs, U* rhs) { - return lhs.GetForComparison() == rhs; - } - template <typename U> - friend ALWAYS_INLINE bool operator!=(const raw_ptr& lhs, U* rhs) { - return !(lhs == rhs); - } - template <typename U> - friend ALWAYS_INLINE bool operator==(U* lhs, const raw_ptr& rhs) { - return rhs == lhs; // Reverse order to call the operator above. - } - template <typename U> - friend ALWAYS_INLINE bool operator!=(U* lhs, const raw_ptr& rhs) { - return rhs != lhs; // Reverse order to call the operator above. - } - template <typename U> - friend ALWAYS_INLINE bool operator<(const raw_ptr& lhs, U* rhs) { - return lhs.GetForComparison() < rhs; - } - template <typename U> - friend ALWAYS_INLINE bool operator<=(const raw_ptr& lhs, U* rhs) { - return lhs.GetForComparison() <= rhs; - } - template <typename U> - friend ALWAYS_INLINE bool operator>(const raw_ptr& lhs, U* rhs) { - return lhs.GetForComparison() > rhs; - } - template <typename U> - friend ALWAYS_INLINE bool operator>=(const raw_ptr& lhs, U* rhs) { - return lhs.GetForComparison() >= rhs; - } - template <typename U> - friend ALWAYS_INLINE bool operator<(U* lhs, const raw_ptr& rhs) { - return lhs < rhs.GetForComparison(); - } - template <typename U> - friend ALWAYS_INLINE bool operator<=(U* lhs, const raw_ptr& rhs) { - return lhs <= rhs.GetForComparison(); - } - template <typename U> - friend ALWAYS_INLINE bool operator>(U* lhs, const raw_ptr& rhs) { - return lhs > rhs.GetForComparison(); - } - template <typename U> - friend ALWAYS_INLINE bool operator>=(U* lhs, const raw_ptr& rhs) { - return lhs >= rhs.GetForComparison(); - } - - // Comparisons with `std::nullptr_t`. - friend ALWAYS_INLINE bool operator==(const raw_ptr& lhs, std::nullptr_t) { - return !lhs; - } - friend ALWAYS_INLINE bool operator!=(const raw_ptr& lhs, std::nullptr_t) { - return !!lhs; // Use !! otherwise the costly implicit cast will be used. - } - friend ALWAYS_INLINE bool operator==(std::nullptr_t, const raw_ptr& rhs) { - return !rhs; - } - friend ALWAYS_INLINE bool operator!=(std::nullptr_t, const raw_ptr& rhs) { - return !!rhs; // Use !! otherwise the costly implicit cast will be used. - } - - friend ALWAYS_INLINE void swap(raw_ptr& lhs, raw_ptr& rhs) noexcept { - Impl::IncrementSwapCountForTest(); - std::swap(lhs.wrapped_ptr_, rhs.wrapped_ptr_); - } - - // If T can be serialised into trace, its alias is also - // serialisable. - template <class U = T> - typename perfetto::check_traced_value_support<U>::type WriteIntoTrace( - perfetto::TracedValue&& context) const { - perfetto::WriteIntoTracedValue(std::move(context), get()); - } - - ALWAYS_INLINE void ReportIfDangling() const noexcept { -#if BUILDFLAG(USE_BACKUP_REF_PTR) - Impl::ReportIfDangling(wrapped_ptr_); -#endif - } - - private: - // This getter is meant for situations where the pointer is meant to be - // dereferenced. It is allowed to crash on nullptr (it may or may not), - // because it knows that the caller will crash on nullptr. - ALWAYS_INLINE T* GetForDereference() const { - return Impl::SafelyUnwrapPtrForDereference(wrapped_ptr_); - } - // This getter is meant for situations where the raw pointer is meant to be - // extracted outside of this class, but not necessarily with an intention to - // dereference. It mustn't crash on nullptr. - ALWAYS_INLINE T* GetForExtraction() const { - return Impl::SafelyUnwrapPtrForExtraction(wrapped_ptr_); - } - // This getter is meant *only* for situations where the pointer is meant to be - // compared (guaranteeing no dereference or extraction outside of this class). - // Any verifications can and should be skipped for performance reasons. - ALWAYS_INLINE T* GetForComparison() const { - return Impl::UnsafelyUnwrapPtrForComparison(wrapped_ptr_); - } - - ALWAYS_INLINE T* GetForExtractionAndReset() { - T* ptr = GetForExtraction(); - operator=(nullptr); - return ptr; - } - - T* wrapped_ptr_; - - template <typename U, typename V> - friend class raw_ptr; -}; - -template <typename U, typename V, typename I> -ALWAYS_INLINE bool operator==(const raw_ptr<U, I>& lhs, - const raw_ptr<V, I>& rhs) { - return lhs.GetForComparison() == rhs.GetForComparison(); -} - -template <typename U, typename V, typename I> -ALWAYS_INLINE bool operator<(const raw_ptr<U, I>& lhs, - const raw_ptr<V, I>& rhs) { - return lhs.GetForComparison() < rhs.GetForComparison(); -} - -template <typename U, typename V, typename I> -ALWAYS_INLINE bool operator>(const raw_ptr<U, I>& lhs, - const raw_ptr<V, I>& rhs) { - return lhs.GetForComparison() > rhs.GetForComparison(); -} - -template <typename U, typename V, typename I> -ALWAYS_INLINE bool operator<=(const raw_ptr<U, I>& lhs, - const raw_ptr<V, I>& rhs) { - return lhs.GetForComparison() <= rhs.GetForComparison(); -} - -template <typename U, typename V, typename I> -ALWAYS_INLINE bool operator>=(const raw_ptr<U, I>& lhs, - const raw_ptr<V, I>& rhs) { - return lhs.GetForComparison() >= rhs.GetForComparison(); -} - -template <typename T> -struct IsRawPtr : std::false_type {}; - -template <typename T, typename I> -struct IsRawPtr<raw_ptr<T, I>> : std::true_type {}; - -template <typename T> -inline constexpr bool IsRawPtrV = IsRawPtr<T>::value; - -// Template helpers for working with T* or raw_ptr<T>. -template <typename T> -struct IsPointer : std::false_type {}; - -template <typename T> -struct IsPointer<T*> : std::true_type {}; - -template <typename T, typename I> -struct IsPointer<raw_ptr<T, I>> : std::true_type {}; - -template <typename T> -inline constexpr bool IsPointerV = IsPointer<T>::value; - -template <typename T> -struct RemovePointer { - using type = T; -}; - -template <typename T> -struct RemovePointer<T*> { - using type = T; -}; - -template <typename T, typename I> -struct RemovePointer<raw_ptr<T, I>> { - using type = T; -}; - -template <typename T> -using RemovePointerT = typename RemovePointer<T>::type; - -} // namespace base - -using gurl_base::raw_ptr; - -// DisableDanglingPtrDetection option for raw_ptr annotates -// "intentional-and-safe" dangling pointers. It is meant to be used at the -// margin, only if there is no better way to re-architecture the code. -// -// Usage: -// raw_ptr<T, DisableDanglingPtrDetection> dangling_ptr; -// -// When using it, please provide a justification about what guarantees it will -// never be dereferenced after becoming dangling. -using DisableDanglingPtrDetection = gurl_base::RawPtrMayDangle; - -// See `docs/dangling_ptr.md` -// Annotates known dangling raw_ptr. Those haven't been triaged yet. All the -// occurrences are meant to be removed. See https://crbug.com/1291138. -using DanglingUntriaged = DisableDanglingPtrDetection; - -// The following template parameters are only meaningful when `raw_ptr` -// is `MTECheckedPtr` (never the case unless a particular GN arg is set -// true.) `raw_ptr` users need not worry about this and can refer solely -// to `DisableDanglingPtrDetection` and `DanglingUntriaged` above. -// -// The `raw_ptr` definition allows users to specify an implementation. -// When `MTECheckedPtr` is in play, we need to augment this -// implementation setting with another layer that allows the `raw_ptr` -// to degrade into the no-op version. -#if defined(PA_ENABLE_MTE_CHECKED_PTR_SUPPORT_WITH_64_BITS_POINTERS) - -// Direct pass-through to no-op implementation. -using DegradeToNoOpWhenMTE = gurl_base::internal::RawPtrNoOpImpl; - -// As above, but with the "untriaged dangling" annotation. -using DanglingUntriagedDegradeToNoOpWhenMTE = gurl_base::internal::RawPtrNoOpImpl; - -// As above, but with the "explicitly disable protection" annotation. -using DisableDanglingPtrDetectionDegradeToNoOpWhenMTE = - gurl_base::internal::RawPtrNoOpImpl; - -#else - -// Direct pass-through to default implementation specified by `raw_ptr` -// template. -using DegradeToNoOpWhenMTE = gurl_base::RawPtrBanDanglingIfSupported; - -// Direct pass-through to `DanglingUntriaged`. -using DanglingUntriagedDegradeToNoOpWhenMTE = DanglingUntriaged; - -// Direct pass-through to `DisableDanglingPtrDetection`. -using DisableDanglingPtrDetectionDegradeToNoOpWhenMTE = - DisableDanglingPtrDetection; - -#endif // defined(PA_ENABLE_MTE_CHECKED_PTR_SUPPORT_WITH_64_BITS_POINTERS) - -namespace std { - -// Override so set/map lookups do not create extra raw_ptr. This also allows -// dangling pointers to be used for lookup. -template <typename T, typename RawPtrType> -struct less<raw_ptr<T, RawPtrType>> { - using Impl = - typename gurl_base::raw_ptr_traits::RawPtrTypeToImpl<RawPtrType>::Impl; - using is_transparent = void; - - bool operator()(const raw_ptr<T, RawPtrType>& lhs, - const raw_ptr<T, RawPtrType>& rhs) const { - Impl::IncrementLessCountForTest(); - return lhs < rhs; - } - - bool operator()(T* lhs, const raw_ptr<T, RawPtrType>& rhs) const { - Impl::IncrementLessCountForTest(); - return lhs < rhs; - } - - bool operator()(const raw_ptr<T, RawPtrType>& lhs, T* rhs) const { - Impl::IncrementLessCountForTest(); - return lhs < rhs; - } -}; - -// Define for cases where raw_ptr<T> holds a pointer to an array of type T. -// This is consistent with definition of std::iterator_traits<T*>. -// Algorithms like std::binary_search need that. -template <typename T, typename Impl> -struct iterator_traits<raw_ptr<T, Impl>> { - using difference_type = ptrdiff_t; - using value_type = std::remove_cv_t<T>; - using pointer = T*; - using reference = T&; - using iterator_category = std::random_access_iterator_tag; -}; - -} // namespace std - -#endif // BASE_MEMORY_RAW_PTR_H_
diff --git a/base/memory/raw_ptr_exclusion.h b/base/memory/raw_ptr_exclusion.h index f881c04..2a30e26 100644 --- a/base/memory/raw_ptr_exclusion.h +++ b/base/memory/raw_ptr_exclusion.h
@@ -6,8 +6,10 @@ #define BASE_MEMORY_RAW_PTR_EXCLUSION_H_ #include "polyfills/base/allocator/buildflags.h" +#include "base/compiler_specific.h" #include "build/build_config.h" +#if HAS_ATTRIBUTE(annotate) #if defined(OFFICIAL_BUILD) && !BUILDFLAG(FORCE_ENABLE_RAW_PTR_EXCLUSION) // The annotation changed compiler output and increased binary size so disable // for official builds. @@ -18,5 +20,8 @@ // Example: RAW_PTR_EXCLUSION Foo* foo_; #define RAW_PTR_EXCLUSION __attribute__((annotate("raw_ptr_exclusion"))) #endif +#else +#define RAW_PTR_EXCLUSION +#endif #endif // BASE_MEMORY_RAW_PTR_EXCLUSION_H_
diff --git a/base/no_destructor.h b/base/no_destructor.h index b183e4f..d2bb766 100644 --- a/base/no_destructor.h +++ b/base/no_destructor.h
@@ -10,28 +10,50 @@ #include <utility> namespace gurl_base { -// A tag type used for NoDestructor to allow it to be created for a type that -// has a trivial destructor. Use for cases where the same class might have -// different implementations that vary on destructor triviality or when the -// LSan hiding properties of NoDestructor are needed. -struct AllowForTriviallyDestructibleType; -// A wrapper that makes it easy to create an object of type T with static -// storage duration that: -// - is only constructed on first access -// - never invokes the destructor -// in order to satisfy the styleguide ban on global constructors and -// destructors. +// Helper type to create a function-local static variable of type `T` when `T` +// has a non-trivial destructor. Storing a `T` in a `gurl_base::NoDestructor<T>` will +// prevent `~T()` from running, even when the variable goes out of scope. // -// Runtime constant example: -// const std::string& GetLineSeparator() { -// // Forwards to std::string(size_t, char, const Allocator&) constructor. -// static const gurl_base::NoDestructor<std::string> s(5, '-'); +// Useful when a variable has static storage duration but its type has a +// non-trivial destructor. Chromium bans global constructors and destructors: +// using a function-local static variable prevents the former, while using +// `gurl_base::NoDestructor<T>` prevents the latter. +// +// ## Caveats +// +// - Must only be used as a function-local static variable. Declaring a global +// variable of type `gurl_base::NoDestructor<T>` will still generate a global +// constructor; declaring a local or member variable will lead to memory leaks +// or other surprising and undesirable behaviour. +// +// - If the data is rarely used, consider creating it on demand rather than +// caching it for the lifetime of the program. Though `gurl_base::NoDestructor<T>` +// does not heap allocate, the compiler still reserves space in bss for +// storing `T`, which costs memory at runtime. +// +// - If `T` is trivially destructible, do not use `gurl_base::NoDestructor<T>`: +// +// const uint64_t GetUnstableSessionSeed() { +// // No need to use `gurl_base::NoDestructor<T>` as `uint64_t` is trivially +// // destructible and does not require a global destructor. +// static const uint64_t kSessionSeed = gurl_base::RandUint64(); +// return kSessionSeed; +// } +// +// ## Example Usage +// +// const std::string& GetDefaultText() { +// // Required since `static const std::string` requires a global destructor. +// static const gurl_base::NoDestructor<std::string> s("Hello world!"); // return *s; // } // -// More complex initialization with a lambda: -// const std::string& GetSessionNonce() { +// More complex initialization using a lambda: +// +// const std::string& GetRandomNonce() { +// // `nonce` is initialized with random data the first time this function is +// // called, but its value is fixed thereafter. // static const gurl_base::NoDestructor<std::string> nonce([] { // std::string s(16); // crypto::RandString(s.data(), s.size()); @@ -40,29 +62,24 @@ // return *nonce; // } // -// NoDestructor<T> stores the object inline, so it also avoids a pointer -// indirection and a malloc. Also note that since C++11 static local variable -// initialization is thread-safe and so is this pattern. Code should prefer to -// use NoDestructor<T> over: -// - A function scoped static T* or T& that is dynamically initialized. -// - A global gurl_base::LazyInstance<T>. +// ## Thread safety // -// Note that since the destructor is never run, this *will* leak memory if used -// as a stack or member variable. Furthermore, a NoDestructor<T> should never -// have global scope as that may require a static initializer. -template <typename T, typename O = std::nullptr_t> +// Initialisation of function-local static variables is thread-safe since C++11. +// The standard guarantees that: +// +// - function-local static variables will be initialised the first time +// execution passes through the declaration. +// +// - if another thread's execution concurrently passes through the declaration +// in the middle of initialisation, that thread will wait for the in-progress +// initialisation to complete. +template <typename T> class NoDestructor { public: static_assert( - !std::is_trivially_destructible<T>::value || - std::is_same<O, AllowForTriviallyDestructibleType>::value, - "gurl_base::NoDestructor is not needed because the templated class has a " - "trivial destructor"); - - static_assert(std::is_same<O, AllowForTriviallyDestructibleType>::value || - std::is_same<O, std::nullptr_t>::value, - "AllowForTriviallyDestructibleType is the only valid option " - "for the second template parameter of NoDestructor"); + !std::is_trivially_destructible_v<T>, + "T is trivially destructible; please use a function-local static " + "of type T directly instead"); // Not constexpr; just write static constexpr T x = ...; if the value should // be a constexpr.
diff --git a/base/stl_util.h b/base/stl_util.h index 74ce611..2511412 100644 --- a/base/stl_util.h +++ b/base/stl_util.h
@@ -26,17 +26,6 @@ } // namespace internal -// Implementation of C++23's std::to_underlying. -// -// Note: This has an additional `std::is_enum<EnumT>` requirement to be SFINAE -// friendly prior to C++20. -// -// Reference: https://en.cppreference.com/w/cpp/utility/to_underlying -template <typename EnumT, typename = std::enable_if_t<std::is_enum<EnumT>{}>> -constexpr std::underlying_type_t<EnumT> to_underlying(EnumT e) noexcept { - return static_cast<std::underlying_type_t<EnumT>>(e); -} - // Returns a const reference to the underlying container of a container adapter. // Works for std::priority_queue, std::queue, and std::stack. template <class A> @@ -59,14 +48,6 @@ obj->reserve(0); } -// Counts the number of instances of val in a container. -template <typename Container, typename T> -typename std::iterator_traits< - typename Container::const_iterator>::difference_type -STLCount(const Container& container, const T& val) { - return std::count(container.begin(), container.end(), val); -} - // O(1) implementation of const casting an iterator for any sequence, // associative or unordered associative container in the STL. //
diff --git a/base/strings/escape.cc b/base/strings/escape.cc index 96657a3..d855c1c 100644 --- a/base/strings/escape.cc +++ b/base/strings/escape.cc
@@ -49,7 +49,7 @@ bool keep_escaped = false) { std::string escaped; escaped.reserve(text.length() * 3); - for (unsigned int i = 0; i < text.length(); ++i) { + for (size_t i = 0; i < text.length(); ++i) { unsigned char c = static_cast<unsigned char>(text[i]); if (use_plus && ' ' == c) { escaped.push_back('+');
diff --git a/base/strings/safe_sprintf.cc b/base/strings/safe_sprintf.cc index 5071377..0b53dff 100644 --- a/base/strings/safe_sprintf.cc +++ b/base/strings/safe_sprintf.cc
@@ -10,7 +10,7 @@ #include <algorithm> #include <limits> -#include "base/memory/raw_ptr.h" +#include "polyfills/base/memory/raw_ptr.h" #include "build/build_config.h" #if !defined(NDEBUG)
diff --git a/base/strings/safe_sprintf_unittest.cc b/base/strings/safe_sprintf_unittest.cc index bdcc2ff..76d72b2 100644 --- a/base/strings/safe_sprintf_unittest.cc +++ b/base/strings/safe_sprintf_unittest.cc
@@ -12,6 +12,7 @@ #include <limits> #include <memory> + #include "polyfills/base/check_op.h" #include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" @@ -482,7 +483,14 @@ } // anonymous namespace -TEST(SafeSPrintfTest, Truncation) { +// TODO(crbug.com/1369007): Fails when OOB protection is turned on. +#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) || \ + BUILDFLAG(ENABLE_MTE_CHECKED_PTR_SUPPORT) +#define MAYBE_Truncation DISABLED_Truncation +#else +#define MAYBE_Truncation Truncation +#endif +TEST(SafeSPrintfTest, MAYBE_Truncation) { // We use PrintLongString() to print a complex long string and then // truncate to all possible lengths. This ends up exercising a lot of // different code paths in SafeSPrintf() and IToASCII(), as truncation can
diff --git a/base/strings/string_number_conversions_fuzzer.cc b/base/strings/string_number_conversions_fuzzer.cc deleted file mode 100644 index cea3d3c..0000000 --- a/base/strings/string_number_conversions_fuzzer.cc +++ /dev/null
@@ -1,118 +0,0 @@ -// Copyright 2015 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <stddef.h> -#include <stdint.h> - -#include <string> -#include <vector> - -#include "base/strings/string_number_conversions.h" - -template <class NumberType, class StringPieceType, class StringType> -void CheckRoundtripsT(const uint8_t* data, - const size_t size, - StringType (*num_to_string)(NumberType), - bool (*string_to_num)(StringPieceType, NumberType*)) { - // Ensure we can read a NumberType from |data| - if (size < sizeof(NumberType)) - return; - const NumberType v1 = *reinterpret_cast<const NumberType*>(data); - - // Because we started with an arbitrary NumberType value, not an arbitrary - // string, we expect that the function |string_to_num| (e.g. StringToInt) will - // return true, indicating a perfect conversion. - NumberType v2; - GURL_CHECK(string_to_num(num_to_string(v1), &v2)); - - // Given that this was a perfect conversion, we expect the original NumberType - // value to equal the newly parsed one. - GURL_CHECK_EQ(v1, v2); -} - -template <class NumberType> -void CheckRoundtrips(const uint8_t* data, - const size_t size, - bool (*string_to_num)(gurl_base::StringPiece, NumberType*)) { - return CheckRoundtripsT<NumberType, gurl_base::StringPiece, std::string>( - data, size, &gurl_base::NumberToString, string_to_num); -} - -template <class NumberType> -void CheckRoundtrips16(const uint8_t* data, - const size_t size, - bool (*string_to_num)(gurl_base::StringPiece16, - NumberType*)) { - return CheckRoundtripsT<NumberType, gurl_base::StringPiece16, std::u16string>( - data, size, &gurl_base::NumberToString16, string_to_num); -} - -// Entry point for LibFuzzer. -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - // For each instantiation of NumberToString f and its corresponding StringTo* - // function g, check that f(g(x)) = x holds for fuzzer-determined values of x. - CheckRoundtrips<int>(data, size, &gurl_base::StringToInt); - CheckRoundtrips16<int>(data, size, &gurl_base::StringToInt); - CheckRoundtrips<unsigned int>(data, size, &gurl_base::StringToUint); - CheckRoundtrips16<unsigned int>(data, size, &gurl_base::StringToUint); - CheckRoundtrips<int64_t>(data, size, &gurl_base::StringToInt64); - CheckRoundtrips16<int64_t>(data, size, &gurl_base::StringToInt64); - CheckRoundtrips<uint64_t>(data, size, &gurl_base::StringToUint64); - CheckRoundtrips16<uint64_t>(data, size, &gurl_base::StringToUint64); - CheckRoundtrips<size_t>(data, size, &gurl_base::StringToSizeT); - CheckRoundtrips16<size_t>(data, size, &gurl_base::StringToSizeT); - - gurl_base::StringPiece string_piece_input(reinterpret_cast<const char*>(data), - size); - std::string string_input(reinterpret_cast<const char*>(data), size); - - int out_int; - gurl_base::StringToInt(string_piece_input, &out_int); - unsigned out_uint; - gurl_base::StringToUint(string_piece_input, &out_uint); - int64_t out_int64; - gurl_base::StringToInt64(string_piece_input, &out_int64); - uint64_t out_uint64; - gurl_base::StringToUint64(string_piece_input, &out_uint64); - size_t out_size; - gurl_base::StringToSizeT(string_piece_input, &out_size); - - // Test for StringPiece16 if size is even. - if (size % 2 == 0) { - gurl_base::StringPiece16 string_piece_input16( - reinterpret_cast<const char16_t*>(data), size / 2); - - gurl_base::StringToInt(string_piece_input16, &out_int); - gurl_base::StringToUint(string_piece_input16, &out_uint); - gurl_base::StringToInt64(string_piece_input16, &out_int64); - gurl_base::StringToUint64(string_piece_input16, &out_uint64); - gurl_base::StringToSizeT(string_piece_input16, &out_size); - } - - double out_double; - gurl_base::StringToDouble(string_input, &out_double); - - gurl_base::HexStringToInt(string_piece_input, &out_int); - gurl_base::HexStringToUInt(string_piece_input, &out_uint); - gurl_base::HexStringToInt64(string_piece_input, &out_int64); - gurl_base::HexStringToUInt64(string_piece_input, &out_uint64); - std::vector<uint8_t> out_bytes; - gurl_base::HexStringToBytes(string_piece_input, &out_bytes); - - gurl_base::HexEncode(data, size); - - // Convert the numbers back to strings. - gurl_base::NumberToString(out_int); - gurl_base::NumberToString16(out_int); - gurl_base::NumberToString(out_uint); - gurl_base::NumberToString16(out_uint); - gurl_base::NumberToString(out_int64); - gurl_base::NumberToString16(out_int64); - gurl_base::NumberToString(out_uint64); - gurl_base::NumberToString16(out_uint64); - gurl_base::NumberToString(out_double); - gurl_base::NumberToString16(out_double); - - return 0; -}
diff --git a/base/strings/string_tokenizer_fuzzer.cc b/base/strings/string_tokenizer_fuzzer.cc deleted file mode 100644 index a0c0788..0000000 --- a/base/strings/string_tokenizer_fuzzer.cc +++ /dev/null
@@ -1,60 +0,0 @@ -// Copyright 2015 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <stddef.h> -#include <stdint.h> - -#include <string> -#include <tuple> - -#include "base/strings/string_tokenizer.h" - -void GetAllTokens(gurl_base::StringTokenizer& t) { - while (t.GetNext()) { - std::ignore = t.token(); - } -} - -// Entry point for LibFuzzer. -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - uint8_t size_t_bytes = sizeof(size_t); - if (size < size_t_bytes + 1) { - return 0; - } - - // Calculate pattern size based on remaining bytes, otherwise fuzzing is - // inefficient with bailouts in most cases. - size_t pattern_size = - *reinterpret_cast<const size_t*>(data) % (size - size_t_bytes); - - std::string pattern(reinterpret_cast<const char*>(data + size_t_bytes), - pattern_size); - std::string input( - reinterpret_cast<const char*>(data + size_t_bytes + pattern_size), - size - pattern_size - size_t_bytes); - - // Allow quote_chars and options to be set. Otherwise full coverage - // won't be possible since IsQuote, FullGetNext and other functions - // won't be called. - for (bool return_delims : {false, true}) { - for (bool return_empty_strings : {false, true}) { - int options = 0; - if (return_delims) - options |= gurl_base::StringTokenizer::RETURN_DELIMS; - if (return_empty_strings) - options |= gurl_base::StringTokenizer::RETURN_EMPTY_TOKENS; - - gurl_base::StringTokenizer t(input, pattern); - t.set_options(options); - GetAllTokens(t); - - gurl_base::StringTokenizer t_quote(input, pattern); - t_quote.set_quote_chars("\""); - t_quote.set_options(options); - GetAllTokens(t_quote); - } - } - - return 0; -}
diff --git a/base/strings/string_util.cc b/base/strings/string_util.cc index 482bb6e..f92029f 100644 --- a/base/strings/string_util.cc +++ b/base/strings/string_util.cc
@@ -16,13 +16,14 @@ #include <wchar.h> #include <wctype.h> -#include <algorithm> #include <limits> #include <type_traits> #include <vector> #include "polyfills/base/check_op.h" #include "base/no_destructor.h" +#include "base/ranges/algorithm.h" +#include "base/strings/string_util_impl_helpers.h" #include "base/strings/string_util_internal.h" #include "base/strings/utf_string_conversion_utils.h" #include "base/strings/utf_string_conversions.h" @@ -82,14 +83,6 @@ return internal::ToUpperASCIIImpl(str); } -int CompareCaseInsensitiveASCII(StringPiece a, StringPiece b) { - return internal::CompareCaseInsensitiveASCIIT(a, b); -} - -int CompareCaseInsensitiveASCII(StringPiece16 a, StringPiece16 b) { - return internal::CompareCaseInsensitiveASCIIT(a, b); -} - const std::string& EmptyString() { static const gurl_base::NoDestructor<std::string> s; return *s; @@ -256,7 +249,7 @@ } bool EqualsASCII(StringPiece16 str, StringPiece ascii) { - return std::equal(ascii.begin(), ascii.end(), str.begin(), str.end()); + return ranges::equal(ascii, str); } bool StartsWith(StringPiece str,
diff --git a/base/strings/string_util.h b/base/strings/string_util.h index 56cf483..b7bee67 100644 --- a/base/strings/string_util.h +++ b/base/strings/string_util.h
@@ -12,7 +12,6 @@ #include <stddef.h> #include <stdint.h> -#include <algorithm> #include <initializer_list> #include <sstream> #include <string> @@ -25,6 +24,7 @@ #include "base/containers/span.h" #include "base/cxx20_to_address.h" #include "base/strings/string_piece.h" // For implicit conversions. +#include "base/strings/string_util_internal.h" #include "build/build_config.h" namespace gurl_base { @@ -123,8 +123,8 @@ // so we don't want to use it here. template <typename CharT, typename = std::enable_if_t<std::is_integral<CharT>::value>> -CharT ToLowerASCII(CharT c) { - return (c >= 'A' && c <= 'Z') ? (c + ('a' - 'A')) : c; +constexpr CharT ToLowerASCII(CharT c) { + return internal::ToLowerASCII(c); } // ASCII-specific toupper. The standard library's toupper is locale sensitive, @@ -165,19 +165,14 @@ // (unlike strcasecmp which can return values greater or less than 1/-1). For // full Unicode support, use gurl_base::i18n::ToLower or gurl_base::i18n::FoldCase // and then just call the normal string operators on the result. -BASE_EXPORT int CompareCaseInsensitiveASCII(StringPiece a, StringPiece b); -BASE_EXPORT int CompareCaseInsensitiveASCII(StringPiece16 a, StringPiece16 b); - -namespace internal { -template <typename CharT, typename CharU> -inline bool EqualsCaseInsensitiveASCIIT(BasicStringPiece<CharT> a, - BasicStringPiece<CharU> b) { - return std::equal(a.begin(), a.end(), b.begin(), b.end(), - [](auto lhs, auto rhs) { - return ToLowerASCII(lhs) == ToLowerASCII(rhs); - }); +BASE_EXPORT constexpr int CompareCaseInsensitiveASCII(StringPiece a, + StringPiece b) { + return internal::CompareCaseInsensitiveASCIIT(a, b); } -} // namespace internal +BASE_EXPORT constexpr int CompareCaseInsensitiveASCII(StringPiece16 a, + StringPiece16 b) { + return internal::CompareCaseInsensitiveASCIIT(a, b); +} // Equality for ASCII case-insensitive comparisons. For full Unicode support, // use gurl_base::i18n::ToLower or gurl_base::i18n::FoldCase and then compare with either
diff --git a/base/strings/string_util_impl_helpers.h b/base/strings/string_util_impl_helpers.h new file mode 100644 index 0000000..970a912 --- /dev/null +++ b/base/strings/string_util_impl_helpers.h
@@ -0,0 +1,582 @@ +// Copyright 2020 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_STRINGS_STRING_UTIL_IMPL_HELPERS_H_ +#define BASE_STRINGS_STRING_UTIL_IMPL_HELPERS_H_ + +#include <algorithm> + +#include "polyfills/base/check.h" +#include "polyfills/base/check_op.h" +#include "polyfills/base/logging.h" +#include "polyfills/base/notreached.h" +#include "base/ranges/algorithm.h" +#include "base/strings/string_piece.h" +#include "base/third_party/icu/icu_utf.h" + +namespace gurl_base::internal { + +// Used by ReplaceStringPlaceholders to track the position in the string of +// replaced parameters. +struct ReplacementOffset { + ReplacementOffset(uintptr_t parameter, size_t offset) + : parameter(parameter), offset(offset) {} + + // Index of the parameter. + size_t parameter; + + // Starting position in the string. + size_t offset; +}; + +static bool CompareParameter(const ReplacementOffset& elem1, + const ReplacementOffset& elem2) { + return elem1.parameter < elem2.parameter; +} + +// Assuming that a pointer is the size of a "machine word", then +// uintptr_t is an integer type that is also a machine word. +using MachineWord = uintptr_t; + +inline bool IsMachineWordAligned(const void* pointer) { + return !(reinterpret_cast<MachineWord>(pointer) & (sizeof(MachineWord) - 1)); +} + +template <typename T, typename CharT = typename T::value_type> +std::basic_string<CharT> ToLowerASCIIImpl(T str) { + std::basic_string<CharT> ret; + ret.reserve(str.size()); + for (size_t i = 0; i < str.size(); i++) + ret.push_back(ToLowerASCII(str[i])); + return ret; +} + +template <typename T, typename CharT = typename T::value_type> +std::basic_string<CharT> ToUpperASCIIImpl(T str) { + std::basic_string<CharT> ret; + ret.reserve(str.size()); + for (size_t i = 0; i < str.size(); i++) + ret.push_back(ToUpperASCII(str[i])); + return ret; +} + +template <typename T, typename CharT = typename T::value_type> +TrimPositions TrimStringT(T input, + T trim_chars, + TrimPositions positions, + std::basic_string<CharT>* output) { + // Find the edges of leading/trailing whitespace as desired. Need to use + // a StringPiece version of input to be able to call find* on it with the + // StringPiece version of trim_chars (normally the trim_chars will be a + // constant so avoid making a copy). + const size_t last_char = input.length() - 1; + const size_t first_good_char = + (positions & TRIM_LEADING) ? input.find_first_not_of(trim_chars) : 0; + const size_t last_good_char = (positions & TRIM_TRAILING) + ? input.find_last_not_of(trim_chars) + : last_char; + + // When the string was all trimmed, report that we stripped off characters + // from whichever position the caller was interested in. For empty input, we + // stripped no characters, but we still need to clear |output|. + if (input.empty() || first_good_char == std::basic_string<CharT>::npos || + last_good_char == std::basic_string<CharT>::npos) { + bool input_was_empty = input.empty(); // in case output == &input + output->clear(); + return input_was_empty ? TRIM_NONE : positions; + } + + // Trim. + output->assign(input.data() + first_good_char, + last_good_char - first_good_char + 1); + + // Return where we trimmed from. + return static_cast<TrimPositions>( + (first_good_char == 0 ? TRIM_NONE : TRIM_LEADING) | + (last_good_char == last_char ? TRIM_NONE : TRIM_TRAILING)); +} + +template <typename T, typename CharT = typename T::value_type> +T TrimStringPieceT(T input, T trim_chars, TrimPositions positions) { + size_t begin = + (positions & TRIM_LEADING) ? input.find_first_not_of(trim_chars) : 0; + size_t end = (positions & TRIM_TRAILING) + ? input.find_last_not_of(trim_chars) + 1 + : input.size(); + return input.substr(std::min(begin, input.size()), end - begin); +} + +template <typename T, typename CharT = typename T::value_type> +std::basic_string<CharT> CollapseWhitespaceT( + T text, + bool trim_sequences_with_line_breaks) { + std::basic_string<CharT> result; + result.resize(text.size()); + + // Set flags to pretend we're already in a trimmed whitespace sequence, so we + // will trim any leading whitespace. + bool in_whitespace = true; + bool already_trimmed = true; + + size_t chars_written = 0; + for (auto c : text) { + if (IsWhitespace(c)) { + if (!in_whitespace) { + // Reduce all whitespace sequences to a single space. + in_whitespace = true; + result[chars_written++] = L' '; + } + if (trim_sequences_with_line_breaks && !already_trimmed && + ((c == '\n') || (c == '\r'))) { + // Whitespace sequences containing CR or LF are eliminated entirely. + already_trimmed = true; + --chars_written; + } + } else { + // Non-whitespace characters are copied straight across. + in_whitespace = false; + already_trimmed = false; + result[chars_written++] = c; + } + } + + if (in_whitespace && !already_trimmed) { + // Any trailing whitespace is eliminated. + --chars_written; + } + + result.resize(chars_written); + return result; +} + +template <class Char> +bool DoIsStringASCII(const Char* characters, size_t length) { + // Bitmasks to detect non ASCII characters for character sizes of 8, 16 and 32 + // bits. + constexpr MachineWord NonASCIIMasks[] = { + 0, MachineWord(0x8080808080808080ULL), MachineWord(0xFF80FF80FF80FF80ULL), + 0, MachineWord(0xFFFFFF80FFFFFF80ULL), + }; + + if (!length) + return true; + constexpr MachineWord non_ascii_bit_mask = NonASCIIMasks[sizeof(Char)]; + static_assert(non_ascii_bit_mask, "Error: Invalid Mask"); + MachineWord all_char_bits = 0; + const Char* end = characters + length; + + // Prologue: align the input. + while (!IsMachineWordAligned(characters) && characters < end) + all_char_bits |= static_cast<MachineWord>(*characters++); + if (all_char_bits & non_ascii_bit_mask) + return false; + + // Compare the values of CPU word size. + constexpr size_t chars_per_word = sizeof(MachineWord) / sizeof(Char); + constexpr int batch_count = 16; + while (characters <= end - batch_count * chars_per_word) { + all_char_bits = 0; + for (int i = 0; i < batch_count; ++i) { + all_char_bits |= *(reinterpret_cast<const MachineWord*>(characters)); + characters += chars_per_word; + } + if (all_char_bits & non_ascii_bit_mask) + return false; + } + + // Process the remaining words. + all_char_bits = 0; + while (characters <= end - chars_per_word) { + all_char_bits |= *(reinterpret_cast<const MachineWord*>(characters)); + characters += chars_per_word; + } + + // Process the remaining bytes. + while (characters < end) + all_char_bits |= static_cast<MachineWord>(*characters++); + + return !(all_char_bits & non_ascii_bit_mask); +} + +template <bool (*Validator)(base_icu::UChar32)> +inline bool DoIsStringUTF8(StringPiece str) { + const uint8_t* src = reinterpret_cast<const uint8_t*>(str.data()); + size_t src_len = str.length(); + size_t char_index = 0; + + while (char_index < src_len) { + base_icu::UChar32 code_point; + CBU8_NEXT(src, char_index, src_len, code_point); + if (!Validator(code_point)) + return false; + } + return true; +} + +template <typename T, typename CharT = typename T::value_type> +bool StartsWithT(T str, T search_for, CompareCase case_sensitivity) { + if (search_for.size() > str.size()) + return false; + + BasicStringPiece<CharT> source = str.substr(0, search_for.size()); + + switch (case_sensitivity) { + case CompareCase::SENSITIVE: + return source == search_for; + + case CompareCase::INSENSITIVE_ASCII: + return std::equal(search_for.begin(), search_for.end(), source.begin(), + CaseInsensitiveCompareASCII<CharT>()); + + default: + GURL_NOTREACHED(); + return false; + } +} + +template <typename T, typename CharT = typename T::value_type> +bool EndsWithT(T str, T search_for, CompareCase case_sensitivity) { + if (search_for.size() > str.size()) + return false; + + BasicStringPiece<CharT> source = + str.substr(str.size() - search_for.size(), search_for.size()); + + switch (case_sensitivity) { + case CompareCase::SENSITIVE: + return source == search_for; + + case CompareCase::INSENSITIVE_ASCII: + return std::equal(source.begin(), source.end(), search_for.begin(), + CaseInsensitiveCompareASCII<CharT>()); + + default: + GURL_NOTREACHED(); + return false; + } +} + +// A Matcher for DoReplaceMatchesAfterOffset() that matches substrings. +template <class CharT> +struct SubstringMatcher { + BasicStringPiece<CharT> find_this; + + size_t Find(const std::basic_string<CharT>& input, size_t pos) { + return input.find(find_this.data(), pos, find_this.length()); + } + size_t MatchSize() { return find_this.length(); } +}; + +// Type deduction helper for SubstringMatcher. +template <typename T, typename CharT = typename T::value_type> +auto MakeSubstringMatcher(T find_this) { + return SubstringMatcher<CharT>{find_this}; +} + +// A Matcher for DoReplaceMatchesAfterOffset() that matches single characters. +template <class CharT> +struct CharacterMatcher { + BasicStringPiece<CharT> find_any_of_these; + + size_t Find(const std::basic_string<CharT>& input, size_t pos) { + return input.find_first_of(find_any_of_these.data(), pos, + find_any_of_these.length()); + } + constexpr size_t MatchSize() { return 1; } +}; + +// Type deduction helper for CharacterMatcher. +template <typename T, typename CharT = typename T::value_type> +auto MakeCharacterMatcher(T find_any_of_these) { + return CharacterMatcher<CharT>{find_any_of_these}; +} + +enum class ReplaceType { REPLACE_ALL, REPLACE_FIRST }; + +// Runs in O(n) time in the length of |str|, and transforms the string without +// reallocating when possible. Returns |true| if any matches were found. +// +// This is parameterized on a |Matcher| traits type, so that it can be the +// implementation for both ReplaceChars() and ReplaceSubstringsAfterOffset(). +template <typename Matcher, typename T, typename CharT = typename T::value_type> +bool DoReplaceMatchesAfterOffset(std::basic_string<CharT>* str, + size_t initial_offset, + Matcher matcher, + T replace_with, + ReplaceType replace_type) { + using CharTraits = std::char_traits<CharT>; + + const size_t find_length = matcher.MatchSize(); + if (!find_length) + return false; + + // If the find string doesn't appear, there's nothing to do. + size_t first_match = matcher.Find(*str, initial_offset); + if (first_match == std::basic_string<CharT>::npos) + return false; + + // If we're only replacing one instance, there's no need to do anything + // complicated. + const size_t replace_length = replace_with.length(); + if (replace_type == ReplaceType::REPLACE_FIRST) { + str->replace(first_match, find_length, replace_with.data(), replace_length); + return true; + } + + // If the find and replace strings are the same length, we can simply use + // replace() on each instance, and finish the entire operation in O(n) time. + if (find_length == replace_length) { + auto* buffer = &((*str)[0]); + for (size_t offset = first_match; offset != std::basic_string<CharT>::npos; + offset = matcher.Find(*str, offset + replace_length)) { + CharTraits::copy(buffer + offset, replace_with.data(), replace_length); + } + return true; + } + + // Since the find and replace strings aren't the same length, a loop like the + // one above would be O(n^2) in the worst case, as replace() will shift the + // entire remaining string each time. We need to be more clever to keep things + // O(n). + // + // When the string is being shortened, it's possible to just shift the matches + // down in one pass while finding, and truncate the length at the end of the + // search. + // + // If the string is being lengthened, more work is required. The strategy used + // here is to make two find() passes through the string. The first pass counts + // the number of matches to determine the new size. The second pass will + // either construct the new string into a new buffer (if the existing buffer + // lacked capacity), or else -- if there is room -- create a region of scratch + // space after |first_match| by shifting the tail of the string to a higher + // index, and doing in-place moves from the tail to lower indices thereafter. + size_t str_length = str->length(); + size_t expansion = 0; + if (replace_length > find_length) { + // This operation lengthens the string; determine the new length by counting + // matches. + const size_t expansion_per_match = (replace_length - find_length); + size_t num_matches = 0; + for (size_t match = first_match; match != std::basic_string<CharT>::npos; + match = matcher.Find(*str, match + find_length)) { + expansion += expansion_per_match; + ++num_matches; + } + const size_t final_length = str_length + expansion; + + if (str->capacity() < final_length) { + // If we'd have to allocate a new buffer to grow the string, build the + // result directly into the new allocation via append(). + std::basic_string<CharT> src(str->get_allocator()); + str->swap(src); + str->reserve(final_length); + + size_t pos = 0; + for (size_t match = first_match;; match = matcher.Find(src, pos)) { + str->append(src, pos, match - pos); + str->append(replace_with.data(), replace_length); + pos = match + find_length; + + // A mid-loop test/break enables skipping the final Find() call; the + // number of matches is known, so don't search past the last one. + if (!--num_matches) + break; + } + + // Handle substring after the final match. + str->append(src, pos, str_length - pos); + return true; + } + + // Prepare for the copy/move loop below -- expand the string to its final + // size by shifting the data after the first match to the end of the resized + // string. + size_t shift_src = first_match + find_length; + size_t shift_dst = shift_src + expansion; + + // Big |expansion| factors (relative to |str_length|) require padding up to + // |shift_dst|. + if (shift_dst > str_length) + str->resize(shift_dst); + + str->replace(shift_dst, str_length - shift_src, *str, shift_src, + str_length - shift_src); + str_length = final_length; + } + + // We can alternate replacement and move operations. This won't overwrite the + // unsearched region of the string so long as |write_offset| <= |read_offset|; + // that condition is always satisfied because: + // + // (a) If the string is being shortened, |expansion| is zero and + // |write_offset| grows slower than |read_offset|. + // + // (b) If the string is being lengthened, |write_offset| grows faster than + // |read_offset|, but |expansion| is big enough so that |write_offset| + // will only catch up to |read_offset| at the point of the last match. + auto* buffer = &((*str)[0]); + size_t write_offset = first_match; + size_t read_offset = first_match + expansion; + do { + if (replace_length) { + CharTraits::copy(buffer + write_offset, replace_with.data(), + replace_length); + write_offset += replace_length; + } + read_offset += find_length; + + // min() clamps std::basic_string<CharT>::npos (the largest unsigned value) + // to str_length. + size_t match = std::min(matcher.Find(*str, read_offset), str_length); + + size_t length = match - read_offset; + if (length) { + CharTraits::move(buffer + write_offset, buffer + read_offset, length); + write_offset += length; + read_offset += length; + } + } while (read_offset < str_length); + + // If we're shortening the string, truncate it now. + str->resize(write_offset); + return true; +} + +template <typename T, typename CharT = typename T::value_type> +bool ReplaceCharsT(T input, + T find_any_of_these, + T replace_with, + std::basic_string<CharT>* output) { + // Commonly, this is called with output and input being the same string; in + // that case, skip the copy. + if (input.data() != output->data() || input.size() != output->size()) + output->assign(input.data(), input.size()); + + return DoReplaceMatchesAfterOffset(output, 0, + MakeCharacterMatcher(find_any_of_these), + replace_with, ReplaceType::REPLACE_ALL); +} + +template <class string_type> +inline typename string_type::value_type* WriteIntoT(string_type* str, + size_t length_with_null) { + GURL_DCHECK_GE(length_with_null, 1u); + str->reserve(length_with_null); + str->resize(length_with_null - 1); + return &((*str)[0]); +} + +// Generic version for all JoinString overloads. |list_type| must be a sequence +// (gurl_base::span or std::initializer_list) of strings/StringPieces (std::string, +// std::u16string, StringPiece or StringPiece16). |CharT| is either char or +// char16_t. +template <typename list_type, + typename T, + typename CharT = typename T::value_type> +static std::basic_string<CharT> JoinStringT(list_type parts, T sep) { + if (std::empty(parts)) + return std::basic_string<CharT>(); + + // Pre-allocate the eventual size of the string. Start with the size of all of + // the separators (note that this *assumes* parts.size() > 0). + size_t total_size = (parts.size() - 1) * sep.size(); + for (const auto& part : parts) + total_size += part.size(); + std::basic_string<CharT> result; + result.reserve(total_size); + + auto iter = parts.begin(); + GURL_DCHECK(iter != parts.end()); + result.append(iter->data(), iter->size()); + ++iter; + + for (; iter != parts.end(); ++iter) { + result.append(sep.data(), sep.size()); + result.append(iter->data(), iter->size()); + } + + // Sanity-check that we pre-allocated correctly. + GURL_DCHECK_EQ(total_size, result.size()); + + return result; +} + +template <typename T, typename CharT = typename T::value_type> +std::basic_string<CharT> DoReplaceStringPlaceholders( + T format_string, + const std::vector<std::basic_string<CharT>>& subst, + std::vector<size_t>* offsets) { + size_t substitutions = subst.size(); + GURL_DCHECK_LT(substitutions, 11U); + + size_t sub_length = 0; + for (const auto& cur : subst) + sub_length += cur.length(); + + std::basic_string<CharT> formatted; + formatted.reserve(format_string.length() + sub_length); + + std::vector<ReplacementOffset> r_offsets; + for (auto i = format_string.begin(); i != format_string.end(); ++i) { + if ('$' == *i) { + if (i + 1 != format_string.end()) { + ++i; + if ('$' == *i) { + while (i != format_string.end() && '$' == *i) { + formatted.push_back('$'); + ++i; + } + --i; + } else { + if (*i < '1' || *i > '9') { + GURL_DLOG(ERROR) << "Invalid placeholder: $" + << std::basic_string<CharT>(1, *i); + continue; + } + size_t index = static_cast<size_t>(*i - '1'); + if (offsets) { + ReplacementOffset r_offset(index, formatted.size()); + r_offsets.insert( + ranges::upper_bound(r_offsets, r_offset, &CompareParameter), + r_offset); + } + if (index < substitutions) + formatted.append(subst.at(index)); + } + } + } else { + formatted.push_back(*i); + } + } + if (offsets) { + for (const auto& cur : r_offsets) + offsets->push_back(cur.offset); + } + return formatted; +} + +// The following code is compatible with the OpenBSD lcpy interface. See: +// http://www.gratisoft.us/todd/papers/strlcpy.html +// ftp://ftp.openbsd.org/pub/OpenBSD/src/lib/libc/string/{wcs,str}lcpy.c + +template <typename CHAR> +size_t lcpyT(CHAR* dst, const CHAR* src, size_t dst_size) { + for (size_t i = 0; i < dst_size; ++i) { + if ((dst[i] = src[i]) == 0) // We hit and copied the terminating NULL. + return i; + } + + // We were left off at dst_size. We over copied 1 byte. Null terminate. + if (dst_size != 0) + dst[dst_size - 1] = 0; + + // Count the rest of the |src|, and return it's length in characters. + while (src[dst_size]) + ++dst_size; + return dst_size; +} + +} // namespace gurl_base::internal + +#endif // BASE_STRINGS_STRING_UTIL_IMPL_HELPERS_H_
diff --git a/base/strings/string_util_internal.h b/base/strings/string_util_internal.h index 1d6a41e..3a493dd 100644 --- a/base/strings/string_util_internal.h +++ b/base/strings/string_util_internal.h
@@ -5,66 +5,21 @@ #ifndef BASE_STRINGS_STRING_UTIL_INTERNAL_H_ #define BASE_STRINGS_STRING_UTIL_INTERNAL_H_ -#include <algorithm> - -#include "polyfills/base/check.h" -#include "polyfills/base/check_op.h" -#include "polyfills/base/logging.h" -#include "polyfills/base/notreached.h" #include "base/ranges/algorithm.h" #include "base/strings/string_piece.h" -#include "base/third_party/icu/icu_utf.h" -namespace gurl_base { +namespace gurl_base::internal { -namespace internal { - -// Used by ReplaceStringPlaceholders to track the position in the string of -// replaced parameters. -struct ReplacementOffset { - ReplacementOffset(uintptr_t parameter, size_t offset) - : parameter(parameter), offset(offset) {} - - // Index of the parameter. - size_t parameter; - - // Starting position in the string. - size_t offset; -}; - -static bool CompareParameter(const ReplacementOffset& elem1, - const ReplacementOffset& elem2) { - return elem1.parameter < elem2.parameter; -} - -// Assuming that a pointer is the size of a "machine word", then -// uintptr_t is an integer type that is also a machine word. -using MachineWord = uintptr_t; - -inline bool IsMachineWordAligned(const void* pointer) { - return !(reinterpret_cast<MachineWord>(pointer) & (sizeof(MachineWord) - 1)); +// ASCII-specific tolower. The standard library's tolower is locale sensitive, +// so we don't want to use it here. +template <typename CharT, + typename = std::enable_if_t<std::is_integral<CharT>::value>> +constexpr CharT ToLowerASCII(CharT c) { + return (c >= 'A' && c <= 'Z') ? (c + ('a' - 'A')) : c; } template <typename T, typename CharT = typename T::value_type> -std::basic_string<CharT> ToLowerASCIIImpl(T str) { - std::basic_string<CharT> ret; - ret.reserve(str.size()); - for (size_t i = 0; i < str.size(); i++) - ret.push_back(ToLowerASCII(str[i])); - return ret; -} - -template <typename T, typename CharT = typename T::value_type> -std::basic_string<CharT> ToUpperASCIIImpl(T str) { - std::basic_string<CharT> ret; - ret.reserve(str.size()); - for (size_t i = 0; i < str.size(); i++) - ret.push_back(ToUpperASCII(str[i])); - return ret; -} - -template <typename T, typename CharT = typename T::value_type> -int CompareCaseInsensitiveASCIIT(T a, T b) { +constexpr int CompareCaseInsensitiveASCIIT(T a, T b) { // Find the first characters that aren't equal and compare them. If the end // of one of the strings is found before a nonequal character, the lengths // of the strings are compared. @@ -89,524 +44,14 @@ return 1; } -template <typename T, typename CharT = typename T::value_type> -TrimPositions TrimStringT(T input, - T trim_chars, - TrimPositions positions, - std::basic_string<CharT>* output) { - // Find the edges of leading/trailing whitespace as desired. Need to use - // a StringPiece version of input to be able to call find* on it with the - // StringPiece version of trim_chars (normally the trim_chars will be a - // constant so avoid making a copy). - const size_t last_char = input.length() - 1; - const size_t first_good_char = - (positions & TRIM_LEADING) ? input.find_first_not_of(trim_chars) : 0; - const size_t last_good_char = (positions & TRIM_TRAILING) - ? input.find_last_not_of(trim_chars) - : last_char; - - // When the string was all trimmed, report that we stripped off characters - // from whichever position the caller was interested in. For empty input, we - // stripped no characters, but we still need to clear |output|. - if (input.empty() || first_good_char == std::basic_string<CharT>::npos || - last_good_char == std::basic_string<CharT>::npos) { - bool input_was_empty = input.empty(); // in case output == &input - output->clear(); - return input_was_empty ? TRIM_NONE : positions; - } - - // Trim. - output->assign(input.data() + first_good_char, - last_good_char - first_good_char + 1); - - // Return where we trimmed from. - return static_cast<TrimPositions>( - (first_good_char == 0 ? TRIM_NONE : TRIM_LEADING) | - (last_good_char == last_char ? TRIM_NONE : TRIM_TRAILING)); +template <typename CharT, typename CharU> +inline bool EqualsCaseInsensitiveASCIIT(BasicStringPiece<CharT> a, + BasicStringPiece<CharU> b) { + return ranges::equal(a, b, [](auto lhs, auto rhs) { + return ToLowerASCII(lhs) == ToLowerASCII(rhs); + }); } -template <typename T, typename CharT = typename T::value_type> -T TrimStringPieceT(T input, T trim_chars, TrimPositions positions) { - size_t begin = - (positions & TRIM_LEADING) ? input.find_first_not_of(trim_chars) : 0; - size_t end = (positions & TRIM_TRAILING) - ? input.find_last_not_of(trim_chars) + 1 - : input.size(); - return input.substr(std::min(begin, input.size()), end - begin); -} - -template <typename T, typename CharT = typename T::value_type> -std::basic_string<CharT> CollapseWhitespaceT( - T text, - bool trim_sequences_with_line_breaks) { - std::basic_string<CharT> result; - result.resize(text.size()); - - // Set flags to pretend we're already in a trimmed whitespace sequence, so we - // will trim any leading whitespace. - bool in_whitespace = true; - bool already_trimmed = true; - - size_t chars_written = 0; - for (auto c : text) { - if (IsWhitespace(c)) { - if (!in_whitespace) { - // Reduce all whitespace sequences to a single space. - in_whitespace = true; - result[chars_written++] = L' '; - } - if (trim_sequences_with_line_breaks && !already_trimmed && - ((c == '\n') || (c == '\r'))) { - // Whitespace sequences containing CR or LF are eliminated entirely. - already_trimmed = true; - --chars_written; - } - } else { - // Non-whitespace characters are copied straight across. - in_whitespace = false; - already_trimmed = false; - result[chars_written++] = c; - } - } - - if (in_whitespace && !already_trimmed) { - // Any trailing whitespace is eliminated. - --chars_written; - } - - result.resize(chars_written); - return result; -} - -template <class Char> -bool DoIsStringASCII(const Char* characters, size_t length) { - // Bitmasks to detect non ASCII characters for character sizes of 8, 16 and 32 - // bits. - constexpr MachineWord NonASCIIMasks[] = { - 0, MachineWord(0x8080808080808080ULL), MachineWord(0xFF80FF80FF80FF80ULL), - 0, MachineWord(0xFFFFFF80FFFFFF80ULL), - }; - - if (!length) - return true; - constexpr MachineWord non_ascii_bit_mask = NonASCIIMasks[sizeof(Char)]; - static_assert(non_ascii_bit_mask, "Error: Invalid Mask"); - MachineWord all_char_bits = 0; - const Char* end = characters + length; - - // Prologue: align the input. - while (!IsMachineWordAligned(characters) && characters < end) - all_char_bits |= static_cast<MachineWord>(*characters++); - if (all_char_bits & non_ascii_bit_mask) - return false; - - // Compare the values of CPU word size. - constexpr size_t chars_per_word = sizeof(MachineWord) / sizeof(Char); - constexpr int batch_count = 16; - while (characters <= end - batch_count * chars_per_word) { - all_char_bits = 0; - for (int i = 0; i < batch_count; ++i) { - all_char_bits |= *(reinterpret_cast<const MachineWord*>(characters)); - characters += chars_per_word; - } - if (all_char_bits & non_ascii_bit_mask) - return false; - } - - // Process the remaining words. - all_char_bits = 0; - while (characters <= end - chars_per_word) { - all_char_bits |= *(reinterpret_cast<const MachineWord*>(characters)); - characters += chars_per_word; - } - - // Process the remaining bytes. - while (characters < end) - all_char_bits |= static_cast<MachineWord>(*characters++); - - return !(all_char_bits & non_ascii_bit_mask); -} - -template <bool (*Validator)(base_icu::UChar32)> -inline bool DoIsStringUTF8(StringPiece str) { - const uint8_t* src = reinterpret_cast<const uint8_t*>(str.data()); - size_t src_len = str.length(); - size_t char_index = 0; - - while (char_index < src_len) { - base_icu::UChar32 code_point; - CBU8_NEXT(src, char_index, src_len, code_point); - if (!Validator(code_point)) - return false; - } - return true; -} - -template <typename T, typename CharT = typename T::value_type> -bool StartsWithT(T str, T search_for, CompareCase case_sensitivity) { - if (search_for.size() > str.size()) - return false; - - BasicStringPiece<CharT> source = str.substr(0, search_for.size()); - - switch (case_sensitivity) { - case CompareCase::SENSITIVE: - return source == search_for; - - case CompareCase::INSENSITIVE_ASCII: - return std::equal(search_for.begin(), search_for.end(), source.begin(), - CaseInsensitiveCompareASCII<CharT>()); - - default: - GURL_NOTREACHED(); - return false; - } -} - -template <typename T, typename CharT = typename T::value_type> -bool EndsWithT(T str, T search_for, CompareCase case_sensitivity) { - if (search_for.size() > str.size()) - return false; - - BasicStringPiece<CharT> source = - str.substr(str.size() - search_for.size(), search_for.size()); - - switch (case_sensitivity) { - case CompareCase::SENSITIVE: - return source == search_for; - - case CompareCase::INSENSITIVE_ASCII: - return std::equal(source.begin(), source.end(), search_for.begin(), - CaseInsensitiveCompareASCII<CharT>()); - - default: - GURL_NOTREACHED(); - return false; - } -} - -// A Matcher for DoReplaceMatchesAfterOffset() that matches substrings. -template <class CharT> -struct SubstringMatcher { - BasicStringPiece<CharT> find_this; - - size_t Find(const std::basic_string<CharT>& input, size_t pos) { - return input.find(find_this.data(), pos, find_this.length()); - } - size_t MatchSize() { return find_this.length(); } -}; - -// Type deduction helper for SubstringMatcher. -template <typename T, typename CharT = typename T::value_type> -auto MakeSubstringMatcher(T find_this) { - return SubstringMatcher<CharT>{find_this}; -} - -// A Matcher for DoReplaceMatchesAfterOffset() that matches single characters. -template <class CharT> -struct CharacterMatcher { - BasicStringPiece<CharT> find_any_of_these; - - size_t Find(const std::basic_string<CharT>& input, size_t pos) { - return input.find_first_of(find_any_of_these.data(), pos, - find_any_of_these.length()); - } - constexpr size_t MatchSize() { return 1; } -}; - -// Type deduction helper for CharacterMatcher. -template <typename T, typename CharT = typename T::value_type> -auto MakeCharacterMatcher(T find_any_of_these) { - return CharacterMatcher<CharT>{find_any_of_these}; -} - -enum class ReplaceType { REPLACE_ALL, REPLACE_FIRST }; - -// Runs in O(n) time in the length of |str|, and transforms the string without -// reallocating when possible. Returns |true| if any matches were found. -// -// This is parameterized on a |Matcher| traits type, so that it can be the -// implementation for both ReplaceChars() and ReplaceSubstringsAfterOffset(). -template <typename Matcher, typename T, typename CharT = typename T::value_type> -bool DoReplaceMatchesAfterOffset(std::basic_string<CharT>* str, - size_t initial_offset, - Matcher matcher, - T replace_with, - ReplaceType replace_type) { - using CharTraits = std::char_traits<CharT>; - - const size_t find_length = matcher.MatchSize(); - if (!find_length) - return false; - - // If the find string doesn't appear, there's nothing to do. - size_t first_match = matcher.Find(*str, initial_offset); - if (first_match == std::basic_string<CharT>::npos) - return false; - - // If we're only replacing one instance, there's no need to do anything - // complicated. - const size_t replace_length = replace_with.length(); - if (replace_type == ReplaceType::REPLACE_FIRST) { - str->replace(first_match, find_length, replace_with.data(), replace_length); - return true; - } - - // If the find and replace strings are the same length, we can simply use - // replace() on each instance, and finish the entire operation in O(n) time. - if (find_length == replace_length) { - auto* buffer = &((*str)[0]); - for (size_t offset = first_match; offset != std::basic_string<CharT>::npos; - offset = matcher.Find(*str, offset + replace_length)) { - CharTraits::copy(buffer + offset, replace_with.data(), replace_length); - } - return true; - } - - // Since the find and replace strings aren't the same length, a loop like the - // one above would be O(n^2) in the worst case, as replace() will shift the - // entire remaining string each time. We need to be more clever to keep things - // O(n). - // - // When the string is being shortened, it's possible to just shift the matches - // down in one pass while finding, and truncate the length at the end of the - // search. - // - // If the string is being lengthened, more work is required. The strategy used - // here is to make two find() passes through the string. The first pass counts - // the number of matches to determine the new size. The second pass will - // either construct the new string into a new buffer (if the existing buffer - // lacked capacity), or else -- if there is room -- create a region of scratch - // space after |first_match| by shifting the tail of the string to a higher - // index, and doing in-place moves from the tail to lower indices thereafter. - size_t str_length = str->length(); - size_t expansion = 0; - if (replace_length > find_length) { - // This operation lengthens the string; determine the new length by counting - // matches. - const size_t expansion_per_match = (replace_length - find_length); - size_t num_matches = 0; - for (size_t match = first_match; match != std::basic_string<CharT>::npos; - match = matcher.Find(*str, match + find_length)) { - expansion += expansion_per_match; - ++num_matches; - } - const size_t final_length = str_length + expansion; - - if (str->capacity() < final_length) { - // If we'd have to allocate a new buffer to grow the string, build the - // result directly into the new allocation via append(). - std::basic_string<CharT> src(str->get_allocator()); - str->swap(src); - str->reserve(final_length); - - size_t pos = 0; - for (size_t match = first_match;; match = matcher.Find(src, pos)) { - str->append(src, pos, match - pos); - str->append(replace_with.data(), replace_length); - pos = match + find_length; - - // A mid-loop test/break enables skipping the final Find() call; the - // number of matches is known, so don't search past the last one. - if (!--num_matches) - break; - } - - // Handle substring after the final match. - str->append(src, pos, str_length - pos); - return true; - } - - // Prepare for the copy/move loop below -- expand the string to its final - // size by shifting the data after the first match to the end of the resized - // string. - size_t shift_src = first_match + find_length; - size_t shift_dst = shift_src + expansion; - - // Big |expansion| factors (relative to |str_length|) require padding up to - // |shift_dst|. - if (shift_dst > str_length) - str->resize(shift_dst); - - str->replace(shift_dst, str_length - shift_src, *str, shift_src, - str_length - shift_src); - str_length = final_length; - } - - // We can alternate replacement and move operations. This won't overwrite the - // unsearched region of the string so long as |write_offset| <= |read_offset|; - // that condition is always satisfied because: - // - // (a) If the string is being shortened, |expansion| is zero and - // |write_offset| grows slower than |read_offset|. - // - // (b) If the string is being lengthened, |write_offset| grows faster than - // |read_offset|, but |expansion| is big enough so that |write_offset| - // will only catch up to |read_offset| at the point of the last match. - auto* buffer = &((*str)[0]); - size_t write_offset = first_match; - size_t read_offset = first_match + expansion; - do { - if (replace_length) { - CharTraits::copy(buffer + write_offset, replace_with.data(), - replace_length); - write_offset += replace_length; - } - read_offset += find_length; - - // min() clamps std::basic_string<CharT>::npos (the largest unsigned value) - // to str_length. - size_t match = std::min(matcher.Find(*str, read_offset), str_length); - - size_t length = match - read_offset; - if (length) { - CharTraits::move(buffer + write_offset, buffer + read_offset, length); - write_offset += length; - read_offset += length; - } - } while (read_offset < str_length); - - // If we're shortening the string, truncate it now. - str->resize(write_offset); - return true; -} - -template <typename T, typename CharT = typename T::value_type> -bool ReplaceCharsT(T input, - T find_any_of_these, - T replace_with, - std::basic_string<CharT>* output) { - // Commonly, this is called with output and input being the same string; in - // that case, skip the copy. - if (input.data() != output->data() || input.size() != output->size()) - output->assign(input.data(), input.size()); - - return DoReplaceMatchesAfterOffset(output, 0, - MakeCharacterMatcher(find_any_of_these), - replace_with, ReplaceType::REPLACE_ALL); -} - -template <class string_type> -inline typename string_type::value_type* WriteIntoT(string_type* str, - size_t length_with_null) { - GURL_DCHECK_GE(length_with_null, 1u); - str->reserve(length_with_null); - str->resize(length_with_null - 1); - return &((*str)[0]); -} - -// Generic version for all JoinString overloads. |list_type| must be a sequence -// (gurl_base::span or std::initializer_list) of strings/StringPieces (std::string, -// std::u16string, StringPiece or StringPiece16). |CharT| is either char or -// char16_t. -template <typename list_type, - typename T, - typename CharT = typename T::value_type> -static std::basic_string<CharT> JoinStringT(list_type parts, T sep) { - if (std::empty(parts)) - return std::basic_string<CharT>(); - - // Pre-allocate the eventual size of the string. Start with the size of all of - // the separators (note that this *assumes* parts.size() > 0). - size_t total_size = (parts.size() - 1) * sep.size(); - for (const auto& part : parts) - total_size += part.size(); - std::basic_string<CharT> result; - result.reserve(total_size); - - auto iter = parts.begin(); - GURL_DCHECK(iter != parts.end()); - result.append(iter->data(), iter->size()); - ++iter; - - for (; iter != parts.end(); ++iter) { - result.append(sep.data(), sep.size()); - result.append(iter->data(), iter->size()); - } - - // Sanity-check that we pre-allocated correctly. - GURL_DCHECK_EQ(total_size, result.size()); - - return result; -} - -template <typename T, typename CharT = typename T::value_type> -std::basic_string<CharT> DoReplaceStringPlaceholders( - T format_string, - const std::vector<std::basic_string<CharT>>& subst, - std::vector<size_t>* offsets) { - size_t substitutions = subst.size(); - GURL_DCHECK_LT(substitutions, 11U); - - size_t sub_length = 0; - for (const auto& cur : subst) - sub_length += cur.length(); - - std::basic_string<CharT> formatted; - formatted.reserve(format_string.length() + sub_length); - - std::vector<ReplacementOffset> r_offsets; - for (auto i = format_string.begin(); i != format_string.end(); ++i) { - if ('$' == *i) { - if (i + 1 != format_string.end()) { - ++i; - if ('$' == *i) { - while (i != format_string.end() && '$' == *i) { - formatted.push_back('$'); - ++i; - } - --i; - } else { - if (*i < '1' || *i > '9') { - GURL_DLOG(ERROR) << "Invalid placeholder: $" - << std::basic_string<CharT>(1, *i); - continue; - } - size_t index = static_cast<size_t>(*i - '1'); - if (offsets) { - ReplacementOffset r_offset(index, formatted.size()); - r_offsets.insert( - ranges::upper_bound(r_offsets, r_offset, &CompareParameter), - r_offset); - } - if (index < substitutions) - formatted.append(subst.at(index)); - } - } - } else { - formatted.push_back(*i); - } - } - if (offsets) { - for (const auto& cur : r_offsets) - offsets->push_back(cur.offset); - } - return formatted; -} - -// The following code is compatible with the OpenBSD lcpy interface. See: -// http://www.gratisoft.us/todd/papers/strlcpy.html -// ftp://ftp.openbsd.org/pub/OpenBSD/src/lib/libc/string/{wcs,str}lcpy.c - -template <typename CHAR> -size_t lcpyT(CHAR* dst, const CHAR* src, size_t dst_size) { - for (size_t i = 0; i < dst_size; ++i) { - if ((dst[i] = src[i]) == 0) // We hit and copied the terminating NULL. - return i; - } - - // We were left off at dst_size. We over copied 1 byte. Null terminate. - if (dst_size != 0) - dst[dst_size - 1] = 0; - - // Count the rest of the |src|, and return it's length in characters. - while (src[dst_size]) - ++dst_size; - return dst_size; -} - -} // namespace internal - -} // namespace base +} // namespace gurl_base::internal #endif // BASE_STRINGS_STRING_UTIL_INTERNAL_H_
diff --git a/base/strings/string_util_unittest.cc b/base/strings/string_util_unittest.cc index d4326c7..4109f8e 100644 --- a/base/strings/string_util_unittest.cc +++ b/base/strings/string_util_unittest.cc
@@ -1474,6 +1474,14 @@ // Differing values. EXPECT_EQ(-1, CompareCaseInsensitiveASCII("AsdfA", "aSDfb")); EXPECT_EQ(1, CompareCaseInsensitiveASCII("Asdfb", "aSDfA")); + + // For constexpr. + static_assert(CompareCaseInsensitiveASCII("", "") == 0); + static_assert(CompareCaseInsensitiveASCII("Asdf", "aSDf") == 0); + static_assert(CompareCaseInsensitiveASCII("Asdf", "aSDfA") == -1); + static_assert(CompareCaseInsensitiveASCII("AsdfA", "aSDf") == 1); + static_assert(CompareCaseInsensitiveASCII("AsdfA", "aSDfb") == -1); + static_assert(CompareCaseInsensitiveASCII("Asdfb", "aSDfA") == 1); } TEST(StringUtilTest, EqualsCaseInsensitiveASCII) {
diff --git a/base/strings/string_util_win.cc b/base/strings/string_util_win.cc index f8d0243..c3569b3 100644 --- a/base/strings/string_util_win.cc +++ b/base/strings/string_util_win.cc
@@ -4,7 +4,8 @@ #include "base/strings/string_util_win.h" -#include "base/strings/string_util_internal.h" +#include "base/ranges/algorithm.h" +#include "base/strings/string_util_impl_helpers.h" namespace gurl_base { @@ -72,7 +73,7 @@ } bool EqualsASCII(WStringPiece str, StringPiece ascii) { - return std::equal(ascii.begin(), ascii.end(), str.begin(), str.end()); + return ranges::equal(ascii, str); } bool StartsWith(WStringPiece str,
diff --git a/base/strings/utf_string_conversions_fuzzer.cc b/base/strings/utf_string_conversions_fuzzer.cc deleted file mode 100644 index 3cc4521..0000000 --- a/base/strings/utf_string_conversions_fuzzer.cc +++ /dev/null
@@ -1,58 +0,0 @@ -// Copyright 2018 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <tuple> - -#include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.h" - -std::string output_std_string; -std::wstring output_std_wstring; -std::u16string output_string16; - -// Entry point for LibFuzzer. -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - gurl_base::StringPiece string_piece_input(reinterpret_cast<const char*>(data), - size); - - std::ignore = gurl_base::UTF8ToWide(string_piece_input); - gurl_base::UTF8ToWide(reinterpret_cast<const char*>(data), size, - &output_std_wstring); - std::ignore = gurl_base::UTF8ToUTF16(string_piece_input); - gurl_base::UTF8ToUTF16(reinterpret_cast<const char*>(data), size, - &output_string16); - - // Test for char16_t. - if (size % 2 == 0) { - gurl_base::StringPiece16 string_piece_input16( - reinterpret_cast<const char16_t*>(data), size / 2); - std::ignore = gurl_base::UTF16ToWide(output_string16); - gurl_base::UTF16ToWide(reinterpret_cast<const char16_t*>(data), size / 2, - &output_std_wstring); - std::ignore = gurl_base::UTF16ToUTF8(string_piece_input16); - gurl_base::UTF16ToUTF8(reinterpret_cast<const char16_t*>(data), size / 2, - &output_std_string); - } - - // Test for wchar_t. - size_t wchar_t_size = sizeof(wchar_t); - if (size % wchar_t_size == 0) { - std::ignore = gurl_base::WideToUTF8(output_std_wstring); - gurl_base::WideToUTF8(reinterpret_cast<const wchar_t*>(data), - size / wchar_t_size, &output_std_string); - std::ignore = gurl_base::WideToUTF16(output_std_wstring); - gurl_base::WideToUTF16(reinterpret_cast<const wchar_t*>(data), - size / wchar_t_size, &output_string16); - } - - // Test for ASCII. This condition is needed to avoid hitting instant GURL_CHECK - // failures. - if (gurl_base::IsStringASCII(string_piece_input)) { - output_string16 = gurl_base::ASCIIToUTF16(string_piece_input); - gurl_base::StringPiece16 string_piece_input16(output_string16); - std::ignore = gurl_base::UTF16ToASCII(string_piece_input16); - } - - return 0; -}
diff --git a/build/build_config.h b/build/build_config.h index 8f90f47..6811872 100644 --- a/build/build_config.h +++ b/build/build_config.h
@@ -245,6 +245,12 @@ #define BUILDFLAG_INTERNAL_IS_WIN() (0) #endif +#if defined(USE_OZONE) +#define BUILDFLAG_INTERNAL_IS_OZONE() (1) +#else +#define BUILDFLAG_INTERNAL_IS_OZONE() (0) +#endif + // Compiler detection. Note: clang masquerades as GCC on POSIX and as MSVC on // Windows. #if defined(__GNUC__)
diff --git a/copy.bara.sky b/copy.bara.sky index f9d1a2a..89f70ce 100644 --- a/copy.bara.sky +++ b/copy.bara.sky
@@ -24,9 +24,10 @@ "base/debug/crash_logging.cc", "base/debug/crash_logging.h", "base/debug/leak_annotations.h", - "base/functional/*.h", + "base/functional/identity.h", + "base/functional/invoke.h", + "base/functional/not_fn.h", "base/i18n/uchar.h", - "base/memory/raw_ptr.h", "base/memory/raw_ptr_exclusion.h", "base/numerics/*.h", "base/no_destructor.h", @@ -47,6 +48,7 @@ ], exclude = [ "url/url_idna_icu_alternatives_android.cc", + "**/*_fuzzer.cc", ], ) @@ -72,10 +74,11 @@ "base/check_op.h", "base/component_export.h", "base/cpu_reduction_experiment.h", - "base/dcheck_is_on.h", + #"base/dcheck_is_on.h", "base/debug/alias.h", "base/export_template.h", "base/logging.h", + "base/memory/raw_ptr.h", "base/notreached.h", "base/trace_event/memory_usage_estimator.h", "third_party/perfetto/include/perfetto/tracing/traced_value.h", @@ -107,7 +110,6 @@ core.replace("#include \"base/strings/string_number_conversions_win.h\"", ""), core.replace("#include \"base/allocator/partition_allocator/partition_alloc_config.h\"", ""), - core.replace("base/trace_event/base_tracing_forward.h", "polyfills/third_party/perfetto/include/perfetto/tracing/traced_value.h"), # Use system ICU. core.replace(
diff --git a/polyfills/BUILD b/polyfills/BUILD index 6a58eec..ea1b73e 100644 --- a/polyfills/BUILD +++ b/polyfills/BUILD
@@ -16,6 +16,7 @@ "base/debug/alias.h", "base/export_template.h", "base/logging.h", + "base/memory/raw_ptr.h", "base/metrics/histogram_macros.h", "base/notreached.h", "base/trace_event/memory_usage_estimator.h",
diff --git a/polyfills/base/memory/raw_ptr.h b/polyfills/base/memory/raw_ptr.h new file mode 100644 index 0000000..ec0e526 --- /dev/null +++ b/polyfills/base/memory/raw_ptr.h
@@ -0,0 +1,17 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef POLYFILLS_BASE_MEMORY_RAW_PTR_H_ +#define POLYFILLS_BASE_MEMORY_RAW_PTR_H_ + +namespace gurl_base { + +template<typename T> +using raw_ptr = T*; + +} // namespace gurl_base + +using gurl_base::raw_ptr; + +#endif // POLYFILLS_BASE_MEMORY_RAW_PTR_H_
diff --git a/url/gurl_fuzzer.cc b/url/gurl_fuzzer.cc deleted file mode 100644 index 170de8a..0000000 --- a/url/gurl_fuzzer.cc +++ /dev/null
@@ -1,89 +0,0 @@ -// Copyright 2015 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/at_exit.h" -#include "polyfills/base/check_op.h" -#include "base/i18n/icu_util.h" -#include "base/no_destructor.h" -#include "url/gurl.h" - -struct TestCase { - TestCase() { GURL_CHECK(gurl_base::i18n::InitializeICU()); } - - // used by ICU integration. - gurl_base::AtExitManager at_exit_manager; -}; - -TestCase* test_case = new TestCase(); - -// Checks that GURL's canonicalization is idempotent. This can help discover -// issues like https://crbug.com/1128999. -void CheckIdempotency(const GURL& url) { - if (!url.is_valid()) - return; - const std::string& spec = url.spec(); - GURL recanonicalized(spec); - GURL_CHECK(recanonicalized.is_valid()); - GURL_CHECK_EQ(spec, recanonicalized.spec()); -} - -// Checks that |url.spec()| is preserved across a call to ReplaceComponents with -// zero replacements, which is effectively a copy. This can help discover issues -// like https://crbug.com/1075515. -void CheckReplaceComponentsPreservesSpec(const GURL& url) { - static const gurl_base::NoDestructor<GURL::Replacements> no_op; - GURL copy = url.ReplaceComponents(*no_op); - GURL_CHECK_EQ(url.is_valid(), copy.is_valid()); - if (url.is_valid()) { - GURL_CHECK_EQ(url.spec(), copy.spec()); - } -} - -// Entry point for LibFuzzer. -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - if (size < 1) - return 0; - { - gurl_base::StringPiece string_piece_input(reinterpret_cast<const char*>(data), - size); - const GURL url_from_string_piece(string_piece_input); - CheckIdempotency(url_from_string_piece); - CheckReplaceComponentsPreservesSpec(url_from_string_piece); - } - // Test for StringPiece16 if size is even. - if (size % sizeof(char16_t) == 0) { - gurl_base::StringPiece16 string_piece_input16( - reinterpret_cast<const char16_t*>(data), size / sizeof(char16_t)); - const GURL url_from_string_piece16(string_piece_input16); - CheckIdempotency(url_from_string_piece16); - CheckReplaceComponentsPreservesSpec(url_from_string_piece16); - } - // Resolve relative url tests. - { - size_t size_t_bytes = sizeof(size_t); - if (size < size_t_bytes + 1) { - return 0; - } - size_t relative_size = - *reinterpret_cast<const size_t*>(data) % (size - size_t_bytes); - std::string relative_string( - reinterpret_cast<const char*>(data + size_t_bytes), relative_size); - gurl_base::StringPiece string_piece_part_input( - reinterpret_cast<const char*>(data + size_t_bytes + relative_size), - size - relative_size - size_t_bytes); - const GURL url_from_string_piece_part(string_piece_part_input); - CheckIdempotency(url_from_string_piece_part); - CheckReplaceComponentsPreservesSpec(url_from_string_piece_part); - - url_from_string_piece_part.Resolve(relative_string); - - if (relative_size % sizeof(char16_t) == 0) { - std::u16string relative_string16( - reinterpret_cast<const char16_t*>(data + size_t_bytes), - relative_size / sizeof(char16_t)); - url_from_string_piece_part.Resolve(relative_string16); - } - } - return 0; -}
diff --git a/url/origin.h b/url/origin.h index 0d04628..2b8caa5 100644 --- a/url/origin.h +++ b/url/origin.h
@@ -311,6 +311,13 @@ gurl_base::android::ScopedJavaLocalRef<jobject> CreateJavaObject() const; static Origin FromJavaObject( const gurl_base::android::JavaRef<jobject>& java_origin); + static jlong CreateNative(JNIEnv* env, + const gurl_base::android::JavaRef<jstring>& java_scheme, + const gurl_base::android::JavaRef<jstring>& java_host, + uint16_t port, + bool is_opaque, + uint64_t tokenHighBits, + uint64_t tokenLowBits); #endif // BUILDFLAG(IS_ANDROID) void WriteIntoTrace(perfetto::TracedValue context) const;
diff --git a/url/origin_unittest.cc b/url/origin_unittest.cc index 61500a6..5f28acc 100644 --- a/url/origin_unittest.cc +++ b/url/origin_unittest.cc
@@ -5,7 +5,7 @@ #include <stddef.h> #include <stdint.h> -#include "base/memory/raw_ptr.h" +#include "polyfills/base/memory/raw_ptr.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h"
diff --git a/url/url_canon_icu.cc b/url/url_canon_icu.cc index 20c31a8..088235b 100644 --- a/url/url_canon_icu.cc +++ b/url/url_canon_icu.cc
@@ -9,7 +9,7 @@ #include <string.h> #include "polyfills/base/check.h" -#include "base/memory/raw_ptr.h" +#include "polyfills/base/memory/raw_ptr.h" #include <unicode/ucnv.h> #include <unicode/ucnv_cb.h> #include <unicode/utypes.h>
diff --git a/url/url_canon_icu.h b/url/url_canon_icu.h index 9a35df1..9b64722 100644 --- a/url/url_canon_icu.h +++ b/url/url_canon_icu.h
@@ -9,7 +9,7 @@ #include "base/compiler_specific.h" #include "polyfills/base/component_export.h" -#include "base/memory/raw_ptr.h" +#include "polyfills/base/memory/raw_ptr.h" #include "url/url_canon.h" typedef struct UConverter UConverter;
diff --git a/url/url_canon_icu_unittest.cc b/url/url_canon_icu_unittest.cc index 0ac0fcb..fc3fb67 100644 --- a/url/url_canon_icu_unittest.cc +++ b/url/url_canon_icu_unittest.cc
@@ -7,7 +7,7 @@ #include <stddef.h> #include "polyfills/base/logging.h" -#include "base/memory/raw_ptr.h" +#include "polyfills/base/memory/raw_ptr.h" #include "testing/gtest/include/gtest/gtest.h" #include <unicode/ucnv.h> #include "url/url_canon.h"
diff --git a/url/url_canon_query.cc b/url/url_canon_query.cc index 4beac7a..d326ce8 100644 --- a/url/url_canon_query.cc +++ b/url/url_canon_query.cc
@@ -39,18 +39,6 @@ namespace { -// Returns true if the characters starting at |begin| and going until |end| -// (non-inclusive) are all representable in 7-bits. -template<typename CHAR, typename UCHAR> -bool IsAllASCII(const CHAR* spec, const Component& query) { - int end = query.end(); - for (int i = query.begin; i < end; i++) { - if (static_cast<UCHAR>(spec[i]) >= 0x80) - return false; - } - return true; -} - // Appends the given string to the output, escaping characters that do not // match the given |type| in SharedCharTypes. This version will accept 8 or 16 // bit characters, but assumes that they have only 7-bit values. It also assumes @@ -93,29 +81,22 @@ static_cast<size_t>(query.len), output); } -template<typename CHAR, typename UCHAR> +template <typename CHAR, typename UCHAR> void DoConvertToQueryEncoding(const CHAR* spec, const Component& query, CharsetConverter* converter, CanonOutput* output) { - if (IsAllASCII<CHAR, UCHAR>(spec, query)) { - // Easy: the input can just appended with no character set conversions. - AppendRaw8BitQueryString(&spec[query.begin], query.len, output); + if (converter) { + // Run the converter to get an 8-bit string, then append it, escaping + // necessary values. + RawCanonOutput<1024> eight_bit; + RunConverter(spec, query, converter, &eight_bit); + AppendRaw8BitQueryString(eight_bit.data(), eight_bit.length(), output); } else { - // Harder: convert to the proper encoding first. - if (converter) { - // Run the converter to get an 8-bit string, then append it, escaping - // necessary values. - RawCanonOutput<1024> eight_bit; - RunConverter(spec, query, converter, &eight_bit); - AppendRaw8BitQueryString(eight_bit.data(), eight_bit.length(), output); - - } else { - // No converter, do our own UTF-8 conversion. - AppendStringOfType(&spec[query.begin], static_cast<size_t>(query.len), - CHAR_QUERY, output); - } + // No converter, do our own UTF-8 conversion. + AppendStringOfType(&spec[query.begin], static_cast<size_t>(query.len), + CHAR_QUERY, output); } }