Bug 1639030 - Part 2: Roll-up of chromium sandbox update and patches to get a running browser. r=bobowen

This commit does:
- Sync files under security/sandbox/chromium/ with Chromium 81.0.4044.138
- Update files under security/sandbox/chromium-shim/
- Apply patches under security/sandbox/chromium-shim/patches/with_update/
- Apply a workaround for Clang's bug to compile *_interception.cc
- Add mozilla::AddWin32kLockdownPolicy to apply MITIGATION_WIN32K_DISABLE before SUBSYS_WIN32K_LOCKDOWN

Differential Revision: https://phabricator.services.mozilla.com/D79560
This commit is contained in:
Toshihito Kikuchi 2020-07-06 04:04:12 +00:00
parent 8e7730db34
commit 0cbcab5ebb
290 changed files with 16399 additions and 10604 deletions

View File

@ -15,4 +15,6 @@
#include "build/buildflag.h" #include "build/buildflag.h"
#define BUILDFLAG_INTERNAL_USE_TCMALLOC() (0)
#endif // BASE_ALLOCATOR_BUILDFLAGS_H_ #endif // BASE_ALLOCATOR_BUILDFLAGS_H_

View File

@ -0,0 +1,22 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// This is a dummy version of base/debug/crash_logging.cc
#include "base/debug/crash_logging.h"
namespace base {
namespace debug {
CrashKeyString* AllocateCrashKeyString(const char name[],
CrashKeySize value_length) {
return nullptr;
}
void SetCrashKeyString(CrashKeyString* crash_key, base::StringPiece value) {}
} // namespace debug
} // namespace base

View File

@ -4,18 +4,24 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// This is a dummy version of Chromium source file base/debug/stack_trace.h. // This is a dummy version of Chromium source file base/debug/stack_trace.h
// To provide a dummy class StackTrace required in base/win/scoped_handle.cc. // to provide a dummy class StackTrace.
#ifndef BASE_DEBUG_STACK_TRACE_H_ #ifndef BASE_DEBUG_STACK_TRACE_H_
#define BASE_DEBUG_STACK_TRACE_H_ #define BASE_DEBUG_STACK_TRACE_H_
#include <iosfwd>
namespace base { namespace base {
namespace debug { namespace debug {
class BASE_EXPORT StackTrace { class BASE_EXPORT StackTrace {
public: public:
StackTrace() {}; StackTrace() {};
#if !defined(__UCLIBC__) & !defined(_AIX)
void OutputToStream(std::ostream*) const {}
#endif
}; };
} // namespace debug } // namespace debug

View File

@ -10,6 +10,8 @@
#ifndef BASE_FEATURE_LIST_H_ #ifndef BASE_FEATURE_LIST_H_
#define BASE_FEATURE_LIST_H_ #define BASE_FEATURE_LIST_H_
#include "base/macros.h"
namespace base { namespace base {
// Specifies whether a given feature is enabled or disabled by default. // Specifies whether a given feature is enabled or disabled by default.

View File

@ -7,8 +7,6 @@
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "mozilla/Assertions.h"
namespace base { namespace base {
using StringType = FilePath::StringType; using StringType = FilePath::StringType;
@ -18,6 +16,41 @@ namespace {
const FilePath::CharType kStringTerminator = FILE_PATH_LITERAL('\0'); const FilePath::CharType kStringTerminator = FILE_PATH_LITERAL('\0');
// If this FilePath contains a drive letter specification, returns the
// position of the last character of the drive letter specification,
// otherwise returns npos. This can only be true on Windows, when a pathname
// begins with a letter followed by a colon. On other platforms, this always
// returns npos.
StringPieceType::size_type FindDriveLetter(StringPieceType path) {
#if defined(FILE_PATH_USES_DRIVE_LETTERS)
// This is dependent on an ASCII-based character set, but that's a
// reasonable assumption. iswalpha can be too inclusive here.
if (path.length() >= 2 && path[1] == L':' &&
((path[0] >= L'A' && path[0] <= L'Z') ||
(path[0] >= L'a' && path[0] <= L'z'))) {
return 1;
}
#endif // FILE_PATH_USES_DRIVE_LETTERS
return StringType::npos;
}
bool IsPathAbsolute(StringPieceType path) {
#if defined(FILE_PATH_USES_DRIVE_LETTERS)
StringType::size_type letter = FindDriveLetter(path);
if (letter != StringType::npos) {
// Look for a separator right after the drive specification.
return path.length() > letter + 1 &&
FilePath::IsSeparator(path[letter + 1]);
}
// Look for a pair of leading separators.
return path.length() > 1 &&
FilePath::IsSeparator(path[0]) && FilePath::IsSeparator(path[1]);
#else // FILE_PATH_USES_DRIVE_LETTERS
// Look for a separator in the first position.
return path.length() > 0 && FilePath::IsSeparator(path[0]);
#endif // FILE_PATH_USES_DRIVE_LETTERS
}
} // namespace } // namespace
FilePath::FilePath() = default; FilePath::FilePath() = default;
@ -37,4 +70,148 @@ FilePath& FilePath::operator=(const FilePath& that) = default;
FilePath& FilePath::operator=(FilePath&& that) = default; FilePath& FilePath::operator=(FilePath&& that) = default;
// static
bool FilePath::IsSeparator(CharType character) {
for (size_t i = 0; i < kSeparatorsLength - 1; ++i) {
if (character == kSeparators[i]) {
return true;
}
}
return false;
}
// libgen's dirname and basename aren't guaranteed to be thread-safe and aren't
// guaranteed to not modify their input strings, and in fact are implemented
// differently in this regard on different platforms. Don't use them, but
// adhere to their behavior.
FilePath FilePath::DirName() const {
FilePath new_path(path_);
new_path.StripTrailingSeparatorsInternal();
// The drive letter, if any, always needs to remain in the output. If there
// is no drive letter, as will always be the case on platforms which do not
// support drive letters, letter will be npos, or -1, so the comparisons and
// resizes below using letter will still be valid.
StringType::size_type letter = FindDriveLetter(new_path.path_);
StringType::size_type last_separator =
new_path.path_.find_last_of(kSeparators, StringType::npos,
kSeparatorsLength - 1);
if (last_separator == StringType::npos) {
// path_ is in the current directory.
new_path.path_.resize(letter + 1);
} else if (last_separator == letter + 1) {
// path_ is in the root directory.
new_path.path_.resize(letter + 2);
} else if (last_separator == letter + 2 &&
IsSeparator(new_path.path_[letter + 1])) {
// path_ is in "//" (possibly with a drive letter); leave the double
// separator intact indicating alternate root.
new_path.path_.resize(letter + 3);
} else if (last_separator != 0) {
// path_ is somewhere else, trim the basename.
new_path.path_.resize(last_separator);
}
new_path.StripTrailingSeparatorsInternal();
if (!new_path.path_.length())
new_path.path_ = kCurrentDirectory;
return new_path;
}
FilePath FilePath::BaseName() const {
FilePath new_path(path_);
new_path.StripTrailingSeparatorsInternal();
// The drive letter, if any, is always stripped.
StringType::size_type letter = FindDriveLetter(new_path.path_);
if (letter != StringType::npos) {
new_path.path_.erase(0, letter + 1);
}
// Keep everything after the final separator, but if the pathname is only
// one character and it's a separator, leave it alone.
StringType::size_type last_separator =
new_path.path_.find_last_of(kSeparators, StringType::npos,
kSeparatorsLength - 1);
if (last_separator != StringType::npos &&
last_separator < new_path.path_.length() - 1) {
new_path.path_.erase(0, last_separator + 1);
}
return new_path;
}
FilePath FilePath::Append(StringPieceType component) const {
StringPieceType appended = component;
StringType without_nuls;
StringType::size_type nul_pos = component.find(kStringTerminator);
if (nul_pos != StringPieceType::npos) {
without_nuls = StringType(component.substr(0, nul_pos));
appended = StringPieceType(without_nuls);
}
DCHECK(!IsPathAbsolute(appended));
if (path_.compare(kCurrentDirectory) == 0 && !appended.empty()) {
// Append normally doesn't do any normalization, but as a special case,
// when appending to kCurrentDirectory, just return a new path for the
// component argument. Appending component to kCurrentDirectory would
// serve no purpose other than needlessly lengthening the path, and
// it's likely in practice to wind up with FilePath objects containing
// only kCurrentDirectory when calling DirName on a single relative path
// component.
return FilePath(appended);
}
FilePath new_path(path_);
new_path.StripTrailingSeparatorsInternal();
// Don't append a separator if the path is empty (indicating the current
// directory) or if the path component is empty (indicating nothing to
// append).
if (!appended.empty() && !new_path.path_.empty()) {
// Don't append a separator if the path still ends with a trailing
// separator after stripping (indicating the root directory).
if (!IsSeparator(new_path.path_.back())) {
// Don't append a separator if the path is just a drive letter.
if (FindDriveLetter(new_path.path_) + 1 != new_path.path_.length()) {
new_path.path_.append(1, kSeparators[0]);
}
}
}
new_path.path_.append(appended.data(), appended.size());
return new_path;
}
FilePath FilePath::Append(const FilePath& component) const {
return Append(component.value());
}
void FilePath::StripTrailingSeparatorsInternal() {
// If there is no drive letter, start will be 1, which will prevent stripping
// the leading separator if there is only one separator. If there is a drive
// letter, start will be set appropriately to prevent stripping the first
// separator following the drive letter, if a separator immediately follows
// the drive letter.
StringType::size_type start = FindDriveLetter(path_) + 2;
StringType::size_type last_stripped = StringType::npos;
for (StringType::size_type pos = path_.length();
pos > start && IsSeparator(path_[pos - 1]);
--pos) {
// If the string only has two separators and they're at the beginning,
// don't strip them, unless the string began with more than two separators.
if (pos != start + 1 || last_stripped == start + 2 ||
!IsSeparator(path_[start - 1])) {
path_.resize(pos - 1);
last_stripped = pos;
}
}
}
} // namespace base } // namespace base

View File

@ -0,0 +1,20 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// This is a copy of a file that is generated by the chromium build, with
// only the build flags we require.
// Generated by build/write_buildflag_header.py
// From "//base:logging_buildflags"
#ifndef BASE_LOGGING_BUILDFLAGS_H_
#define BASE_LOGGING_BUILDFLAGS_H_
#include "build/buildflag.h"
#define BUILDFLAG_INTERNAL_ENABLE_LOG_ERROR_NOT_REACHED() (0)
#endif // BASE_LOGGING_BUILDFLAGS_H_

View File

@ -24,9 +24,9 @@ class BASE_EXPORT SharedMemoryTracker {
return instance; return instance;
} }
void IncrementMemoryUsage(const SharedMemory& shared_memory) {}; void IncrementMemoryUsage(const SharedMemoryMapping& mapping) {};
void DecrementMemoryUsage(const SharedMemory& shared_memory) {}; void DecrementMemoryUsage(const SharedMemoryMapping& mapping) {};
private: private:
SharedMemoryTracker() {}; SharedMemoryTracker() {};

View File

@ -11,7 +11,6 @@
#ifndef BASE_METRICS_HISTOGRAM_MACROS_H_ #ifndef BASE_METRICS_HISTOGRAM_MACROS_H_
#define BASE_METRICS_HISTOGRAM_MACROS_H_ #define BASE_METRICS_HISTOGRAM_MACROS_H_
#define UMA_HISTOGRAM_ENUMERATION(name, sample, enum_max) do { } while (0) #define UMA_HISTOGRAM_ENUMERATION(name, sample) do { } while (0)
#define SCOPED_UMA_HISTOGRAM_TIMER(name) do { } while (0)
#endif // BASE_METRICS_HISTOGRAM_MACROS_H_ #endif // BASE_METRICS_HISTOGRAM_MACROS_H_

View File

@ -0,0 +1,12 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// This is a cut down version of //base/observer_list.h
#ifndef BASE_OBSERVER_LIST_H_
#define BASE_OBSERVER_LIST_H_
#endif // BASE_OBSERVER_LIST_H_

View File

@ -0,0 +1,25 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// This is a reduced version of Chromium's //base/process/launch.h
// to satisfy compiler.
#ifndef BASE_PROCESS_LAUNCH_H_
#define BASE_PROCESS_LAUNCH_H_
#include <vector>
#include "base/environment.h"
namespace base {
#if defined(OS_WIN)
typedef std::vector<HANDLE> HandlesToInheritVector;
#endif
} // namespace base
#endif // BASE_PROCESS_LAUNCH_H_

View File

@ -0,0 +1,17 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "base/process/memory.h"
#include "mozilla/Assertions.h"
namespace base {
void TerminateBecauseOutOfMemory(size_t size) {
MOZ_CRASH("Hit base::TerminateBecauseOutOfMemory");
}
} // namespace base

View File

@ -35,7 +35,8 @@ namespace internal {
class BASE_EXPORT ScopedBlockingCallWithBaseSyncPrimitives { class BASE_EXPORT ScopedBlockingCallWithBaseSyncPrimitives {
public: public:
ScopedBlockingCallWithBaseSyncPrimitives(BlockingType blocking_type) {}; ScopedBlockingCallWithBaseSyncPrimitives(const Location& from_here,
BlockingType blocking_type) {}
~ScopedBlockingCallWithBaseSyncPrimitives() {}; ~ScopedBlockingCallWithBaseSyncPrimitives() {};
}; };

View File

@ -330,6 +330,30 @@ DeriveAppContainerSidFromAppContainerName(
#define PROC_THREAD_ATTRIBUTE_ALL_APPLICATION_PACKAGES_POLICY \ #define PROC_THREAD_ATTRIBUTE_ALL_APPLICATION_PACKAGES_POLICY \
ProcThreadAttributeValue (ProcThreadAttributeAllApplicationPackagesPolicy, FALSE, TRUE, FALSE) ProcThreadAttributeValue (ProcThreadAttributeAllApplicationPackagesPolicy, FALSE, TRUE, FALSE)
#endif // (_WIN32_WINNT >= 0x0A00) //
// Define functions declared only when _WIN32_WINNT >= 0x0A00
//
WINBASEAPI
BOOL
WINAPI
IsWow64Process2(
_In_ HANDLE hProcess,
_Out_ USHORT* pProcessMachine,
_Out_opt_ USHORT* pNativeMachine
);
#endif // (_WIN32_WINNT < 0x0A00)
#if defined(__MINGW32__)
// winnt.h
#define THREAD_DYNAMIC_CODE_ALLOW 1 // Opt-out of dynamic code generation.
// Mingw uses an old version THREAD_INFORMATION_CLASS defined in winbase.h
// where ThreadDynamicCodePolicy does not exist.
#define ThreadDynamicCodePolicy static_cast<THREAD_INFORMATION_CLASS>(2)
#endif // defined(__MINGW32__)
#endif // _SECURITY_SANDBOX_BASE_SHIM_SDKDECLS_H_ #endif // _SECURITY_SANDBOX_BASE_SHIM_SDKDECLS_H_

View File

@ -0,0 +1,42 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// This is a partial implementation of Chromium's source file
// base/win/win_util.cc
#include "base/win/win_util.h"
#include "base/logging.h"
#include "base/strings/string_util.h"
namespace base {
namespace win {
std::wstring GetWindowObjectName(HANDLE handle) {
// Get the size of the name.
std::wstring object_name;
DWORD size = 0;
::GetUserObjectInformation(handle, UOI_NAME, nullptr, 0, &size);
if (!size) {
DPCHECK(false);
return object_name;
}
LOG_ASSERT(size % sizeof(wchar_t) == 0u);
// Query the name of the object.
if (!::GetUserObjectInformation(
handle, UOI_NAME, WriteInto(&object_name, size / sizeof(wchar_t)),
size, &size)) {
DPCHECK(false);
}
return object_name;
}
} // namespace win
} // namespace base

View File

@ -0,0 +1,26 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// This is a partial implementation of Chromium's source file
// base/win/win_util.h
#ifndef BASE_WIN_WIN_UTIL_H_
#define BASE_WIN_WIN_UTIL_H_
#include <string>
#include "base/base_export.h"
namespace base {
namespace win {
// Returns the name of a desktop or a window station.
BASE_EXPORT std::wstring GetWindowObjectName(HANDLE handle);
} // namespace win
} // namespace base
#endif // BASE_WIN_WIN_UTIL_H_

View File

@ -0,0 +1,31 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// This is a partial implementation of Chromium's source file
// //sandbox/win/src/sandbox_policy_diagnostic.h
#ifndef SANDBOX_WIN_SRC_SANDBOX_POLICY_DIAGNOSTIC_H_
#define SANDBOX_WIN_SRC_SANDBOX_POLICY_DIAGNOSTIC_H_
#include "mozilla/Assertions.h"
namespace sandbox {
class PolicyBase;
class PolicyDiagnostic final : public PolicyInfo {
public:
PolicyDiagnostic(PolicyBase*) {}
~PolicyDiagnostic() override = default;
const char* JsonString() override { MOZ_CRASH(); }
private:
DISALLOW_COPY_AND_ASSIGN(PolicyDiagnostic);
};
} // namespace sandbox
#endif // SANDBOX_WIN_SRC_SANDBOX_POLICY_DIAGNOSTIC_H_

View File

@ -48,11 +48,11 @@ AtExitManager::~AtExitManager() {
// static // static
void AtExitManager::RegisterCallback(AtExitCallbackType func, void* param) { void AtExitManager::RegisterCallback(AtExitCallbackType func, void* param) {
DCHECK(func); DCHECK(func);
RegisterTask(base::Bind(func, param)); RegisterTask(base::BindOnce(func, param));
} }
// static // static
void AtExitManager::RegisterTask(base::Closure task) { void AtExitManager::RegisterTask(base::OnceClosure task) {
if (!g_top_manager) { if (!g_top_manager) {
NOTREACHED() << "Tried to RegisterCallback without an AtExitManager"; NOTREACHED() << "Tried to RegisterCallback without an AtExitManager";
return; return;
@ -75,7 +75,7 @@ void AtExitManager::ProcessCallbacksNow() {
// Callbacks may try to add new callbacks, so run them without holding // Callbacks may try to add new callbacks, so run them without holding
// |lock_|. This is an error and caught by the DCHECK in RegisterTask(), but // |lock_|. This is an error and caught by the DCHECK in RegisterTask(), but
// handle it gracefully in release builds so we don't deadlock. // handle it gracefully in release builds so we don't deadlock.
base::stack<base::Closure> tasks; base::stack<base::OnceClosure> tasks;
{ {
AutoLock lock(g_top_manager->lock_); AutoLock lock(g_top_manager->lock_);
tasks.swap(g_top_manager->stack_); tasks.swap(g_top_manager->stack_);
@ -89,8 +89,7 @@ void AtExitManager::ProcessCallbacksNow() {
ScopedAllowCrossThreadRefCountAccess allow_cross_thread_ref_count_access; ScopedAllowCrossThreadRefCountAccess allow_cross_thread_ref_count_access;
while (!tasks.empty()) { while (!tasks.empty()) {
base::Closure task = tasks.top(); std::move(tasks.top()).Run();
task.Run();
tasks.pop(); tasks.pop();
} }

View File

@ -43,7 +43,7 @@ class BASE_EXPORT AtExitManager {
static void RegisterCallback(AtExitCallbackType func, void* param); static void RegisterCallback(AtExitCallbackType func, void* param);
// Registers the specified task to be called at exit. // Registers the specified task to be called at exit.
static void RegisterTask(base::Closure task); static void RegisterTask(base::OnceClosure task);
// Calls the functions registered with RegisterCallback in LIFO order. It // Calls the functions registered with RegisterCallback in LIFO order. It
// is possible to register new callbacks after calling this function. // is possible to register new callbacks after calling this function.
@ -63,7 +63,7 @@ class BASE_EXPORT AtExitManager {
private: private:
base::Lock lock_; base::Lock lock_;
base::stack<base::Closure> stack_ GUARDED_BY(lock_); base::stack<base::OnceClosure> stack_ GUARDED_BY(lock_);
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
bool processing_callbacks_ GUARDED_BY(lock_) = false; bool processing_callbacks_ GUARDED_BY(lock_) = false;

View File

@ -39,15 +39,6 @@
#include "base/base_export.h" #include "base/base_export.h"
#include "build/build_config.h" #include "build/build_config.h"
#if defined(OS_WIN) && (defined(ARCH_CPU_64_BITS) || defined(__MINGW32__))
// windows.h #defines this (only on x64). This causes problems because the
// public API also uses MemoryBarrier at the public name for this fence. So, on
// X64, undef it, and call its documented
// (http://msdn.microsoft.com/en-us/library/windows/desktop/ms684208.aspx)
// implementation directly.
#undef MemoryBarrier
#endif
namespace base { namespace base {
namespace subtle { namespace subtle {
@ -100,8 +91,7 @@ Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
// ensure that no later memory access can be reordered ahead of the operation. // ensure that no later memory access can be reordered ahead of the operation.
// "Release" operations ensure that no previous memory access can be reordered // "Release" operations ensure that no previous memory access can be reordered
// after the operation. "Barrier" operations have both "Acquire" and "Release" // after the operation. "Barrier" operations have both "Acquire" and "Release"
// semantics. A MemoryBarrier() has "Barrier" semantics, but does no memory // semantics.
// access.
Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr, Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value, Atomic32 old_value,
Atomic32 new_value); Atomic32 new_value);
@ -109,7 +99,6 @@ Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value, Atomic32 old_value,
Atomic32 new_value); Atomic32 new_value);
void MemoryBarrier();
void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value); void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value);
void Acquire_Store(volatile Atomic32* ptr, Atomic32 value); void Acquire_Store(volatile Atomic32* ptr, Atomic32 value);
void Release_Store(volatile Atomic32* ptr, Atomic32 value); void Release_Store(volatile Atomic32* ptr, Atomic32 value);

View File

@ -52,16 +52,6 @@ typedef volatile std::atomic<Atomic32>* AtomicLocation32;
static_assert(sizeof(*(AtomicLocation32) nullptr) == sizeof(Atomic32), static_assert(sizeof(*(AtomicLocation32) nullptr) == sizeof(Atomic32),
"incompatible 32-bit atomic layout"); "incompatible 32-bit atomic layout");
inline void MemoryBarrier() {
#if defined(__GLIBCXX__)
// Work around libstdc++ bug 51038 where atomic_thread_fence was declared but
// not defined, leading to the linker complaining about undefined references.
__atomic_thread_fence(std::memory_order_seq_cst);
#else
std::atomic_thread_fence(std::memory_order_seq_cst);
#endif
}
inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr, inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value, Atomic32 old_value,
Atomic32 new_value) { Atomic32 new_value) {
@ -119,7 +109,7 @@ inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) { inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
((AtomicLocation32)ptr)->store(value, std::memory_order_relaxed); ((AtomicLocation32)ptr)->store(value, std::memory_order_relaxed);
MemoryBarrier(); std::atomic_thread_fence(std::memory_order_seq_cst);
} }
inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) { inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
@ -135,7 +125,7 @@ inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
} }
inline Atomic32 Release_Load(volatile const Atomic32* ptr) { inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
MemoryBarrier(); std::atomic_thread_fence(std::memory_order_seq_cst);
return ((AtomicLocation32)ptr)->load(std::memory_order_relaxed); return ((AtomicLocation32)ptr)->load(std::memory_order_relaxed);
} }
@ -202,7 +192,7 @@ inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) { inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
((AtomicLocation64)ptr)->store(value, std::memory_order_relaxed); ((AtomicLocation64)ptr)->store(value, std::memory_order_relaxed);
MemoryBarrier(); std::atomic_thread_fence(std::memory_order_seq_cst);
} }
inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) { inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
@ -218,7 +208,7 @@ inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
} }
inline Atomic64 Release_Load(volatile const Atomic64* ptr) { inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
MemoryBarrier(); std::atomic_thread_fence(std::memory_order_seq_cst);
return ((AtomicLocation64)ptr)->load(std::memory_order_relaxed); return ((AtomicLocation64)ptr)->load(std::memory_order_relaxed);
} }

View File

@ -11,18 +11,11 @@
#include <intrin.h> #include <intrin.h>
#include <atomic>
#include "base/macros.h" #include "base/macros.h"
#include "build/build_config.h" #include "build/build_config.h"
#if defined(ARCH_CPU_64_BITS) || defined(__MINGW32__)
// windows.h #defines this (only on x64). This causes problems because the
// public API also uses MemoryBarrier at the public name for this fence. So, on
// X64, undef it, and call its documented
// (http://msdn.microsoft.com/en-us/library/windows/desktop/ms684208.aspx)
// implementation directly.
#undef MemoryBarrier
#endif
namespace base { namespace base {
namespace subtle { namespace subtle {
@ -56,18 +49,6 @@ inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
return Barrier_AtomicIncrement(ptr, increment); return Barrier_AtomicIncrement(ptr, increment);
} }
inline void MemoryBarrier() {
#if defined(ARCH_CPU_64_BITS)
// See #undef and note at the top of this file.
__faststorefence();
#else
// We use the implementation of MemoryBarrier from WinNT.h
LONG barrier;
_InterlockedOr(&barrier, 0);
#endif
}
inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr, inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value, Atomic32 old_value,
Atomic32 new_value) { Atomic32 new_value) {
@ -104,7 +85,7 @@ inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
} }
inline Atomic32 Release_Load(volatile const Atomic32* ptr) { inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
MemoryBarrier(); std::atomic_thread_fence(std::memory_order_seq_cst);
return *ptr; return *ptr;
} }
@ -173,7 +154,7 @@ inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
} }
inline Atomic64 Release_Load(volatile const Atomic64* ptr) { inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
MemoryBarrier(); std::atomic_thread_fence(std::memory_order_seq_cst);
return *ptr; return *ptr;
} }

View File

@ -7,7 +7,7 @@
namespace switches { namespace switches {
// Delays execution of base::TaskPriority::BEST_EFFORT tasks until shutdown. // Delays execution of TaskPriority::BEST_EFFORT tasks until shutdown.
const char kDisableBestEffortTasks[] = "disable-best-effort-tasks"; const char kDisableBestEffortTasks[] = "disable-best-effort-tasks";
// Disables the crash reporting. // Disables the crash reporting.
@ -16,6 +16,9 @@ const char kDisableBreakpad[] = "disable-breakpad";
// Comma-separated list of feature names to disable. See also kEnableFeatures. // Comma-separated list of feature names to disable. See also kEnableFeatures.
const char kDisableFeatures[] = "disable-features"; const char kDisableFeatures[] = "disable-features";
// Force disabling of low-end device mode when set.
const char kDisableLowEndDeviceMode[] = "disable-low-end-device-mode";
// Indicates that crash reporting should be enabled. On platforms where helper // Indicates that crash reporting should be enabled. On platforms where helper
// processes cannot access to files needed to make this decision, this flag is // processes cannot access to files needed to make this decision, this flag is
// generated internally. // generated internally.
@ -24,15 +27,9 @@ const char kEnableCrashReporter[] = "enable-crash-reporter";
// Comma-separated list of feature names to enable. See also kDisableFeatures. // Comma-separated list of feature names to enable. See also kDisableFeatures.
const char kEnableFeatures[] = "enable-features"; const char kEnableFeatures[] = "enable-features";
// Generates full memory crash dump.
const char kFullMemoryCrashReport[] = "full-memory-crash-report";
// Force low-end device mode when set. // Force low-end device mode when set.
const char kEnableLowEndDeviceMode[] = "enable-low-end-device-mode"; const char kEnableLowEndDeviceMode[] = "enable-low-end-device-mode";
// Force disabling of low-end device mode when set.
const char kDisableLowEndDeviceMode[] = "disable-low-end-device-mode";
// This option can be used to force field trials when testing changes locally. // This option can be used to force field trials when testing changes locally.
// The argument is a list of name and value pairs, separated by slashes. If a // The argument is a list of name and value pairs, separated by slashes. If a
// trial name is prefixed with an asterisk, that trial will start activated. // trial name is prefixed with an asterisk, that trial will start activated.
@ -43,44 +40,19 @@ const char kDisableLowEndDeviceMode[] = "disable-low-end-device-mode";
// FieldTrialList::CreateTrialsFromString() in field_trial.h for details. // FieldTrialList::CreateTrialsFromString() in field_trial.h for details.
const char kForceFieldTrials[] = "force-fieldtrials"; const char kForceFieldTrials[] = "force-fieldtrials";
// Generates full memory crash dump.
const char kFullMemoryCrashReport[] = "full-memory-crash-report";
// Logs information about all tasks posted with TaskPriority::BEST_EFFORT. Use
// this to diagnose issues that are thought to be caused by
// TaskPriority::BEST_EFFORT execution fences. Note: Tasks posted to a
// non-BEST_EFFORT UpdateableSequencedTaskRunner whose priority is later lowered
// to BEST_EFFORT are not logged.
const char kLogBestEffortTasks[] = "log-best-effort-tasks";
// Suppresses all error dialogs when present. // Suppresses all error dialogs when present.
const char kNoErrorDialogs[] = "noerrdialogs"; const char kNoErrorDialogs[] = "noerrdialogs";
// When running certain tests that spawn child processes, this switch indicates
// to the test framework that the current process is a child process.
const char kTestChildProcess[] = "test-child-process";
// When running certain tests that spawn child processes, this switch indicates
// to the test framework that the current process should not initialize ICU to
// avoid creating any scoped handles too early in startup.
const char kTestDoNotInitializeIcu[] = "test-do-not-initialize-icu";
// Gives the default maximal active V-logging level; 0 is the default.
// Normally positive values are used for V-logging levels.
const char kV[] = "v";
// Gives the per-module maximal V-logging levels to override the value
// given by --v. E.g. "my_module=2,foo*=3" would change the logging
// level for all code in source files "my_module.*" and "foo*.*"
// ("-inl" suffixes are also disregarded for this matching).
//
// Any pattern containing a forward or backward slash will be tested
// against the whole pathname and not just the module. E.g.,
// "*/foo/bar/*=2" would change the logging level for all code in
// source files under a "foo/bar" directory.
const char kVModule[] = "vmodule";
// Will wait for 60 seconds for a debugger to come to attach to the process.
const char kWaitForDebugger[] = "wait-for-debugger";
// Sends trace events from these categories to a file.
// --trace-to-file on its own sends to default categories.
const char kTraceToFile[] = "trace-to-file";
// Specifies the file name for --trace-to-file. If unspecified, it will
// go to a default file name.
const char kTraceToFileName[] = "trace-to-file-name";
// Starts the sampling based profiler for the browser process at startup. This // Starts the sampling based profiler for the browser process at startup. This
// will only work if chrome has been built with the gn arg enable_profiling = // will only work if chrome has been built with the gn arg enable_profiling =
// true. The output will go to the value of kProfilingFile. // true. The output will go to the value of kProfilingFile.
@ -102,7 +74,45 @@ const char kProfilingFile[] = "profiling-file";
// specified. // specified.
const char kProfilingFlush[] = "profiling-flush"; const char kProfilingFlush[] = "profiling-flush";
// When running certain tests that spawn child processes, this switch indicates
// to the test framework that the current process is a child process.
const char kTestChildProcess[] = "test-child-process";
// When running certain tests that spawn child processes, this switch indicates
// to the test framework that the current process should not initialize ICU to
// avoid creating any scoped handles too early in startup.
const char kTestDoNotInitializeIcu[] = "test-do-not-initialize-icu";
// Sends trace events from these categories to a file.
// --trace-to-file on its own sends to default categories.
const char kTraceToFile[] = "trace-to-file";
// Specifies the file name for --trace-to-file. If unspecified, it will
// go to a default file name.
const char kTraceToFileName[] = "trace-to-file-name";
// Gives the default maximal active V-logging level; 0 is the default.
// Normally positive values are used for V-logging levels.
const char kV[] = "v";
// Gives the per-module maximal V-logging levels to override the value
// given by --v. E.g. "my_module=2,foo*=3" would change the logging
// level for all code in source files "my_module.*" and "foo*.*"
// ("-inl" suffixes are also disregarded for this matching).
//
// Any pattern containing a forward or backward slash will be tested
// against the whole pathname and not just the module. E.g.,
// "*/foo/bar/*=2" would change the logging level for all code in
// source files under a "foo/bar" directory.
const char kVModule[] = "vmodule";
// Will wait for 60 seconds for a debugger to come to attach to the process.
const char kWaitForDebugger[] = "wait-for-debugger";
#if defined(OS_WIN) #if defined(OS_WIN)
// Disable high-resolution timer on Windows.
const char kDisableHighResTimer[] = "disable-highres-timer";
// Disables the USB keyboard detection for blocking the OSK on Win8+. // Disables the USB keyboard detection for blocking the OSK on Win8+.
const char kDisableUsbKeyboardDetect[] = "disable-usb-keyboard-detect"; const char kDisableUsbKeyboardDetect[] = "disable-usb-keyboard-detect";
#endif #endif
@ -126,17 +136,14 @@ const char kEnableCrashReporterForTesting[] =
// Enables the reached code profiler that samples all threads in all processes // Enables the reached code profiler that samples all threads in all processes
// to determine which functions are almost never executed. // to determine which functions are almost never executed.
const char kEnableReachedCodeProfiler[] = "enable-reached-code-profiler"; const char kEnableReachedCodeProfiler[] = "enable-reached-code-profiler";
#endif
// Specifies optimization of memory layout of the native library using the #if defined(OS_LINUX)
// orderfile symbols given in base/android/library_loader/anchor_functions.h, // Controls whether or not retired instruction counts are surfaced for threads
// via madvise and changing the library prefetch behavior. // in trace events on Linux.
// //
// If this switch is not specified, an optimization may be done depending on a // This flag requires the BPF sandbox to be disabled.
// synthetic trial. If specified, its values may be 'on' or 'off'. These const char kEnableThreadInstructionCount[] = "enable-thread-instruction-count";
// override the synthetic trial.
//
// This flag is only used on architectures with SUPPORTS_CODE_ORDERING defined.
const char kOrderfileMemoryOptimization[] = "orderfile-memory-optimization";
#endif #endif
} // namespace switches } // namespace switches

View File

@ -20,6 +20,7 @@ extern const char kEnableFeatures[];
extern const char kEnableLowEndDeviceMode[]; extern const char kEnableLowEndDeviceMode[];
extern const char kForceFieldTrials[]; extern const char kForceFieldTrials[];
extern const char kFullMemoryCrashReport[]; extern const char kFullMemoryCrashReport[];
extern const char kLogBestEffortTasks[];
extern const char kNoErrorDialogs[]; extern const char kNoErrorDialogs[];
extern const char kProfilingAtStart[]; extern const char kProfilingAtStart[];
extern const char kProfilingFile[]; extern const char kProfilingFile[];
@ -33,6 +34,7 @@ extern const char kVModule[];
extern const char kWaitForDebugger[]; extern const char kWaitForDebugger[];
#if defined(OS_WIN) #if defined(OS_WIN)
extern const char kDisableHighResTimer[];
extern const char kDisableUsbKeyboardDetect[]; extern const char kDisableUsbKeyboardDetect[];
#endif #endif
@ -49,6 +51,10 @@ extern const char kEnableReachedCodeProfiler[];
extern const char kOrderfileMemoryOptimization[]; extern const char kOrderfileMemoryOptimization[];
#endif #endif
#if defined(OS_LINUX)
extern const char kEnableThreadInstructionCount[];
#endif
} // namespace switches } // namespace switches
#endif // BASE_BASE_SWITCHES_H_ #endif // BASE_BASE_SWITCHES_H_

View File

@ -7,6 +7,7 @@
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <type_traits>
#include <utility> #include <utility>
#include "base/bind_internal.h" #include "base/bind_internal.h"
@ -41,7 +42,7 @@
// class C : public base::RefCounted<C> { void F(); }; // class C : public base::RefCounted<C> { void F(); };
// auto instance = base::MakeRefCounted<C>(); // auto instance = base::MakeRefCounted<C>();
// auto cb = base::BindOnce(&C::F, instance); // auto cb = base::BindOnce(&C::F, instance);
// cb.Run(); // Identical to instance->F() // std::move(cb).Run(); // Identical to instance->F()
// //
// base::Bind is currently a type alias for base::BindRepeating(). In the // base::Bind is currently a type alias for base::BindRepeating(). In the
// future, we expect to flip this to default to base::BindOnce(). // future, we expect to flip this to default to base::BindOnce().
@ -179,26 +180,36 @@ template <bool is_once, bool is_method, typename... Args>
using MakeUnwrappedTypeList = using MakeUnwrappedTypeList =
typename MakeUnwrappedTypeListImpl<is_once, is_method, Args...>::Type; typename MakeUnwrappedTypeListImpl<is_once, is_method, Args...>::Type;
} // namespace internal // Used below in BindImpl to determine whether to use Invoker::Run or
// Invoker::RunOnce.
// Note: Simply using `kIsOnce ? &Invoker::RunOnce : &Invoker::Run` does not
// work, since the compiler needs to check whether both expressions are
// well-formed. Using `Invoker::Run` with a OnceCallback triggers a
// static_assert, which is why the ternary expression does not compile.
// TODO(crbug.com/752720): Remove this indirection once we have `if constexpr`.
template <typename Invoker>
constexpr auto GetInvokeFunc(std::true_type) {
return Invoker::RunOnce;
}
// Bind as OnceCallback. template <typename Invoker>
template <typename Functor, typename... Args> constexpr auto GetInvokeFunc(std::false_type) {
inline OnceCallback<MakeUnboundRunType<Functor, Args...>> return Invoker::Run;
BindOnce(Functor&& functor, Args&&... args) { }
static_assert(!internal::IsOnceCallback<std::decay_t<Functor>>() ||
(std::is_rvalue_reference<Functor&&>() &&
!std::is_const<std::remove_reference_t<Functor>>()),
"BindOnce requires non-const rvalue for OnceCallback binding."
" I.e.: base::BindOnce(std::move(callback)).");
template <template <typename> class CallbackT,
typename Functor,
typename... Args>
decltype(auto) BindImpl(Functor&& functor, Args&&... args) {
// This block checks if each |args| matches to the corresponding params of the // This block checks if each |args| matches to the corresponding params of the
// target function. This check does not affect the behavior of Bind, but its // target function. This check does not affect the behavior of Bind, but its
// error message should be more readable. // error message should be more readable.
static constexpr bool kIsOnce = IsOnceCallback<CallbackT<void()>>::value;
using Helper = internal::BindTypeHelper<Functor, Args...>; using Helper = internal::BindTypeHelper<Functor, Args...>;
using FunctorTraits = typename Helper::FunctorTraits; using FunctorTraits = typename Helper::FunctorTraits;
using BoundArgsList = typename Helper::BoundArgsList; using BoundArgsList = typename Helper::BoundArgsList;
using UnwrappedArgsList = using UnwrappedArgsList =
internal::MakeUnwrappedTypeList<true, FunctorTraits::is_method, internal::MakeUnwrappedTypeList<kIsOnce, FunctorTraits::is_method,
Args&&...>; Args&&...>;
using BoundParamsList = typename Helper::BoundParamsList; using BoundParamsList = typename Helper::BoundParamsList;
static_assert(internal::AssertBindArgsValidity< static_assert(internal::AssertBindArgsValidity<
@ -209,13 +220,14 @@ BindOnce(Functor&& functor, Args&&... args) {
using BindState = internal::MakeBindStateType<Functor, Args...>; using BindState = internal::MakeBindStateType<Functor, Args...>;
using UnboundRunType = MakeUnboundRunType<Functor, Args...>; using UnboundRunType = MakeUnboundRunType<Functor, Args...>;
using Invoker = internal::Invoker<BindState, UnboundRunType>; using Invoker = internal::Invoker<BindState, UnboundRunType>;
using CallbackType = OnceCallback<UnboundRunType>; using CallbackType = CallbackT<UnboundRunType>;
// Store the invoke func into PolymorphicInvoke before casting it to // Store the invoke func into PolymorphicInvoke before casting it to
// InvokeFuncStorage, so that we can ensure its type matches to // InvokeFuncStorage, so that we can ensure its type matches to
// PolymorphicInvoke, to which CallbackType will cast back. // PolymorphicInvoke, to which CallbackType will cast back.
using PolymorphicInvoke = typename CallbackType::PolymorphicInvoke; using PolymorphicInvoke = typename CallbackType::PolymorphicInvoke;
PolymorphicInvoke invoke_func = &Invoker::RunOnce; PolymorphicInvoke invoke_func =
GetInvokeFunc<Invoker>(std::integral_constant<bool, kIsOnce>());
using InvokeFuncStorage = internal::BindStateBase::InvokeFuncStorage; using InvokeFuncStorage = internal::BindStateBase::InvokeFuncStorage;
return CallbackType(BindState::Create( return CallbackType(BindState::Create(
@ -223,6 +235,23 @@ BindOnce(Functor&& functor, Args&&... args) {
std::forward<Functor>(functor), std::forward<Args>(args)...)); std::forward<Functor>(functor), std::forward<Args>(args)...));
} }
} // namespace internal
// Bind as OnceCallback.
template <typename Functor, typename... Args>
inline OnceCallback<MakeUnboundRunType<Functor, Args...>> BindOnce(
Functor&& functor,
Args&&... args) {
static_assert(!internal::IsOnceCallback<std::decay_t<Functor>>() ||
(std::is_rvalue_reference<Functor&&>() &&
!std::is_const<std::remove_reference_t<Functor>>()),
"BindOnce requires non-const rvalue for OnceCallback binding."
" I.e.: base::BindOnce(std::move(callback)).");
return internal::BindImpl<OnceCallback>(std::forward<Functor>(functor),
std::forward<Args>(args)...);
}
// Bind as RepeatingCallback. // Bind as RepeatingCallback.
template <typename Functor, typename... Args> template <typename Functor, typename... Args>
inline RepeatingCallback<MakeUnboundRunType<Functor, Args...>> inline RepeatingCallback<MakeUnboundRunType<Functor, Args...>>
@ -231,36 +260,8 @@ BindRepeating(Functor&& functor, Args&&... args) {
!internal::IsOnceCallback<std::decay_t<Functor>>(), !internal::IsOnceCallback<std::decay_t<Functor>>(),
"BindRepeating cannot bind OnceCallback. Use BindOnce with std::move()."); "BindRepeating cannot bind OnceCallback. Use BindOnce with std::move().");
// This block checks if each |args| matches to the corresponding params of the return internal::BindImpl<RepeatingCallback>(std::forward<Functor>(functor),
// target function. This check does not affect the behavior of Bind, but its std::forward<Args>(args)...);
// error message should be more readable.
using Helper = internal::BindTypeHelper<Functor, Args...>;
using FunctorTraits = typename Helper::FunctorTraits;
using BoundArgsList = typename Helper::BoundArgsList;
using UnwrappedArgsList =
internal::MakeUnwrappedTypeList<false, FunctorTraits::is_method,
Args&&...>;
using BoundParamsList = typename Helper::BoundParamsList;
static_assert(internal::AssertBindArgsValidity<
std::make_index_sequence<Helper::num_bounds>, BoundArgsList,
UnwrappedArgsList, BoundParamsList>::ok,
"The bound args need to be convertible to the target params.");
using BindState = internal::MakeBindStateType<Functor, Args...>;
using UnboundRunType = MakeUnboundRunType<Functor, Args...>;
using Invoker = internal::Invoker<BindState, UnboundRunType>;
using CallbackType = RepeatingCallback<UnboundRunType>;
// Store the invoke func into PolymorphicInvoke before casting it to
// InvokeFuncStorage, so that we can ensure its type matches to
// PolymorphicInvoke, to which CallbackType will cast back.
using PolymorphicInvoke = typename CallbackType::PolymorphicInvoke;
PolymorphicInvoke invoke_func = &Invoker::Run;
using InvokeFuncStorage = internal::BindStateBase::InvokeFuncStorage;
return CallbackType(BindState::Create(
reinterpret_cast<InvokeFuncStorage>(invoke_func),
std::forward<Functor>(functor), std::forward<Args>(args)...));
} }
// Unannotated Bind. // Unannotated Bind.
@ -275,22 +276,27 @@ Bind(Functor&& functor, Args&&... args) {
// Special cases for binding to a base::Callback without extra bound arguments. // Special cases for binding to a base::Callback without extra bound arguments.
template <typename Signature> template <typename Signature>
OnceCallback<Signature> BindOnce(OnceCallback<Signature> closure) { OnceCallback<Signature> BindOnce(OnceCallback<Signature> callback) {
return closure; return callback;
}
template <typename Signature>
OnceCallback<Signature> BindOnce(RepeatingCallback<Signature> callback) {
return callback;
} }
template <typename Signature> template <typename Signature>
RepeatingCallback<Signature> BindRepeating( RepeatingCallback<Signature> BindRepeating(
RepeatingCallback<Signature> closure) { RepeatingCallback<Signature> callback) {
return closure; return callback;
} }
template <typename Signature> template <typename Signature>
Callback<Signature> Bind(Callback<Signature> closure) { Callback<Signature> Bind(Callback<Signature> callback) {
return closure; return callback;
} }
// Unretained() allows Bind() to bind a non-refcounted class, and to disable // Unretained() allows binding a non-refcounted class, and to disable
// refcounting on arguments that are refcounted objects. // refcounting on arguments that are refcounted objects.
// //
// EXAMPLE OF Unretained(): // EXAMPLE OF Unretained():
@ -302,9 +308,9 @@ Callback<Signature> Bind(Callback<Signature> closure) {
// //
// // In some function somewhere. // // In some function somewhere.
// Foo foo; // Foo foo;
// Closure foo_callback = // OnceClosure foo_callback =
// Bind(&Foo::func, Unretained(&foo)); // BindOnce(&Foo::func, Unretained(&foo));
// foo_callback.Run(); // Prints "Foo:f". // std::move(foo_callback).Run(); // Prints "Foo:f".
// //
// Without the Unretained() wrapper on |&foo|, the above call would fail // Without the Unretained() wrapper on |&foo|, the above call would fail
// to compile because Foo does not support the AddRef() and Release() methods. // to compile because Foo does not support the AddRef() and Release() methods.
@ -321,13 +327,13 @@ static inline internal::UnretainedWrapper<T> Unretained(T* o) {
// void foo(RefCountedBytes* bytes) {} // void foo(RefCountedBytes* bytes) {}
// //
// scoped_refptr<RefCountedBytes> bytes = ...; // scoped_refptr<RefCountedBytes> bytes = ...;
// Closure callback = Bind(&foo, base::RetainedRef(bytes)); // OnceClosure callback = BindOnce(&foo, base::RetainedRef(bytes));
// callback.Run(); // std::move(callback).Run();
// //
// Without RetainedRef, the scoped_refptr would try to implicitly convert to // Without RetainedRef, the scoped_refptr would try to implicitly convert to
// a raw pointer and fail compilation: // a raw pointer and fail compilation:
// //
// Closure callback = Bind(&foo, bytes); // ERROR! // OnceClosure callback = BindOnce(&foo, bytes); // ERROR!
template <typename T> template <typename T>
static inline internal::RetainedRefWrapper<T> RetainedRef(T* o) { static inline internal::RetainedRefWrapper<T> RetainedRef(T* o) {
return internal::RetainedRefWrapper<T>(o); return internal::RetainedRefWrapper<T>(o);
@ -337,40 +343,41 @@ static inline internal::RetainedRefWrapper<T> RetainedRef(scoped_refptr<T> o) {
return internal::RetainedRefWrapper<T>(std::move(o)); return internal::RetainedRefWrapper<T>(std::move(o));
} }
// Owned() transfers ownership of an object to the Callback resulting from // Owned() transfers ownership of an object to the callback resulting from
// bind; the object will be deleted when the Callback is deleted. // bind; the object will be deleted when the callback is deleted.
// //
// EXAMPLE OF Owned(): // EXAMPLE OF Owned():
// //
// void foo(int* arg) { cout << *arg << endl } // void foo(int* arg) { cout << *arg << endl }
// //
// int* pn = new int(1); // int* pn = new int(1);
// Closure foo_callback = Bind(&foo, Owned(pn)); // RepeatingClosure foo_callback = BindRepeating(&foo, Owned(pn));
// //
// foo_callback.Run(); // Prints "1" // foo_callback.Run(); // Prints "1"
// foo_callback.Run(); // Prints "1" // foo_callback.Run(); // Prints "1"
// *n = 2; // *pn = 2;
// foo_callback.Run(); // Prints "2" // foo_callback.Run(); // Prints "2"
// //
// foo_callback.Reset(); // |pn| is deleted. Also will happen when // foo_callback.Reset(); // |pn| is deleted. Also will happen when
// // |foo_callback| goes out of scope. // // |foo_callback| goes out of scope.
// //
// Without Owned(), someone would have to know to delete |pn| when the last // Without Owned(), someone would have to know to delete |pn| when the last
// reference to the Callback is deleted. // reference to the callback is deleted.
template <typename T> template <typename T>
static inline internal::OwnedWrapper<T> Owned(T* o) { static inline internal::OwnedWrapper<T> Owned(T* o) {
return internal::OwnedWrapper<T>(o); return internal::OwnedWrapper<T>(o);
} }
template <typename T> template <typename T, typename Deleter>
static inline internal::OwnedWrapper<T> Owned(std::unique_ptr<T>&& ptr) { static inline internal::OwnedWrapper<T, Deleter> Owned(
return internal::OwnedWrapper<T>(std::move(ptr)); std::unique_ptr<T, Deleter>&& ptr) {
return internal::OwnedWrapper<T, Deleter>(std::move(ptr));
} }
// Passed() is for transferring movable-but-not-copyable types (eg. unique_ptr) // Passed() is for transferring movable-but-not-copyable types (eg. unique_ptr)
// through a Callback. Logically, this signifies a destructive transfer of // through a RepeatingCallback. Logically, this signifies a destructive transfer
// the state of the argument into the target function. Invoking // of the state of the argument into the target function. Invoking
// Callback::Run() twice on a Callback that was created with a Passed() // RepeatingCallback::Run() twice on a callback that was created with a Passed()
// argument will CHECK() because the first invocation would have already // argument will CHECK() because the first invocation would have already
// transferred ownership to the target function. // transferred ownership to the target function.
// //
@ -387,22 +394,22 @@ static inline internal::OwnedWrapper<T> Owned(std::unique_ptr<T>&& ptr) {
// //
// // |cb| is given ownership of Foo(). |f| is now NULL. // // |cb| is given ownership of Foo(). |f| is now NULL.
// // You can use std::move(f) in place of &f, but it's more verbose. // // You can use std::move(f) in place of &f, but it's more verbose.
// Closure cb = Bind(&TakesOwnership, Passed(&f)); // RepeatingClosure cb = BindRepeating(&TakesOwnership, Passed(&f));
// //
// // Run was never called so |cb| still owns Foo() and deletes // // Run was never called so |cb| still owns Foo() and deletes
// // it on Reset(). // // it on Reset().
// cb.Reset(); // cb.Reset();
// //
// // |cb| is given a new Foo created by CreateFoo(). // // |cb| is given a new Foo created by CreateFoo().
// cb = Bind(&TakesOwnership, Passed(CreateFoo())); // cb = BindRepeating(&TakesOwnership, Passed(CreateFoo()));
// //
// // |arg| in TakesOwnership() is given ownership of Foo(). |cb| // // |arg| in TakesOwnership() is given ownership of Foo(). |cb|
// // no longer owns Foo() and, if reset, would not delete Foo(). // // no longer owns Foo() and, if reset, would not delete Foo().
// cb.Run(); // Foo() is now transferred to |arg| and deleted. // cb.Run(); // Foo() is now transferred to |arg| and deleted.
// cb.Run(); // This CHECK()s since Foo() already been used once. // cb.Run(); // This CHECK()s since Foo() already been used once.
// //
// We offer 2 syntaxes for calling Passed(). The first takes an rvalue and // We offer 2 syntaxes for calling Passed(). The first takes an rvalue and is
// is best suited for use with the return value of a function or other temporary // best suited for use with the return value of a function or other temporary
// rvalues. The second takes a pointer to the scoper and is just syntactic sugar // rvalues. The second takes a pointer to the scoper and is just syntactic sugar
// to avoid having to write Passed(std::move(scoper)). // to avoid having to write Passed(std::move(scoper)).
// //
@ -418,21 +425,21 @@ static inline internal::PassedWrapper<T> Passed(T* scoper) {
return internal::PassedWrapper<T>(std::move(*scoper)); return internal::PassedWrapper<T>(std::move(*scoper));
} }
// IgnoreResult() is used to adapt a function or Callback with a return type to // IgnoreResult() is used to adapt a function or callback with a return type to
// one with a void return. This is most useful if you have a function with, // one with a void return. This is most useful if you have a function with,
// say, a pesky ignorable bool return that you want to use with PostTask or // say, a pesky ignorable bool return that you want to use with PostTask or
// something else that expect a Callback with a void return. // something else that expect a callback with a void return.
// //
// EXAMPLE OF IgnoreResult(): // EXAMPLE OF IgnoreResult():
// //
// int DoSomething(int arg) { cout << arg << endl; } // int DoSomething(int arg) { cout << arg << endl; }
// //
// // Assign to a Callback with a void return type. // // Assign to a callback with a void return type.
// Callback<void(int)> cb = Bind(IgnoreResult(&DoSomething)); // OnceCallback<void(int)> cb = BindOnce(IgnoreResult(&DoSomething));
// cb->Run(1); // Prints "1". // std::move(cb).Run(1); // Prints "1".
// //
// // Prints "1" on |ml|. // // Prints "2" on |ml|.
// ml->PostTask(FROM_HERE, BindOnce(IgnoreResult(&DoSomething), 1); // ml->PostTask(FROM_HERE, BindOnce(IgnoreResult(&DoSomething), 2);
template <typename T> template <typename T>
static inline internal::IgnoreResultHelper<T> IgnoreResult(T data) { static inline internal::IgnoreResultHelper<T> IgnoreResult(T data) {
return internal::IgnoreResultHelper<T>(std::move(data)); return internal::IgnoreResultHelper<T>(std::move(data));
@ -447,8 +454,9 @@ static inline internal::IgnoreResultHelper<T> IgnoreResult(T data) {
// EXAMPLE OF RetainBlock(): // EXAMPLE OF RetainBlock():
// //
// // Wrap the block and bind it to a callback. // // Wrap the block and bind it to a callback.
// Callback<void(int)> cb = Bind(RetainBlock(^(int n) { NSLog(@"%d", n); })); // OnceCallback<void(int)> cb =
// cb.Run(1); // Logs "1". // BindOnce(RetainBlock(^(int n) { NSLog(@"%d", n); }));
// std::move(cb).Run(1); // Logs "1".
template <typename R, typename... Args> template <typename R, typename... Args>
base::mac::ScopedBlock<R (^)(Args...)> RetainBlock(R (^block)(Args...)) { base::mac::ScopedBlock<R (^)(Args...)> RetainBlock(R (^block)(Args...)) {
return base::mac::ScopedBlock<R (^)(Args...)>(block, return base::mac::ScopedBlock<R (^)(Args...)>(block,

View File

@ -16,7 +16,7 @@
#include "build/build_config.h" #include "build/build_config.h"
// This defines a set of simple functions and utilities that people want when // This defines a set of simple functions and utilities that people want when
// using Callback<> and Bind(). // using {Once,Repeating}Callback<> and Bind{Once,Repeating}().
namespace base { namespace base {

View File

@ -104,15 +104,16 @@ struct IgnoreResultHelper {
T functor_; T functor_;
}; };
template <typename T> template <typename T, typename Deleter = std::default_delete<T>>
class OwnedWrapper { class OwnedWrapper {
public: public:
explicit OwnedWrapper(T* o) : ptr_(o) {} explicit OwnedWrapper(T* o) : ptr_(o) {}
explicit OwnedWrapper(std::unique_ptr<T>&& ptr) : ptr_(std::move(ptr)) {} explicit OwnedWrapper(std::unique_ptr<T, Deleter>&& ptr)
: ptr_(std::move(ptr)) {}
T* get() const { return ptr_.get(); } T* get() const { return ptr_.get(); }
private: private:
std::unique_ptr<T> ptr_; std::unique_ptr<T, Deleter> ptr_;
}; };
// PassedWrapper is a copyable adapter for a scoper that ignores const. // PassedWrapper is a copyable adapter for a scoper that ignores const.
@ -354,10 +355,9 @@ template <typename Functor, typename SFINAE>
struct FunctorTraits; struct FunctorTraits;
// For empty callable types. // For empty callable types.
// This specialization is intended to allow binding captureless lambdas by // This specialization is intended to allow binding captureless lambdas, based
// base::Bind(), based on the fact that captureless lambdas are empty while // on the fact that captureless lambdas are empty while capturing lambdas are
// capturing lambdas are not. This also allows any functors as far as it's an // not. This also allows any functors as far as it's an empty class.
// empty class.
// Example: // Example:
// //
// // Captureless lambdas are allowed. // // Captureless lambdas are allowed.
@ -791,10 +791,10 @@ BanUnconstructedRefCountedReceiver(const Receiver& receiver, Unused&&...) {
// //
// scoped_refptr<Foo> oo = Foo::Create(); // scoped_refptr<Foo> oo = Foo::Create();
DCHECK(receiver->HasAtLeastOneRef()) DCHECK(receiver->HasAtLeastOneRef())
<< "base::Bind() refuses to create the first reference to ref-counted " << "base::Bind{Once,Repeating}() refuses to create the first reference "
"objects. That is typically happens around PostTask() in their " "to ref-counted objects. That typically happens around PostTask() in "
"constructor, and such objects can be destroyed before `new` returns " "their constructor, and such objects can be destroyed before `new` "
"if the task resolves fast enough."; "returns if the task resolves fast enough.";
} }
// BindState<> // BindState<>
@ -917,7 +917,7 @@ using MakeBindStateType =
// }; // };
// //
// WeakPtr<Foo> oo = nullptr; // WeakPtr<Foo> oo = nullptr;
// base::Bind(&Foo::bar, oo).Run(); // base::BindOnce(&Foo::bar, oo).Run();
template <typename T> template <typename T>
struct IsWeakReceiver : std::false_type {}; struct IsWeakReceiver : std::false_type {};
@ -953,9 +953,11 @@ struct BindUnwrapTraits<internal::RetainedRefWrapper<T>> {
static T* Unwrap(const internal::RetainedRefWrapper<T>& o) { return o.get(); } static T* Unwrap(const internal::RetainedRefWrapper<T>& o) { return o.get(); }
}; };
template <typename T> template <typename T, typename Deleter>
struct BindUnwrapTraits<internal::OwnedWrapper<T>> { struct BindUnwrapTraits<internal::OwnedWrapper<T, Deleter>> {
static T* Unwrap(const internal::OwnedWrapper<T>& o) { return o.get(); } static T* Unwrap(const internal::OwnedWrapper<T, Deleter>& o) {
return o.get();
}
}; };
template <typename T> template <typename T>

View File

@ -0,0 +1,209 @@
// Copyright (c) 2013 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.
// This file defines some bit utilities.
#ifndef BASE_BITS_H_
#define BASE_BITS_H_
#include <stddef.h>
#include <stdint.h>
#include <type_traits>
#include "base/compiler_specific.h"
#include "base/logging.h"
#include "build/build_config.h"
#if defined(COMPILER_MSVC)
#include <intrin.h>
#endif
namespace base {
namespace bits {
// Returns true iff |value| is a power of 2.
template <typename T,
typename = typename std::enable_if<std::is_integral<T>::value>>
constexpr inline bool IsPowerOfTwo(T value) {
// From "Hacker's Delight": Section 2.1 Manipulating Rightmost Bits.
//
// Only positive integers with a single bit set are powers of two. If only one
// bit is set in x (e.g. 0b00000100000000) then |x-1| will have that bit set
// to zero and all bits to its right set to 1 (e.g. 0b00000011111111). Hence
// |x & (x-1)| is 0 iff x is a power of two.
return value > 0 && (value & (value - 1)) == 0;
}
// Round up |size| to a multiple of alignment, which must be a power of two.
inline size_t Align(size_t size, size_t alignment) {
DCHECK(IsPowerOfTwo(alignment));
return (size + alignment - 1) & ~(alignment - 1);
}
// Round down |size| to a multiple of alignment, which must be a power of two.
inline size_t AlignDown(size_t size, size_t alignment) {
DCHECK(IsPowerOfTwo(alignment));
return size & ~(alignment - 1);
}
// CountLeadingZeroBits(value) returns the number of zero bits following the
// most significant 1 bit in |value| if |value| is non-zero, otherwise it
// returns {sizeof(T) * 8}.
// Example: 00100010 -> 2
//
// CountTrailingZeroBits(value) returns the number of zero bits preceding the
// least significant 1 bit in |value| if |value| is non-zero, otherwise it
// returns {sizeof(T) * 8}.
// Example: 00100010 -> 1
//
// C does not have an operator to do this, but fortunately the various
// compilers have built-ins that map to fast underlying processor instructions.
#if defined(COMPILER_MSVC)
template <typename T, unsigned bits = sizeof(T) * 8>
ALWAYS_INLINE
typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) <= 4,
unsigned>::type
CountLeadingZeroBits(T x) {
static_assert(bits > 0, "invalid instantiation");
unsigned long index;
return LIKELY(_BitScanReverse(&index, static_cast<uint32_t>(x)))
? (31 - index - (32 - bits))
: bits;
}
template <typename T, unsigned bits = sizeof(T) * 8>
ALWAYS_INLINE
typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) == 8,
unsigned>::type
CountLeadingZeroBits(T x) {
static_assert(bits > 0, "invalid instantiation");
unsigned long index;
// MSVC only supplies _BitScanReverse64 when building for a 64-bit target.
#if defined(ARCH_CPU_64_BITS)
return LIKELY(_BitScanReverse64(&index, static_cast<uint64_t>(x)))
? (63 - index)
: 64;
#else
uint32_t left = static_cast<uint32_t>(x >> 32);
if (LIKELY(_BitScanReverse(&index, left)))
return 31 - index;
uint32_t right = static_cast<uint32_t>(x);
if (LIKELY(_BitScanReverse(&index, right)))
return 63 - index;
return 64;
#endif
}
template <typename T, unsigned bits = sizeof(T) * 8>
ALWAYS_INLINE
typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) <= 4,
unsigned>::type
CountTrailingZeroBits(T x) {
static_assert(bits > 0, "invalid instantiation");
unsigned long index;
return LIKELY(_BitScanForward(&index, static_cast<uint32_t>(x))) ? index
: bits;
}
template <typename T, unsigned bits = sizeof(T) * 8>
ALWAYS_INLINE
typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) == 8,
unsigned>::type
CountTrailingZeroBits(T x) {
static_assert(bits > 0, "invalid instantiation");
unsigned long index;
// MSVC only supplies _BitScanForward64 when building for a 64-bit target.
#if defined(ARCH_CPU_64_BITS)
return LIKELY(_BitScanForward64(&index, static_cast<uint64_t>(x))) ? index
: 64;
#else
uint32_t right = static_cast<uint32_t>(x);
if (LIKELY(_BitScanForward(&index, right)))
return index;
uint32_t left = static_cast<uint32_t>(x >> 32);
if (LIKELY(_BitScanForward(&index, left)))
return 32 + index;
return 64;
#endif
}
ALWAYS_INLINE uint32_t CountLeadingZeroBits32(uint32_t x) {
return CountLeadingZeroBits(x);
}
ALWAYS_INLINE uint64_t CountLeadingZeroBits64(uint64_t x) {
return CountLeadingZeroBits(x);
}
#elif defined(COMPILER_GCC)
// __builtin_clz has undefined behaviour for an input of 0, even though there's
// clearly a return value that makes sense, and even though some processor clz
// instructions have defined behaviour for 0. We could drop to raw __asm__ to
// do better, but we'll avoid doing that unless we see proof that we need to.
template <typename T, unsigned bits = sizeof(T) * 8>
ALWAYS_INLINE
typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) <= 8,
unsigned>::type
CountLeadingZeroBits(T value) {
static_assert(bits > 0, "invalid instantiation");
return LIKELY(value)
? bits == 64
? __builtin_clzll(static_cast<uint64_t>(value))
: __builtin_clz(static_cast<uint32_t>(value)) - (32 - bits)
: bits;
}
template <typename T, unsigned bits = sizeof(T) * 8>
ALWAYS_INLINE
typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) <= 8,
unsigned>::type
CountTrailingZeroBits(T value) {
return LIKELY(value) ? bits == 64
? __builtin_ctzll(static_cast<uint64_t>(value))
: __builtin_ctz(static_cast<uint32_t>(value))
: bits;
}
ALWAYS_INLINE uint32_t CountLeadingZeroBits32(uint32_t x) {
return CountLeadingZeroBits(x);
}
ALWAYS_INLINE uint64_t CountLeadingZeroBits64(uint64_t x) {
return CountLeadingZeroBits(x);
}
#endif
ALWAYS_INLINE size_t CountLeadingZeroBitsSizeT(size_t x) {
return CountLeadingZeroBits(x);
}
ALWAYS_INLINE size_t CountTrailingZeroBitsSizeT(size_t x) {
return CountTrailingZeroBits(x);
}
// Returns the integer i such as 2^i <= n < 2^(i+1)
inline int Log2Floor(uint32_t n) {
return 31 - CountLeadingZeroBits(n);
}
// Returns the integer i such as 2^(i-1) < n <= 2^i
inline int Log2Ceiling(uint32_t n) {
// When n == 0, we want the function to return -1.
// When n == 0, (n - 1) will underflow to 0xFFFFFFFF, which is
// why the statement below starts with (n ? 32 : -1).
return (n ? 32 : -1) - CountLeadingZeroBits(n - 1);
}
} // namespace bits
} // namespace base
#endif // BASE_BITS_H_

View File

@ -2,8 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// //
// NOTE: Header files that do not require the full definition of Callback or // NOTE: Header files that do not require the full definition of
// Closure should #include "base/callback_forward.h" instead of this file. // base::{Once,Repeating}Callback or base::{Once,Repeating}Closure should
// #include "base/callback_forward.h" instead of this file.
#ifndef BASE_CALLBACK_H_ #ifndef BASE_CALLBACK_H_
#define BASE_CALLBACK_H_ #define BASE_CALLBACK_H_
@ -42,7 +43,7 @@
// //
// Callbacks also support cancellation. A common use is binding the receiver // Callbacks also support cancellation. A common use is binding the receiver
// object as a WeakPtr<T>. If that weak pointer is invalidated, calling Run() // object as a WeakPtr<T>. If that weak pointer is invalidated, calling Run()
// will be a no-op. Note that |is_cancelled()| and |is_null()| are distinct: // will be a no-op. Note that |IsCancelled()| and |is_null()| are distinct:
// simply cancelling a callback will not also make it null. // simply cancelling a callback will not also make it null.
// //
// base::Callback is currently a type alias for base::RepeatingCallback. In the // base::Callback is currently a type alias for base::RepeatingCallback. In the
@ -117,10 +118,14 @@ class RepeatingCallback<R(Args...)> : public internal::CallbackBaseCopyable {
RepeatingCallback(RepeatingCallback&&) noexcept = default; RepeatingCallback(RepeatingCallback&&) noexcept = default;
RepeatingCallback& operator=(RepeatingCallback&&) noexcept = default; RepeatingCallback& operator=(RepeatingCallback&&) noexcept = default;
bool Equals(const RepeatingCallback& other) const { bool operator==(const RepeatingCallback& other) const {
return EqualsInternal(other); return EqualsInternal(other);
} }
bool operator!=(const RepeatingCallback& other) const {
return !operator==(other);
}
R Run(Args... args) const & { R Run(Args... args) const & {
PolymorphicInvoke f = PolymorphicInvoke f =
reinterpret_cast<PolymorphicInvoke>(this->polymorphic_invoke()); reinterpret_cast<PolymorphicInvoke>(this->polymorphic_invoke());
@ -135,7 +140,7 @@ class RepeatingCallback<R(Args...)> : public internal::CallbackBaseCopyable {
RepeatingCallback cb = std::move(*this); RepeatingCallback cb = std::move(*this);
PolymorphicInvoke f = PolymorphicInvoke f =
reinterpret_cast<PolymorphicInvoke>(cb.polymorphic_invoke()); reinterpret_cast<PolymorphicInvoke>(cb.polymorphic_invoke());
return f(cb.bind_state_.get(), std::forward<Args>(args)...); return f(std::move(cb).bind_state_.get(), std::forward<Args>(args)...);
} }
}; };

View File

@ -16,8 +16,9 @@ class RepeatingCallback;
template <typename Signature> template <typename Signature>
using Callback = RepeatingCallback<Signature>; using Callback = RepeatingCallback<Signature>;
// Syntactic sugar to make Callback<void()> easier to declare since it // Syntactic sugar to make OnceClosure<void()> and RepeatingClosure<void()>
// will be used in a lot of APIs with delayed execution. // easier to declare since they will be used in a lot of APIs with delayed
// execution.
using OnceClosure = OnceCallback<void()>; using OnceClosure = OnceCallback<void()>;
using RepeatingClosure = RepeatingCallback<void()>; using RepeatingClosure = RepeatingCallback<void()>;
using Closure = Callback<void()>; using Closure = Callback<void()>;

View File

@ -19,14 +19,19 @@ struct FakeBindState;
namespace internal { namespace internal {
class CallbackBase;
class CallbackBaseCopyable;
class BindStateBase; class BindStateBase;
class FinallyExecutorCommon;
class ThenAndCatchExecutorCommon;
template <typename ReturnType>
class PostTaskExecutor;
template <typename Functor, typename... BoundArgs> template <typename Functor, typename... BoundArgs>
struct BindState; struct BindState;
class CallbackBase;
class CallbackBaseCopyable;
struct BindStateBaseRefCountTraits { struct BindStateBaseRefCountTraits {
static void Destruct(const BindStateBase*); static void Destruct(const BindStateBase*);
}; };
@ -40,11 +45,10 @@ using PassingType = std::conditional_t<std::is_scalar<T>::value, T, T&&>;
// DoInvoke function to perform the function execution. This allows // DoInvoke function to perform the function execution. This allows
// us to shield the Callback class from the types of the bound argument via // us to shield the Callback class from the types of the bound argument via
// "type erasure." // "type erasure."
// At the base level, the only task is to add reference counting data. Don't use // At the base level, the only task is to add reference counting data. Avoid
// RefCountedThreadSafe since it requires the destructor to be a virtual method. // using or inheriting any virtual functions. Creating a vtable for every
// Creating a vtable for every BindState template instantiation results in a lot // BindState template instantiation results in a lot of bloat. Its only task is
// of bloat. Its only task is to call the destructor which can be done with a // to call the destructor which can be done with a function pointer.
// function pointer.
class BASE_EXPORT BindStateBase class BASE_EXPORT BindStateBase
: public RefCountedThreadSafe<BindStateBase, BindStateBaseRefCountTraits> { : public RefCountedThreadSafe<BindStateBase, BindStateBaseRefCountTraits> {
public: public:
@ -135,6 +139,12 @@ class BASE_EXPORT CallbackBase {
void Reset(); void Reset();
protected: protected:
friend class FinallyExecutorCommon;
friend class ThenAndCatchExecutorCommon;
template <typename ReturnType>
friend class PostTaskExecutor;
using InvokeFuncStorage = BindStateBase::InvokeFuncStorage; using InvokeFuncStorage = BindStateBase::InvokeFuncStorage;
// Returns true if this callback equals |other|. |other| may be null. // Returns true if this callback equals |other|. |other| may be null.

View File

@ -9,6 +9,10 @@
#if defined(COMPILER_MSVC) #if defined(COMPILER_MSVC)
#if !defined(__clang__)
#error "Only clang-cl is supported on Windows, see https://crbug.com/988071"
#endif
// Macros for suppressing and disabling warnings on MSVC. // Macros for suppressing and disabling warnings on MSVC.
// //
// Warning numbers are enumerated at: // Warning numbers are enumerated at:
@ -22,8 +26,8 @@
// MSVC_PUSH_DISABLE_WARNING pushes |n| onto a stack of warnings to be disabled. // MSVC_PUSH_DISABLE_WARNING pushes |n| onto a stack of warnings to be disabled.
// The warning remains disabled until popped by MSVC_POP_WARNING. // The warning remains disabled until popped by MSVC_POP_WARNING.
#define MSVC_PUSH_DISABLE_WARNING(n) __pragma(warning(push)) \ #define MSVC_PUSH_DISABLE_WARNING(n) \
__pragma(warning(disable:n)) __pragma(warning(push)) __pragma(warning(disable : n))
// Pop effects of innermost MSVC_PUSH_* macro. // Pop effects of innermost MSVC_PUSH_* macro.
#define MSVC_POP_WARNING() __pragma(warning(pop)) #define MSVC_POP_WARNING() __pragma(warning(pop))
@ -137,7 +141,7 @@
// For member functions, the implicit this parameter counts as index 1. // For member functions, the implicit this parameter counts as index 1.
#if defined(COMPILER_GCC) || defined(__clang__) #if defined(COMPILER_GCC) || defined(__clang__)
#define PRINTF_FORMAT(format_param, dots_param) \ #define PRINTF_FORMAT(format_param, dots_param) \
__attribute__((format(printf, format_param, dots_param))) __attribute__((format(printf, format_param, dots_param)))
#else #else
#define PRINTF_FORMAT(format_param, dots_param) #define PRINTF_FORMAT(format_param, dots_param)
#endif #endif
@ -166,14 +170,14 @@
// Mark a memory region fully initialized. // Mark a memory region fully initialized.
// Use this to annotate code that deliberately reads uninitialized data, for // Use this to annotate code that deliberately reads uninitialized data, for
// example a GC scavenging root set pointers from the stack. // example a GC scavenging root set pointers from the stack.
#define MSAN_UNPOISON(p, size) __msan_unpoison(p, size) #define MSAN_UNPOISON(p, size) __msan_unpoison(p, size)
// Check a memory region for initializedness, as if it was being used here. // Check a memory region for initializedness, as if it was being used here.
// If any bits are uninitialized, crash with an MSan report. // If any bits are uninitialized, crash with an MSan report.
// Use this to sanitize data which MSan won't be able to track, e.g. before // Use this to sanitize data which MSan won't be able to track, e.g. before
// passing data to another process via shared memory. // passing data to another process via shared memory.
#define MSAN_CHECK_MEM_IS_INITIALIZED(p, size) \ #define MSAN_CHECK_MEM_IS_INITIALIZED(p, size) \
__msan_check_mem_is_initialized(p, size) __msan_check_mem_is_initialized(p, size)
#else // MEMORY_SANITIZER #else // MEMORY_SANITIZER
#define MSAN_UNPOISON(p, size) #define MSAN_UNPOISON(p, size)
#define MSAN_CHECK_MEM_IS_INITIALIZED(p, size) #define MSAN_CHECK_MEM_IS_INITIALIZED(p, size)
@ -238,4 +242,57 @@
#define PRETTY_FUNCTION __func__ #define PRETTY_FUNCTION __func__
#endif #endif
#if !defined(CPU_ARM_NEON)
#if defined(__arm__)
#if !defined(__ARMEB__) && !defined(__ARM_EABI__) && !defined(__EABI__) && \
!defined(__VFP_FP__) && !defined(_WIN32_WCE) && !defined(ANDROID)
#error Chromium does not support middle endian architecture
#endif
#if defined(__ARM_NEON__)
#define CPU_ARM_NEON 1
#endif
#endif // defined(__arm__)
#endif // !defined(CPU_ARM_NEON)
#if !defined(HAVE_MIPS_MSA_INTRINSICS)
#if defined(__mips_msa) && defined(__mips_isa_rev) && (__mips_isa_rev >= 5)
#define HAVE_MIPS_MSA_INTRINSICS 1
#endif
#endif
#if defined(__clang__) && __has_attribute(uninitialized)
// Attribute "uninitialized" disables -ftrivial-auto-var-init=pattern for
// the specified variable.
// Library-wide alternative is
// 'configs -= [ "//build/config/compiler:default_init_stack_vars" ]' in .gn
// file.
//
// See "init_stack_vars" in build/config/compiler/BUILD.gn and
// http://crbug.com/977230
// "init_stack_vars" is enabled for non-official builds and we hope to enable it
// in official build in 2020 as well. The flag writes fixed pattern into
// uninitialized parts of all local variables. In rare cases such initialization
// is undesirable and attribute can be used:
// 1. Degraded performance
// In most cases compiler is able to remove additional stores. E.g. if memory is
// never accessed or properly initialized later. Preserved stores mostly will
// not affect program performance. However if compiler failed on some
// performance critical code we can get a visible regression in a benchmark.
// 2. memset, memcpy calls
// Compiler may replaces some memory writes with memset or memcpy calls. This is
// not -ftrivial-auto-var-init specific, but it can happen more likely with the
// flag. It can be a problem if code is not linked with C run-time library.
//
// Note: The flag is security risk mitigation feature. So in future the
// attribute uses should be avoided when possible. However to enable this
// mitigation on the most of the code we need to be less strict now and minimize
// number of exceptions later. So if in doubt feel free to use attribute, but
// please document the problem for someone who is going to cleanup it later.
// E.g. platform, bot, benchmark or test name in patch description or next to
// the attribute.
#define STACK_UNINITIALIZED __attribute__((uninitialized))
#else
#define STACK_UNINITIALIZED
#endif
#endif // BASE_COMPILER_SPECIFIC_H_ #endif // BASE_COMPILER_SPECIFIC_H_

View File

@ -0,0 +1,145 @@
// Copyright 2019 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 BASE_CONTAINERS_BUFFER_ITERATOR_H_
#define BASE_CONTAINERS_BUFFER_ITERATOR_H_
#include <type_traits>
#include "base/bit_cast.h"
#include "base/containers/span.h"
#include "base/numerics/checked_math.h"
namespace base {
// BufferIterator is a bounds-checked container utility to access variable-
// length, heterogeneous structures contained within a buffer. If the data are
// homogeneous, use base::span<> instead.
//
// After being created with a weakly-owned buffer, BufferIterator returns
// pointers to structured data within the buffer. After each method call that
// returns data in the buffer, the iterator position is advanced by the byte
// size of the object (or span of objects) returned. If there are not enough
// bytes remaining in the buffer to return the requested object(s), a nullptr
// or empty span is returned.
//
// This class is similar to base::Pickle, which should be preferred for
// serializing to disk. Pickle versions its header and does not support writing
// structures, which are problematic for serialization due to struct padding and
// version shear concerns.
//
// Example usage:
//
// std::vector<uint8_t> buffer(4096);
// if (!ReadSomeData(&buffer, buffer.size())) {
// LOG(ERROR) << "Failed to read data.";
// return false;
// }
//
// BufferIterator<uint8_t> iterator(buffer);
// uint32_t* num_items = iterator.Object<uint32_t>();
// if (!num_items) {
// LOG(ERROR) << "No num_items field."
// return false;
// }
//
// base::span<const item_struct> items =
// iterator.Span<item_struct>(*num_items);
// if (items.size() != *num_items) {
// LOG(ERROR) << "Not enough items.";
// return false;
// }
//
// // ... validate the objects in |items|.
template <typename B>
class BufferIterator {
public:
static_assert(std::is_same<std::remove_const_t<B>, char>::value ||
std::is_same<std::remove_const_t<B>, unsigned char>::value,
"Underlying buffer type must be char-type.");
BufferIterator() {}
BufferIterator(B* data, size_t size)
: BufferIterator(make_span(data, size)) {}
explicit BufferIterator(span<B> buffer)
: buffer_(buffer), remaining_(buffer) {}
~BufferIterator() {}
// Returns a pointer to a mutable structure T in the buffer at the current
// position. On success, the iterator position is advanced by sizeof(T). If
// there are not sizeof(T) bytes remaining in the buffer, returns nullptr.
template <typename T,
typename =
typename std::enable_if_t<std::is_trivially_copyable<T>::value>>
T* MutableObject() {
size_t size = sizeof(T);
size_t next_position;
if (!CheckAdd(position(), size).AssignIfValid(&next_position))
return nullptr;
if (next_position > total_size())
return nullptr;
T* t = bit_cast<T*>(remaining_.data());
remaining_ = remaining_.subspan(size);
return t;
}
// Returns a const pointer to an object of type T in the buffer at the current
// position.
template <typename T,
typename =
typename std::enable_if_t<std::is_trivially_copyable<T>::value>>
const T* Object() {
return MutableObject<const T>();
}
// Returns a span of |count| T objects in the buffer at the current position.
// On success, the iterator position is advanced by |sizeof(T) * count|. If
// there are not enough bytes remaining in the buffer to fulfill the request,
// returns an empty span.
template <typename T,
typename =
typename std::enable_if_t<std::is_trivially_copyable<T>::value>>
span<T> MutableSpan(size_t count) {
size_t size;
if (!CheckMul(sizeof(T), count).AssignIfValid(&size))
return span<T>();
size_t next_position;
if (!CheckAdd(position(), size).AssignIfValid(&next_position))
return span<T>();
if (next_position > total_size())
return span<T>();
auto result = span<T>(bit_cast<T*>(remaining_.data()), count);
remaining_ = remaining_.subspan(size);
return result;
}
// Returns a span to |count| const objects of type T in the buffer at the
// current position.
template <typename T,
typename =
typename std::enable_if_t<std::is_trivially_copyable<T>::value>>
span<const T> Span(size_t count) {
return MutableSpan<const T>(count);
}
// Resets the iterator position to the absolute offset |to|.
void Seek(size_t to) { remaining_ = buffer_.subspan(to); }
// Returns the total size of the underlying buffer.
size_t total_size() { return buffer_.size(); }
// Returns the current position in the buffer.
size_t position() { return buffer_.size_bytes() - remaining_.size_bytes(); }
private:
// The original buffer that the iterator was constructed with.
const span<B> buffer_;
// A subspan of |buffer_| containing the remaining bytes to iterate over.
span<B> remaining_;
// Copy and assign allowed.
};
} // namespace base
#endif // BASE_CONTAINERS_BUFFER_ITERATOR_H_

View File

@ -0,0 +1,205 @@
// Copyright 2018 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 BASE_CONTAINERS_CHECKED_ITERATORS_H_
#define BASE_CONTAINERS_CHECKED_ITERATORS_H_
#include <iterator>
#include <memory>
#include <type_traits>
#include "base/containers/util.h"
#include "base/logging.h"
namespace base {
template <typename T>
class CheckedContiguousIterator {
public:
using difference_type = std::ptrdiff_t;
using value_type = std::remove_cv_t<T>;
using pointer = T*;
using reference = T&;
using iterator_category = std::random_access_iterator_tag;
// Required for converting constructor below.
template <typename U>
friend class CheckedContiguousIterator;
constexpr CheckedContiguousIterator() = default;
constexpr CheckedContiguousIterator(T* start, const T* end)
: CheckedContiguousIterator(start, start, end) {}
constexpr CheckedContiguousIterator(const T* start, T* current, const T* end)
: start_(start), current_(current), end_(end) {
CHECK_LE(start, current);
CHECK_LE(current, end);
}
constexpr CheckedContiguousIterator(const CheckedContiguousIterator& other) =
default;
// Converting constructor allowing conversions like CCI<T> to CCI<const T>,
// but disallowing CCI<const T> to CCI<T> or CCI<Derived> to CCI<Base>, which
// are unsafe. Furthermore, this is the same condition as used by the
// converting constructors of std::span<T> and std::unique_ptr<T[]>.
// See https://wg21.link/n4042 for details.
template <
typename U,
std::enable_if_t<std::is_convertible<U (*)[], T (*)[]>::value>* = nullptr>
constexpr CheckedContiguousIterator(const CheckedContiguousIterator<U>& other)
: start_(other.start_), current_(other.current_), end_(other.end_) {
// We explicitly don't delegate to the 3-argument constructor here. Its
// CHECKs would be redundant, since we expect |other| to maintain its own
// invariant. However, DCHECKs never hurt anybody. Presumably.
DCHECK_LE(other.start_, other.current_);
DCHECK_LE(other.current_, other.end_);
}
~CheckedContiguousIterator() = default;
constexpr CheckedContiguousIterator& operator=(
const CheckedContiguousIterator& other) = default;
constexpr bool operator==(const CheckedContiguousIterator& other) const {
CheckComparable(other);
return current_ == other.current_;
}
constexpr bool operator!=(const CheckedContiguousIterator& other) const {
CheckComparable(other);
return current_ != other.current_;
}
constexpr bool operator<(const CheckedContiguousIterator& other) const {
CheckComparable(other);
return current_ < other.current_;
}
constexpr bool operator<=(const CheckedContiguousIterator& other) const {
CheckComparable(other);
return current_ <= other.current_;
}
constexpr bool operator>(const CheckedContiguousIterator& other) const {
CheckComparable(other);
return current_ > other.current_;
}
constexpr bool operator>=(const CheckedContiguousIterator& other) const {
CheckComparable(other);
return current_ >= other.current_;
}
constexpr CheckedContiguousIterator& operator++() {
CHECK_NE(current_, end_);
++current_;
return *this;
}
constexpr CheckedContiguousIterator operator++(int) {
CheckedContiguousIterator old = *this;
++*this;
return old;
}
constexpr CheckedContiguousIterator& operator--() {
CHECK_NE(current_, start_);
--current_;
return *this;
}
constexpr CheckedContiguousIterator operator--(int) {
CheckedContiguousIterator old = *this;
--*this;
return old;
}
constexpr CheckedContiguousIterator& operator+=(difference_type rhs) {
if (rhs > 0) {
CHECK_LE(rhs, end_ - current_);
} else {
CHECK_LE(-rhs, current_ - start_);
}
current_ += rhs;
return *this;
}
constexpr CheckedContiguousIterator operator+(difference_type rhs) const {
CheckedContiguousIterator it = *this;
it += rhs;
return it;
}
constexpr CheckedContiguousIterator& operator-=(difference_type rhs) {
if (rhs < 0) {
CHECK_LE(-rhs, end_ - current_);
} else {
CHECK_LE(rhs, current_ - start_);
}
current_ -= rhs;
return *this;
}
constexpr CheckedContiguousIterator operator-(difference_type rhs) const {
CheckedContiguousIterator it = *this;
it -= rhs;
return it;
}
constexpr friend difference_type operator-(
const CheckedContiguousIterator& lhs,
const CheckedContiguousIterator& rhs) {
CHECK_EQ(lhs.start_, rhs.start_);
CHECK_EQ(lhs.end_, rhs.end_);
return lhs.current_ - rhs.current_;
}
constexpr reference operator*() const {
CHECK_NE(current_, end_);
return *current_;
}
constexpr pointer operator->() const {
CHECK_NE(current_, end_);
return current_;
}
constexpr reference operator[](difference_type rhs) const {
CHECK_GE(rhs, 0);
CHECK_LT(rhs, end_ - current_);
return current_[rhs];
}
static bool IsRangeMoveSafe(const CheckedContiguousIterator& from_begin,
const CheckedContiguousIterator& from_end,
const CheckedContiguousIterator& to)
WARN_UNUSED_RESULT {
if (from_end < from_begin)
return false;
const auto from_begin_uintptr = get_uintptr(from_begin.current_);
const auto from_end_uintptr = get_uintptr(from_end.current_);
const auto to_begin_uintptr = get_uintptr(to.current_);
const auto to_end_uintptr =
get_uintptr((to + std::distance(from_begin, from_end)).current_);
return to_begin_uintptr >= from_end_uintptr ||
to_end_uintptr <= from_begin_uintptr;
}
private:
constexpr void CheckComparable(const CheckedContiguousIterator& other) const {
CHECK_EQ(start_, other.start_);
CHECK_EQ(end_, other.end_);
}
const T* start_ = nullptr;
T* current_ = nullptr;
const T* end_ = nullptr;
};
template <typename T>
using CheckedContiguousConstIterator = CheckedContiguousIterator<const T>;
} // namespace base
#endif // BASE_CONTAINERS_CHECKED_ITERATORS_H_

View File

@ -14,6 +14,7 @@
#include "base/containers/vector_buffer.h" #include "base/containers/vector_buffer.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/stl_util.h"
#include "base/template_util.h" #include "base/template_util.h"
// base::circular_deque is similar to std::deque. Unlike std::deque, the // base::circular_deque is similar to std::deque. Unlike std::deque, the
@ -521,15 +522,15 @@ class circular_deque {
return buffer_[i - right_size]; return buffer_[i - right_size];
} }
value_type& at(size_type i) { value_type& at(size_type i) {
return const_cast<value_type&>( return const_cast<value_type&>(as_const(*this).at(i));
const_cast<const circular_deque*>(this)->at(i));
} }
value_type& operator[](size_type i) { return at(i); } value_type& operator[](size_type i) {
const value_type& operator[](size_type i) const { return const_cast<value_type&>(as_const(*this)[i]);
return const_cast<circular_deque*>(this)->at(i);
} }
const value_type& operator[](size_type i) const { return at(i); }
value_type& front() { value_type& front() {
DCHECK(!empty()); DCHECK(!empty());
return buffer_[begin_]; return buffer_[begin_];

View File

@ -0,0 +1,530 @@
// Copyright 2017 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 BASE_CONTAINERS_SPAN_H_
#define BASE_CONTAINERS_SPAN_H_
#include <stddef.h>
#include <algorithm>
#include <array>
#include <iterator>
#include <limits>
#include <type_traits>
#include <utility>
#include "base/containers/checked_iterators.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/stl_util.h"
namespace base {
// [views.constants]
constexpr size_t dynamic_extent = std::numeric_limits<size_t>::max();
template <typename T, size_t Extent = dynamic_extent>
class span;
namespace internal {
template <typename T>
struct ExtentImpl : std::integral_constant<size_t, dynamic_extent> {};
template <typename T, size_t N>
struct ExtentImpl<T[N]> : std::integral_constant<size_t, N> {};
template <typename T, size_t N>
struct ExtentImpl<std::array<T, N>> : std::integral_constant<size_t, N> {};
template <typename T, size_t N>
struct ExtentImpl<base::span<T, N>> : std::integral_constant<size_t, N> {};
template <typename T>
using Extent = ExtentImpl<std::remove_cv_t<std::remove_reference_t<T>>>;
template <typename T>
struct IsSpanImpl : std::false_type {};
template <typename T, size_t Extent>
struct IsSpanImpl<span<T, Extent>> : std::true_type {};
template <typename T>
using IsSpan = IsSpanImpl<std::decay_t<T>>;
template <typename T>
struct IsStdArrayImpl : std::false_type {};
template <typename T, size_t N>
struct IsStdArrayImpl<std::array<T, N>> : std::true_type {};
template <typename T>
using IsStdArray = IsStdArrayImpl<std::decay_t<T>>;
template <typename T>
using IsCArray = std::is_array<std::remove_reference_t<T>>;
template <typename From, typename To>
using IsLegalDataConversion = std::is_convertible<From (*)[], To (*)[]>;
template <typename Container, typename T>
using ContainerHasConvertibleData = IsLegalDataConversion<
std::remove_pointer_t<decltype(base::data(std::declval<Container>()))>,
T>;
template <typename Container>
using ContainerHasIntegralSize =
std::is_integral<decltype(base::size(std::declval<Container>()))>;
template <typename From, size_t FromExtent, typename To, size_t ToExtent>
using EnableIfLegalSpanConversion =
std::enable_if_t<(ToExtent == dynamic_extent || ToExtent == FromExtent) &&
IsLegalDataConversion<From, To>::value>;
// SFINAE check if Array can be converted to a span<T>.
template <typename Array, typename T, size_t Extent>
using EnableIfSpanCompatibleArray =
std::enable_if_t<(Extent == dynamic_extent ||
Extent == internal::Extent<Array>::value) &&
ContainerHasConvertibleData<Array, T>::value>;
// SFINAE check if Container can be converted to a span<T>.
template <typename Container, typename T>
using IsSpanCompatibleContainer =
std::conditional_t<!IsSpan<Container>::value &&
!IsStdArray<Container>::value &&
!IsCArray<Container>::value &&
ContainerHasConvertibleData<Container, T>::value &&
ContainerHasIntegralSize<Container>::value,
std::true_type,
std::false_type>;
template <typename Container, typename T>
using EnableIfSpanCompatibleContainer =
std::enable_if_t<IsSpanCompatibleContainer<Container, T>::value>;
template <typename Container, typename T, size_t Extent>
using EnableIfSpanCompatibleContainerAndSpanIsDynamic =
std::enable_if_t<IsSpanCompatibleContainer<Container, T>::value &&
Extent == dynamic_extent>;
// A helper template for storing the size of a span. Spans with static extents
// don't require additional storage, since the extent itself is specified in the
// template parameter.
template <size_t Extent>
class ExtentStorage {
public:
constexpr explicit ExtentStorage(size_t size) noexcept {}
constexpr size_t size() const noexcept { return Extent; }
};
// Specialization of ExtentStorage for dynamic extents, which do require
// explicit storage for the size.
template <>
struct ExtentStorage<dynamic_extent> {
constexpr explicit ExtentStorage(size_t size) noexcept : size_(size) {}
constexpr size_t size() const noexcept { return size_; }
private:
size_t size_;
};
} // namespace internal
// A span is a value type that represents an array of elements of type T. Since
// it only consists of a pointer to memory with an associated size, it is very
// light-weight. It is cheap to construct, copy, move and use spans, so that
// users are encouraged to use it as a pass-by-value parameter. A span does not
// own the underlying memory, so care must be taken to ensure that a span does
// not outlive the backing store.
//
// span is somewhat analogous to StringPiece, but with arbitrary element types,
// allowing mutation if T is non-const.
//
// span is implicitly convertible from C++ arrays, as well as most [1]
// container-like types that provide a data() and size() method (such as
// std::vector<T>). A mutable span<T> can also be implicitly converted to an
// immutable span<const T>.
//
// Consider using a span for functions that take a data pointer and size
// parameter: it allows the function to still act on an array-like type, while
// allowing the caller code to be a bit more concise.
//
// For read-only data access pass a span<const T>: the caller can supply either
// a span<const T> or a span<T>, while the callee will have a read-only view.
// For read-write access a mutable span<T> is required.
//
// Without span:
// Read-Only:
// // std::string HexEncode(const uint8_t* data, size_t size);
// std::vector<uint8_t> data_buffer = GenerateData();
// std::string r = HexEncode(data_buffer.data(), data_buffer.size());
//
// Mutable:
// // ssize_t SafeSNPrintf(char* buf, size_t N, const char* fmt, Args...);
// char str_buffer[100];
// SafeSNPrintf(str_buffer, sizeof(str_buffer), "Pi ~= %lf", 3.14);
//
// With span:
// Read-Only:
// // std::string HexEncode(base::span<const uint8_t> data);
// std::vector<uint8_t> data_buffer = GenerateData();
// std::string r = HexEncode(data_buffer);
//
// Mutable:
// // ssize_t SafeSNPrintf(base::span<char>, const char* fmt, Args...);
// char str_buffer[100];
// SafeSNPrintf(str_buffer, "Pi ~= %lf", 3.14);
//
// Spans with "const" and pointers
// -------------------------------
//
// Const and pointers can get confusing. Here are vectors of pointers and their
// corresponding spans:
//
// const std::vector<int*> => base::span<int* const>
// std::vector<const int*> => base::span<const int*>
// const std::vector<const int*> => base::span<const int* const>
//
// Differences from the C++20 draft
// --------------------------------
//
// http://eel.is/c++draft/views contains the latest C++20 draft of std::span.
// Chromium tries to follow the draft as close as possible. Differences between
// the draft and the implementation are documented in subsections below.
//
// Differences from [span.objectrep]:
// - as_bytes() and as_writable_bytes() return spans of uint8_t instead of
// std::byte (std::byte is a C++17 feature)
//
// Differences from [span.cons]:
// - Constructing a static span (i.e. Extent != dynamic_extent) from a dynamic
// sized container (e.g. std::vector) requires an explicit conversion (in the
// C++20 draft this is simply UB)
//
// Differences from [span.obs]:
// - empty() is marked with WARN_UNUSED_RESULT instead of [[nodiscard]]
// ([[nodiscard]] is a C++17 feature)
//
// Furthermore, all constructors and methods are marked noexcept due to the lack
// of exceptions in Chromium.
//
// Due to the lack of class template argument deduction guides in C++14
// appropriate make_span() utility functions are provided.
// [span], class template span
template <typename T, size_t Extent>
class span : public internal::ExtentStorage<Extent> {
private:
using ExtentStorage = internal::ExtentStorage<Extent>;
public:
using element_type = T;
using value_type = std::remove_cv_t<T>;
using size_type = size_t;
using difference_type = ptrdiff_t;
using pointer = T*;
using reference = T&;
using iterator = CheckedContiguousIterator<T>;
using const_iterator = CheckedContiguousConstIterator<T>;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
static constexpr size_t extent = Extent;
// [span.cons], span constructors, copy, assignment, and destructor
constexpr span() noexcept : ExtentStorage(0), data_(nullptr) {
static_assert(Extent == dynamic_extent || Extent == 0, "Invalid Extent");
}
constexpr span(T* data, size_t size) noexcept
: ExtentStorage(size), data_(data) {
CHECK(Extent == dynamic_extent || Extent == size);
}
// Artificially templatized to break ambiguity for span(ptr, 0).
template <typename = void>
constexpr span(T* begin, T* end) noexcept : span(begin, end - begin) {
// Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
CHECK(begin <= end);
}
template <
size_t N,
typename = internal::EnableIfSpanCompatibleArray<T (&)[N], T, Extent>>
constexpr span(T (&array)[N]) noexcept : span(base::data(array), N) {}
template <
size_t N,
typename = internal::
EnableIfSpanCompatibleArray<std::array<value_type, N>&, T, Extent>>
constexpr span(std::array<value_type, N>& array) noexcept
: span(base::data(array), N) {}
template <size_t N,
typename = internal::EnableIfSpanCompatibleArray<
const std::array<value_type, N>&,
T,
Extent>>
constexpr span(const std::array<value_type, N>& array) noexcept
: span(base::data(array), N) {}
// Conversion from a container that has compatible base::data() and integral
// base::size().
template <
typename Container,
typename =
internal::EnableIfSpanCompatibleContainerAndSpanIsDynamic<Container&,
T,
Extent>>
constexpr span(Container& container) noexcept
: span(base::data(container), base::size(container)) {}
template <
typename Container,
typename = internal::EnableIfSpanCompatibleContainerAndSpanIsDynamic<
const Container&,
T,
Extent>>
constexpr span(const Container& container) noexcept
: span(base::data(container), base::size(container)) {}
constexpr span(const span& other) noexcept = default;
// Conversions from spans of compatible types and extents: this allows a
// span<T> to be seamlessly used as a span<const T>, but not the other way
// around. If extent is not dynamic, OtherExtent has to be equal to Extent.
template <
typename U,
size_t OtherExtent,
typename =
internal::EnableIfLegalSpanConversion<U, OtherExtent, T, Extent>>
constexpr span(const span<U, OtherExtent>& other)
: span(other.data(), other.size()) {}
constexpr span& operator=(const span& other) noexcept = default;
~span() noexcept = default;
// [span.sub], span subviews
template <size_t Count>
constexpr span<T, Count> first() const noexcept {
static_assert(Extent == dynamic_extent || Count <= Extent,
"Count must not exceed Extent");
CHECK(Extent != dynamic_extent || Count <= size());
return {data(), Count};
}
template <size_t Count>
constexpr span<T, Count> last() const noexcept {
static_assert(Extent == dynamic_extent || Count <= Extent,
"Count must not exceed Extent");
CHECK(Extent != dynamic_extent || Count <= size());
return {data() + (size() - Count), Count};
}
template <size_t Offset, size_t Count = dynamic_extent>
constexpr span<T,
(Count != dynamic_extent
? Count
: (Extent != dynamic_extent ? Extent - Offset
: dynamic_extent))>
subspan() const noexcept {
static_assert(Extent == dynamic_extent || Offset <= Extent,
"Offset must not exceed Extent");
static_assert(Extent == dynamic_extent || Count == dynamic_extent ||
Count <= Extent - Offset,
"Count must not exceed Extent - Offset");
CHECK(Extent != dynamic_extent || Offset <= size());
CHECK(Extent != dynamic_extent || Count == dynamic_extent ||
Count <= size() - Offset);
return {data() + Offset, Count != dynamic_extent ? Count : size() - Offset};
}
constexpr span<T, dynamic_extent> first(size_t count) const noexcept {
// Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
CHECK(count <= size());
return {data(), count};
}
constexpr span<T, dynamic_extent> last(size_t count) const noexcept {
// Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
CHECK(count <= size());
return {data() + (size() - count), count};
}
constexpr span<T, dynamic_extent> subspan(size_t offset,
size_t count = dynamic_extent) const
noexcept {
// Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
CHECK(offset <= size());
CHECK(count == dynamic_extent || count <= size() - offset);
return {data() + offset, count != dynamic_extent ? count : size() - offset};
}
// [span.obs], span observers
constexpr size_t size() const noexcept { return ExtentStorage::size(); }
constexpr size_t size_bytes() const noexcept { return size() * sizeof(T); }
constexpr bool empty() const noexcept WARN_UNUSED_RESULT {
return size() == 0;
}
// [span.elem], span element access
constexpr T& operator[](size_t idx) const noexcept {
// Note: CHECK_LT is not constexpr, hence regular CHECK must be used.
CHECK(idx < size());
return *(data() + idx);
}
constexpr T& front() const noexcept {
static_assert(Extent == dynamic_extent || Extent > 0,
"Extent must not be 0");
CHECK(Extent != dynamic_extent || !empty());
return *data();
}
constexpr T& back() const noexcept {
static_assert(Extent == dynamic_extent || Extent > 0,
"Extent must not be 0");
CHECK(Extent != dynamic_extent || !empty());
return *(data() + size() - 1);
}
constexpr T* data() const noexcept { return data_; }
// [span.iter], span iterator support
constexpr iterator begin() const noexcept {
return iterator(data_, data_ + size());
}
constexpr iterator end() const noexcept {
return iterator(data_, data_ + size(), data_ + size());
}
constexpr const_iterator cbegin() const noexcept { return begin(); }
constexpr const_iterator cend() const noexcept { return end(); }
constexpr reverse_iterator rbegin() const noexcept {
return reverse_iterator(end());
}
constexpr reverse_iterator rend() const noexcept {
return reverse_iterator(begin());
}
constexpr const_reverse_iterator crbegin() const noexcept {
return const_reverse_iterator(cend());
}
constexpr const_reverse_iterator crend() const noexcept {
return const_reverse_iterator(cbegin());
}
private:
T* data_;
};
// span<T, Extent>::extent can not be declared inline prior to C++17, hence this
// definition is required.
template <class T, size_t Extent>
constexpr size_t span<T, Extent>::extent;
// [span.objectrep], views of object representation
template <typename T, size_t X>
span<const uint8_t, (X == dynamic_extent ? dynamic_extent : sizeof(T) * X)>
as_bytes(span<T, X> s) noexcept {
return {reinterpret_cast<const uint8_t*>(s.data()), s.size_bytes()};
}
template <typename T,
size_t X,
typename = std::enable_if_t<!std::is_const<T>::value>>
span<uint8_t, (X == dynamic_extent ? dynamic_extent : sizeof(T) * X)>
as_writable_bytes(span<T, X> s) noexcept {
return {reinterpret_cast<uint8_t*>(s.data()), s.size_bytes()};
}
// Type-deducing helpers for constructing a span.
template <int&... ExplicitArgumentBarrier, typename T>
constexpr span<T> make_span(T* data, size_t size) noexcept {
return {data, size};
}
template <int&... ExplicitArgumentBarrier, typename T>
constexpr span<T> make_span(T* begin, T* end) noexcept {
return {begin, end};
}
// make_span utility function that deduces both the span's value_type and extent
// from the passed in argument.
//
// Usage: auto span = base::make_span(...);
template <int&... ExplicitArgumentBarrier, typename Container>
constexpr auto make_span(Container&& container) noexcept {
using T =
std::remove_pointer_t<decltype(base::data(std::declval<Container>()))>;
using Extent = internal::Extent<Container>;
return span<T, Extent::value>(std::forward<Container>(container));
}
// make_span utility function that allows callers to explicit specify the span's
// extent, the value_type is deduced automatically. This is useful when passing
// a dynamically sized container to a method expecting static spans, when the
// container is known to have the correct size.
//
// Note: This will CHECK that N indeed matches size(container).
//
// Usage: auto static_span = base::make_span<N>(...);
template <size_t N, int&... ExplicitArgumentBarrier, typename Container>
constexpr auto make_span(Container&& container) noexcept {
using T =
std::remove_pointer_t<decltype(base::data(std::declval<Container>()))>;
return span<T, N>(base::data(container), base::size(container));
}
} // namespace base
// Note: std::tuple_size, std::tuple_element and std::get are specialized for
// static spans, so that they can be used in C++17's structured bindings. While
// we don't support C++17 yet, there is no harm in providing these
// specializations already.
namespace std {
// [span.tuple], tuple interface
#if defined(__clang__)
// Due to https://llvm.org/PR39871 and https://llvm.org/PR41331 and their
// respective fixes different versions of libc++ declare std::tuple_size and
// std::tuple_element either as classes or structs. In order to be able to
// specialize std::tuple_size and std::tuple_element for custom base types we
// thus need to disable -Wmismatched-tags in order to support all build
// configurations. Note that this is blessed by the standard in
// https://timsong-cpp.github.io/cppwp/n4140/dcl.type.elab#3.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wmismatched-tags"
#endif
template <typename T, size_t X>
struct tuple_size<base::span<T, X>> : public integral_constant<size_t, X> {};
template <typename T>
struct tuple_size<base::span<T, base::dynamic_extent>>; // not defined
template <size_t I, typename T, size_t X>
struct tuple_element<I, base::span<T, X>> {
static_assert(
base::dynamic_extent != X,
"std::tuple_element<> not supported for base::span<T, dynamic_extent>");
static_assert(I < X,
"Index out of bounds in std::tuple_element<> (base::span)");
using type = T;
};
#if defined(__clang__)
#pragma clang diagnostic pop // -Wmismatched-tags
#endif
template <size_t I, typename T, size_t X>
constexpr T& get(base::span<T, X> s) noexcept {
static_assert(base::dynamic_extent != X,
"std::get<> not supported for base::span<T, dynamic_extent>");
static_assert(I < X, "Index out of bounds in std::get<> (base::span)");
return s[I];
}
} // namespace std
#endif // BASE_CONTAINERS_SPAN_H_

View File

@ -13,7 +13,6 @@
#include <utility> #include <utility>
#include "base/stl_util.h" #include "base/stl_util.h"
#include "build/build_config.h"
#if defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX)) #if defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX))
#include "base/files/file_util.h" #include "base/files/file_util.h"
@ -28,6 +27,44 @@
namespace base { namespace base {
#if defined(ARCH_CPU_X86_FAMILY)
namespace internal {
std::tuple<int, int, int, int> ComputeX86FamilyAndModel(
const std::string& vendor,
int signature) {
int family = (signature >> 8) & 0xf;
int model = (signature >> 4) & 0xf;
int ext_family = 0;
int ext_model = 0;
// The "Intel 64 and IA-32 Architectures Developer's Manual: Vol. 2A"
// specifies the Extended Model is defined only when the Base Family is
// 06h or 0Fh.
// The "AMD CPUID Specification" specifies that the Extended Model is
// defined only when Base Family is 0Fh.
// Both manuals define the display model as
// {ExtendedModel[3:0],BaseModel[3:0]} in that case.
if (family == 0xf || (family == 0x6 && vendor == "GenuineIntel")) {
ext_model = (signature >> 16) & 0xf;
model += ext_model << 4;
}
// Both the "Intel 64 and IA-32 Architectures Developer's Manual: Vol. 2A"
// and the "AMD CPUID Specification" specify that the Extended Family is
// defined only when the Base Family is 0Fh.
// Both manuals define the display family as {0000b,BaseFamily[3:0]} +
// ExtendedFamily[7:0] in that case.
if (family == 0xf) {
ext_family = (signature >> 20) & 0xff;
family += ext_family;
}
return {family, model, ext_family, ext_model};
}
} // namespace internal
#endif // defined(ARCH_CPU_X86_FAMILY)
CPU::CPU() CPU::CPU()
: signature_(0), : signature_(0),
type_(0), type_(0),
@ -48,6 +85,7 @@ CPU::CPU()
has_avx2_(false), has_avx2_(false),
has_aesni_(false), has_aesni_(false),
has_non_stop_time_stamp_counter_(false), has_non_stop_time_stamp_counter_(false),
is_running_in_vm_(false),
cpu_vendor_("unknown") { cpu_vendor_("unknown") {
Initialize(); Initialize();
} }
@ -156,7 +194,6 @@ void CPU::Initialize() {
memcpy(cpu_string, &cpu_info[1], kVendorNameSize); memcpy(cpu_string, &cpu_info[1], kVendorNameSize);
cpu_string[kVendorNameSize] = '\0'; cpu_string[kVendorNameSize] = '\0';
cpu_vendor_ = cpu_string; cpu_vendor_ = cpu_string;
bool hypervisor = false;
// Interpret CPU feature information. // Interpret CPU feature information.
if (num_ids > 0) { if (num_ids > 0) {
@ -167,11 +204,9 @@ void CPU::Initialize() {
} }
signature_ = cpu_info[0]; signature_ = cpu_info[0];
stepping_ = cpu_info[0] & 0xf; stepping_ = cpu_info[0] & 0xf;
model_ = ((cpu_info[0] >> 4) & 0xf) + ((cpu_info[0] >> 12) & 0xf0);
family_ = (cpu_info[0] >> 8) & 0xf;
type_ = (cpu_info[0] >> 12) & 0x3; type_ = (cpu_info[0] >> 12) & 0x3;
ext_model_ = (cpu_info[0] >> 16) & 0xf; std::tie(family_, model_, ext_family_, ext_model_) =
ext_family_ = (cpu_info[0] >> 20) & 0xff; internal::ComputeX86FamilyAndModel(cpu_vendor_, signature_);
has_mmx_ = (cpu_info[3] & 0x00800000) != 0; has_mmx_ = (cpu_info[3] & 0x00800000) != 0;
has_sse_ = (cpu_info[3] & 0x02000000) != 0; has_sse_ = (cpu_info[3] & 0x02000000) != 0;
has_sse2_ = (cpu_info[3] & 0x04000000) != 0; has_sse2_ = (cpu_info[3] & 0x04000000) != 0;
@ -186,7 +221,7 @@ void CPU::Initialize() {
// This is checking for any hypervisor. Hypervisors may choose not to // This is checking for any hypervisor. Hypervisors may choose not to
// announce themselves. Hypervisors trap CPUID and sometimes return // announce themselves. Hypervisors trap CPUID and sometimes return
// different results to underlying hardware. // different results to underlying hardware.
hypervisor = (cpu_info[2] & 0x80000000) != 0; is_running_in_vm_ = (cpu_info[2] & 0x80000000) != 0;
// AVX instructions will generate an illegal instruction exception unless // AVX instructions will generate an illegal instruction exception unless
// a) they are supported by the CPU, // a) they are supported by the CPU,
@ -235,7 +270,7 @@ void CPU::Initialize() {
has_non_stop_time_stamp_counter_ = (cpu_info[3] & (1 << 8)) != 0; has_non_stop_time_stamp_counter_ = (cpu_info[3] & (1 << 8)) != 0;
} }
if (!has_non_stop_time_stamp_counter_ && hypervisor) { if (!has_non_stop_time_stamp_counter_ && is_running_in_vm_) {
int cpu_info_hv[4] = {}; int cpu_info_hv[4] = {};
__cpuid(cpu_info_hv, 0x40000000); __cpuid(cpu_info_hv, 0x40000000);
if (cpu_info_hv[1] == 0x7263694D && // Micr if (cpu_info_hv[1] == 0x7263694D && // Micr
@ -251,8 +286,14 @@ void CPU::Initialize() {
has_non_stop_time_stamp_counter_ = true; has_non_stop_time_stamp_counter_ = true;
} }
} }
#elif defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX)) #elif defined(ARCH_CPU_ARM_FAMILY)
#if (defined(OS_ANDROID) || defined(OS_LINUX))
cpu_brand_ = *CpuInfoBrand(); cpu_brand_ = *CpuInfoBrand();
#elif defined(OS_WIN)
// Windows makes high-resolution thread timing information available in
// user-space.
has_non_stop_time_stamp_counter_ = true;
#endif
#endif #endif
} }

View File

@ -6,11 +6,25 @@
#define BASE_CPU_H_ #define BASE_CPU_H_
#include <string> #include <string>
#include <tuple>
#include "base/base_export.h" #include "base/base_export.h"
#include "build/build_config.h"
namespace base { namespace base {
#if defined(ARCH_CPU_X86_FAMILY)
namespace internal {
// Compute the CPU family and model based on the vendor and CPUID signature.
// Returns in order: family, model, extended family, extended model.
BASE_EXPORT std::tuple<int, int, int, int> ComputeX86FamilyAndModel(
const std::string& vendor,
int signature);
} // namespace internal
#endif // defined(ARCH_CPU_X86_FAMILY)
// Query information about the processor. // Query information about the processor.
class BASE_EXPORT CPU final { class BASE_EXPORT CPU final {
public: public:
@ -52,6 +66,7 @@ class BASE_EXPORT CPU final {
bool has_non_stop_time_stamp_counter() const { bool has_non_stop_time_stamp_counter() const {
return has_non_stop_time_stamp_counter_; return has_non_stop_time_stamp_counter_;
} }
bool is_running_in_vm() const { return is_running_in_vm_; }
IntelMicroArchitecture GetIntelMicroArchitecture() const; IntelMicroArchitecture GetIntelMicroArchitecture() const;
const std::string& cpu_brand() const { return cpu_brand_; } const std::string& cpu_brand() const { return cpu_brand_; }
@ -79,6 +94,7 @@ class BASE_EXPORT CPU final {
bool has_avx2_; bool has_avx2_;
bool has_aesni_; bool has_aesni_;
bool has_non_stop_time_stamp_counter_; bool has_non_stop_time_stamp_counter_;
bool is_running_in_vm_;
std::string cpu_vendor_; std::string cpu_vendor_;
std::string cpu_brand_; std::string cpu_brand_;
}; };

View File

@ -0,0 +1,104 @@
// Copyright (c) 2012 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 BASE_DEBUG_CRASH_LOGGING_H_
#define BASE_DEBUG_CRASH_LOGGING_H_
#include <stddef.h>
#include <memory>
#include "base/base_export.h"
#include "base/macros.h"
#include "base/strings/string_piece.h"
namespace base {
namespace debug {
// A crash key is an annotation that is carried along with a crash report, to
// provide additional debugging information beyond a stack trace. Crash keys
// have a name and a string value.
//
// The preferred API is //components/crash/core/common:crash_key, however not
// all clients can hold a direct dependency on that target. The API provided
// in this file indirects the dependency.
//
// Example usage:
// static CrashKeyString* crash_key =
// AllocateCrashKeyString("name", CrashKeySize::Size32);
// SetCrashKeyString(crash_key, "value");
// ClearCrashKeyString(crash_key);
// The maximum length for a crash key's value must be one of the following
// pre-determined values.
enum class CrashKeySize {
Size32 = 32,
Size64 = 64,
Size256 = 256,
};
struct CrashKeyString;
// Allocates a new crash key with the specified |name| with storage for a
// value up to length |size|. This will return null if the crash key system is
// not initialized.
BASE_EXPORT CrashKeyString* AllocateCrashKeyString(const char name[],
CrashKeySize size);
// Stores |value| into the specified |crash_key|. The |crash_key| may be null
// if AllocateCrashKeyString() returned null. If |value| is longer than the
// size with which the key was allocated, it will be truncated.
BASE_EXPORT void SetCrashKeyString(CrashKeyString* crash_key,
base::StringPiece value);
// Clears any value that was stored in |crash_key|. The |crash_key| may be
// null.
BASE_EXPORT void ClearCrashKeyString(CrashKeyString* crash_key);
// A scoper that sets the specified key to value for the lifetime of the
// object, and clears it on destruction.
class BASE_EXPORT ScopedCrashKeyString {
public:
ScopedCrashKeyString(CrashKeyString* crash_key, base::StringPiece value);
~ScopedCrashKeyString();
private:
CrashKeyString* const crash_key_;
DISALLOW_COPY_AND_ASSIGN(ScopedCrashKeyString);
};
////////////////////////////////////////////////////////////////////////////////
// The following declarations are used to initialize the crash key system
// in //base by providing implementations for the above functions.
// The virtual interface that provides the implementation for the crash key
// API. This is implemented by a higher-layer component, and the instance is
// set using the function below.
class CrashKeyImplementation {
public:
virtual ~CrashKeyImplementation() = default;
virtual CrashKeyString* Allocate(const char name[], CrashKeySize size) = 0;
virtual void Set(CrashKeyString* crash_key, base::StringPiece value) = 0;
virtual void Clear(CrashKeyString* crash_key) = 0;
};
// Initializes the crash key system in base by replacing the existing
// implementation, if it exists, with |impl|. The |impl| is copied into base.
BASE_EXPORT void SetCrashKeyImplementation(
std::unique_ptr<CrashKeyImplementation> impl);
// The base structure for a crash key, storing the allocation metadata.
struct CrashKeyString {
constexpr CrashKeyString(const char name[], CrashKeySize size)
: name(name), size(size) {}
const char* const name;
const CrashKeySize size;
};
} // namespace debug
} // namespace base
#endif // BASE_DEBUG_CRASH_LOGGING_H_

View File

@ -38,6 +38,12 @@ BASE_EXPORT void BreakDebugger();
BASE_EXPORT void SetSuppressDebugUI(bool suppress); BASE_EXPORT void SetSuppressDebugUI(bool suppress);
BASE_EXPORT bool IsDebugUISuppressed(); BASE_EXPORT bool IsDebugUISuppressed();
// If a debugger is present, verifies that it is properly set up, and DCHECK()s
// if misconfigured. Currently only verifies that //tools/gdb/gdbinit has been
// sourced when using gdb on Linux and //tools/lldb/lldbinit.py has been sourced
// when using lldb on macOS.
BASE_EXPORT void VerifyDebugger();
} // namespace debug } // namespace debug
} // namespace base } // namespace base

View File

@ -19,29 +19,23 @@
#endif // defined(OS_WIN) #endif // defined(OS_WIN)
// TODO(peria): Enable profiling on Windows. // TODO(peria): Enable profiling on Windows.
#if BUILDFLAG(ENABLE_PROFILING) && !defined(NO_TCMALLOC) && !defined(OS_WIN) #if BUILDFLAG(ENABLE_PROFILING) && BUILDFLAG(USE_TCMALLOC) && !defined(OS_WIN)
#if BUILDFLAG(USE_NEW_TCMALLOC)
#include "third_party/tcmalloc/chromium/src/gperftools/profiler.h" #include "third_party/tcmalloc/chromium/src/gperftools/profiler.h"
#else
#include "third_party/tcmalloc/gperftools-2.0/chromium/src/gperftools/profiler.h"
#endif
#endif #endif
namespace base { namespace base {
namespace debug { namespace debug {
// TODO(peria): Enable profiling on Windows. // TODO(peria): Enable profiling on Windows.
#if BUILDFLAG(ENABLE_PROFILING) && !defined(NO_TCMALLOC) && !defined(OS_WIN) #if BUILDFLAG(ENABLE_PROFILING) && BUILDFLAG(USE_TCMALLOC) && !defined(OS_WIN)
static int profile_count = 0; static int profile_count = 0;
void StartProfiling(const std::string& name) { void StartProfiling(const std::string& name) {
++profile_count; ++profile_count;
std::string full_name(name); std::string full_name(name);
std::string pid = IntToString(GetCurrentProcId()); std::string pid = NumberToString(GetCurrentProcId());
std::string count = IntToString(profile_count); std::string count = NumberToString(profile_count);
ReplaceSubstringsAfterOffset(&full_name, 0, "{pid}", pid); ReplaceSubstringsAfterOffset(&full_name, 0, "{pid}", pid);
ReplaceSubstringsAfterOffset(&full_name, 0, "{count}", count); ReplaceSubstringsAfterOffset(&full_name, 0, "{count}", count);
ProfilerStart(full_name.c_str()); ProfilerStart(full_name.c_str());
@ -158,7 +152,7 @@ FunctionType FindFunctionInImports(const char* function_name) {
base::win::PEImage image(CURRENT_MODULE()); base::win::PEImage image(CURRENT_MODULE());
FunctionSearchContext ctx = { function_name, NULL }; FunctionSearchContext ctx = { function_name, NULL };
image.EnumImportChunks(FindResolutionFunctionInImports, &ctx); image.EnumImportChunks(FindResolutionFunctionInImports, &ctx, nullptr);
return reinterpret_cast<FunctionType>(ctx.function); return reinterpret_cast<FunctionType>(ctx.function);
} }

View File

@ -4,10 +4,6 @@
#include "base/environment.h" #include "base/environment.h"
#include <stddef.h>
#include <vector>
#include "base/memory/ptr_util.h" #include "base/memory/ptr_util.h"
#include "base/strings/string_piece.h" #include "base/strings/string_piece.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
@ -101,23 +97,6 @@ class EnvironmentImpl : public Environment {
} }
}; };
// Parses a null-terminated input string of an environment block. The key is
// placed into the given string, and the total length of the line, including
// the terminating null, is returned.
size_t ParseEnvLine(const NativeEnvironmentString::value_type* input,
NativeEnvironmentString* key) {
// Skip to the equals or end of the string, this is the key.
size_t cur = 0;
while (input[cur] && input[cur] != '=')
cur++;
*key = NativeEnvironmentString(&input[0], cur);
// Now just skip to the end of the string.
while (input[cur])
cur++;
return cur + 1;
}
} // namespace } // namespace
namespace env_vars { namespace env_vars {
@ -141,97 +120,4 @@ bool Environment::HasVar(StringPiece variable_name) {
return GetVar(variable_name, nullptr); return GetVar(variable_name, nullptr);
} }
#if defined(OS_WIN)
string16 AlterEnvironment(const wchar_t* env,
const EnvironmentMap& changes) {
string16 result;
// First copy all unmodified values to the output.
size_t cur_env = 0;
string16 key;
while (env[cur_env]) {
const wchar_t* line = &env[cur_env];
size_t line_length = ParseEnvLine(line, &key);
// Keep only values not specified in the change vector.
EnvironmentMap::const_iterator found_change = changes.find(key);
if (found_change == changes.end())
result.append(line, line_length);
cur_env += line_length;
}
// Now append all modified and new values.
for (EnvironmentMap::const_iterator i = changes.begin();
i != changes.end(); ++i) {
if (!i->second.empty()) {
result.append(i->first);
result.push_back('=');
result.append(i->second);
result.push_back(0);
}
}
// An additional null marks the end of the list. We always need a double-null
// in case nothing was added above.
if (result.empty())
result.push_back(0);
result.push_back(0);
return result;
}
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
std::unique_ptr<char* []> AlterEnvironment(const char* const* const env,
const EnvironmentMap& changes) {
std::string value_storage; // Holds concatenated null-terminated strings.
std::vector<size_t> result_indices; // Line indices into value_storage.
// First build up all of the unchanged environment strings. These are
// null-terminated of the form "key=value".
std::string key;
for (size_t i = 0; env[i]; i++) {
size_t line_length = ParseEnvLine(env[i], &key);
// Keep only values not specified in the change vector.
auto found_change = changes.find(key);
if (found_change == changes.end()) {
result_indices.push_back(value_storage.size());
value_storage.append(env[i], line_length);
}
}
// Now append all modified and new values.
for (const auto& i : changes) {
if (!i.second.empty()) {
result_indices.push_back(value_storage.size());
value_storage.append(i.first);
value_storage.push_back('=');
value_storage.append(i.second);
value_storage.push_back(0);
}
}
size_t pointer_count_required =
result_indices.size() + 1 + // Null-terminated array of pointers.
(value_storage.size() + sizeof(char*) - 1) / sizeof(char*); // Buffer.
std::unique_ptr<char* []> result(new char*[pointer_count_required]);
// The string storage goes after the array of pointers.
char* storage_data = reinterpret_cast<char*>(
&result.get()[result_indices.size() + 1]);
if (!value_storage.empty())
memcpy(storage_data, value_storage.data(), value_storage.size());
// Fill array of pointers at the beginning of the result.
for (size_t i = 0; i < result_indices.size(); i++)
result[i] = &storage_data[result_indices[i]];
result[result_indices.size()] = 0; // Null terminator.
return result;
}
#endif // OS_POSIX || OS_FUCHSIA
} // namespace base } // namespace base

View File

@ -38,52 +38,23 @@ class BASE_EXPORT Environment {
// Syntactic sugar for GetVar(variable_name, nullptr); // Syntactic sugar for GetVar(variable_name, nullptr);
virtual bool HasVar(StringPiece variable_name); virtual bool HasVar(StringPiece variable_name);
// Returns true on success, otherwise returns false. // Returns true on success, otherwise returns false. This method should not
// be called in a multi-threaded process.
virtual bool SetVar(StringPiece variable_name, virtual bool SetVar(StringPiece variable_name,
const std::string& new_value) = 0; const std::string& new_value) = 0;
// Returns true on success, otherwise returns false. // Returns true on success, otherwise returns false. This method should not
// be called in a multi-threaded process.
virtual bool UnSetVar(StringPiece variable_name) = 0; virtual bool UnSetVar(StringPiece variable_name) = 0;
}; };
#if defined(OS_WIN) #if defined(OS_WIN)
using NativeEnvironmentString = std::wstring;
typedef string16 NativeEnvironmentString;
typedef std::map<NativeEnvironmentString, NativeEnvironmentString>
EnvironmentMap;
// Returns a modified environment vector constructed from the given environment
// and the list of changes given in |changes|. Each key in the environment is
// matched against the first element of the pairs. In the event of a match, the
// value is replaced by the second of the pair, unless the second is empty, in
// which case the key-value is removed.
//
// This Windows version takes and returns a Windows-style environment block
// which is a concatenated list of null-terminated 16-bit strings. The end is
// marked by a double-null terminator. The size of the returned string will
// include the terminators.
BASE_EXPORT string16 AlterEnvironment(const wchar_t* env,
const EnvironmentMap& changes);
#elif defined(OS_POSIX) || defined(OS_FUCHSIA) #elif defined(OS_POSIX) || defined(OS_FUCHSIA)
using NativeEnvironmentString = std::string;
typedef std::string NativeEnvironmentString;
typedef std::map<NativeEnvironmentString, NativeEnvironmentString>
EnvironmentMap;
// See general comments for the Windows version above.
//
// This Posix version takes and returns a Posix-style environment block, which
// is a null-terminated list of pointers to null-terminated strings. The
// returned array will have appended to it the storage for the array itself so
// there is only one pointer to manage, but this means that you can't copy the
// array without keeping the original around.
BASE_EXPORT std::unique_ptr<char* []> AlterEnvironment(
const char* const* env,
const EnvironmentMap& changes);
#endif #endif
using EnvironmentMap =
std::map<NativeEnvironmentString, NativeEnvironmentString>;
} // namespace base } // namespace base

View File

@ -104,6 +104,7 @@
#include <stddef.h> #include <stddef.h>
#include <functional>
#include <iosfwd> #include <iosfwd>
#include <string> #include <string>
#include <vector> #include <vector>
@ -152,7 +153,7 @@ class BASE_EXPORT FilePath {
#if defined(OS_WIN) #if defined(OS_WIN)
// On Windows, for Unicode-aware applications, native pathnames are wchar_t // On Windows, for Unicode-aware applications, native pathnames are wchar_t
// arrays encoded in UTF-16. // arrays encoded in UTF-16.
typedef base::string16 StringType; typedef std::wstring StringType;
#elif defined(OS_POSIX) || defined(OS_FUCHSIA) #elif defined(OS_POSIX) || defined(OS_FUCHSIA)
// On most platforms, native pathnames are char arrays, and the encoding // On most platforms, native pathnames are char arrays, and the encoding
// may or may not be specified. On Mac OS X, native pathnames are encoded // may or may not be specified. On Mac OS X, native pathnames are encoded

View File

@ -0,0 +1,25 @@
// Copyright 2013 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.
#include <stddef.h>
#include "base/files/file_path.h"
#include "base/stl_util.h"
namespace base {
#if defined(FILE_PATH_USES_WIN_SEPARATORS)
const FilePath::CharType FilePath::kSeparators[] = FILE_PATH_LITERAL("\\/");
#else // FILE_PATH_USES_WIN_SEPARATORS
const FilePath::CharType FilePath::kSeparators[] = FILE_PATH_LITERAL("/");
#endif // FILE_PATH_USES_WIN_SEPARATORS
const size_t FilePath::kSeparatorsLength = base::size(kSeparators);
const FilePath::CharType FilePath::kCurrentDirectory[] = FILE_PATH_LITERAL(".");
const FilePath::CharType FilePath::kParentDirectory[] = FILE_PATH_LITERAL("..");
const FilePath::CharType FilePath::kExtensionSeparator = FILE_PATH_LITERAL('.');
} // namespace base

View File

@ -2,7 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "base/hash.h" #include "base/hash/hash.h"
#include "base/rand_util.h"
#include "base/third_party/cityhash/city.h"
#include "build/build_config.h"
// Definition in base/third_party/superfasthash/superfasthash.c. (Third-party // Definition in base/third_party/superfasthash/superfasthash.c. (Third-party
// code did not come with its own header file, so declaring the function here.) // code did not come with its own header file, so declaring the function here.)
@ -11,34 +15,18 @@ extern "C" uint32_t SuperFastHash(const char* data, int len);
namespace base { namespace base {
uint32_t Hash(const void* data, size_t length) { namespace {
// Currently our in-memory hash is the same as the persistent hash. The
// split between in-memory and persistent hash functions is maintained to
// allow the in-memory hash function to be updated in the future.
return PersistentHash(data, length);
}
uint32_t Hash(const std::string& str) { size_t FastHashImpl(base::span<const uint8_t> data) {
return PersistentHash(str.data(), str.size()); // We use the updated CityHash within our namespace (not the deprecated
} // version from third_party/smhasher).
#if defined(ARCH_CPU_64_BITS)
uint32_t Hash(const string16& str) { return base::internal::cityhash_v111::CityHash64(
return PersistentHash(str.data(), str.size() * sizeof(char16)); reinterpret_cast<const char*>(data.data()), data.size());
} #else
return base::internal::cityhash_v111::CityHash32(
uint32_t PersistentHash(const void* data, size_t length) { reinterpret_cast<const char*>(data.data()), data.size());
// This hash function must not change, since it is designed to be persistable #endif
// to disk.
if (length > static_cast<size_t>(std::numeric_limits<int>::max())) {
NOTREACHED();
return 0;
}
return ::SuperFastHash(reinterpret_cast<const char*>(data),
static_cast<int>(length));
}
uint32_t PersistentHash(const std::string& str) {
return PersistentHash(str.data(), str.size());
} }
// Implement hashing for pairs of at-most 32 bit integer values. // Implement hashing for pairs of at-most 32 bit integer values.
@ -50,7 +38,7 @@ uint32_t PersistentHash(const std::string& str) {
// h32(x32, y32) = (h64(x32, y32) * rand_odd64 + rand16 * 2^16) % 2^64 / 2^32 // h32(x32, y32) = (h64(x32, y32) * rand_odd64 + rand16 * 2^16) % 2^64 / 2^32
// //
// Contact danakj@chromium.org for any questions. // Contact danakj@chromium.org for any questions.
size_t HashInts32(uint32_t value1, uint32_t value2) { size_t HashInts32Impl(uint32_t value1, uint32_t value2) {
uint64_t value1_64 = value1; uint64_t value1_64 = value1;
uint64_t hash64 = (value1_64 << 32) | value2; uint64_t hash64 = (value1_64 << 32) | value2;
@ -71,7 +59,7 @@ size_t HashInts32(uint32_t value1, uint32_t value2) {
// breaking the two 64-bit inputs into 4 32-bit values: // breaking the two 64-bit inputs into 4 32-bit values:
// http://opendatastructures.org/versions/edition-0.1d/ods-java/node33.html#SECTION00832000000000000000 // http://opendatastructures.org/versions/edition-0.1d/ods-java/node33.html#SECTION00832000000000000000
// Then we reduce our result to 32 bits if required, similar to above. // Then we reduce our result to 32 bits if required, similar to above.
size_t HashInts64(uint64_t value1, uint64_t value2) { size_t HashInts64Impl(uint64_t value1, uint64_t value2) {
uint32_t short_random1 = 842304669U; uint32_t short_random1 = 842304669U;
uint32_t short_random2 = 619063811U; uint32_t short_random2 = 619063811U;
uint32_t short_random3 = 937041849U; uint32_t short_random3 = 937041849U;
@ -101,4 +89,79 @@ size_t HashInts64(uint64_t value1, uint64_t value2) {
return high_bits; return high_bits;
} }
// The random seed is used to perturb the output of base::FastHash() and
// base::HashInts() so that it is only deterministic within the lifetime of a
// process. This prevents inadvertent dependencies on the underlying
// implementation, e.g. anything that persists the hash value and expects it to
// be unchanging will break.
//
// Note: this is the same trick absl uses to generate a random seed. This is
// more robust than using base::RandBytes(), which can fail inside a sandboxed
// environment. Note that without ASLR, the seed won't be quite as random...
#if DCHECK_IS_ON()
constexpr const void* kSeed = &kSeed;
#endif
template <typename T>
T Scramble(T input) {
#if DCHECK_IS_ON()
return HashInts64Impl(input, reinterpret_cast<uintptr_t>(kSeed));
#else
return input;
#endif
}
} // namespace
size_t FastHash(base::span<const uint8_t> data) {
return Scramble(FastHashImpl(data));
}
uint32_t Hash(const void* data, size_t length) {
// Currently our in-memory hash is the same as the persistent hash. The
// split between in-memory and persistent hash functions is maintained to
// allow the in-memory hash function to be updated in the future.
return PersistentHash(data, length);
}
uint32_t Hash(const std::string& str) {
return PersistentHash(as_bytes(make_span(str)));
}
uint32_t Hash(const string16& str) {
return PersistentHash(as_bytes(make_span(str)));
}
uint32_t PersistentHash(span<const uint8_t> data) {
// This hash function must not change, since it is designed to be persistable
// to disk.
if (data.size() > static_cast<size_t>(std::numeric_limits<int>::max())) {
NOTREACHED();
return 0;
}
return ::SuperFastHash(reinterpret_cast<const char*>(data.data()),
static_cast<int>(data.size()));
}
uint32_t PersistentHash(const void* data, size_t length) {
return PersistentHash(make_span(static_cast<const uint8_t*>(data), length));
}
uint32_t PersistentHash(const std::string& str) {
return PersistentHash(str.data(), str.size());
}
size_t HashInts32(uint32_t value1, uint32_t value2) {
return Scramble(HashInts32Impl(value1, value2));
}
// Implement hashing for pairs of up-to 64-bit integer values.
// We use the compound integer hash method to produce a 64-bit hash code, by
// breaking the two 64-bit inputs into 4 32-bit values:
// http://opendatastructures.org/versions/edition-0.1d/ods-java/node33.html#SECTION00832000000000000000
// Then we reduce our result to 32 bits if required, similar to above.
size_t HashInts64(uint64_t value1, uint64_t value2) {
return Scramble(HashInts64Impl(value1, value2));
}
} // namespace base } // namespace base

View File

@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#ifndef BASE_HASH_H_ #ifndef BASE_HASH_HASH_H_
#define BASE_HASH_H_ #define BASE_HASH_HASH_H_
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
@ -13,26 +13,42 @@
#include <utility> #include <utility>
#include "base/base_export.h" #include "base/base_export.h"
#include "base/containers/span.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/strings/string16.h" #include "base/strings/string16.h"
#include "base/strings/string_piece.h"
namespace base { namespace base {
// Computes a hash of a memory buffer. This hash function is subject to change // WARNING: This hash functions should not be used for any cryptographic
// in the future, so use only for temporary in-memory structures. If you need // purpose.
// to persist a change on disk or between computers, use PersistentHash().
// // Deprecated: Computes a hash of a memory buffer, use FastHash() instead.
// WARNING: This hash function should not be used for any cryptographic purpose. // If you need to persist a change on disk or between computers, use
// PersistentHash().
// TODO(https://crbug.com/1025358): Migrate client code to new hash function.
BASE_EXPORT uint32_t Hash(const void* data, size_t length); BASE_EXPORT uint32_t Hash(const void* data, size_t length);
BASE_EXPORT uint32_t Hash(const std::string& str); BASE_EXPORT uint32_t Hash(const std::string& str);
BASE_EXPORT uint32_t Hash(const string16& str); BASE_EXPORT uint32_t Hash(const string16& str);
// Really *fast* and high quality hash.
// Recommended hash function for general use, we pick the best performant
// hash for each build target.
// It is prone to be updated whenever a newer/faster hash function is
// publicly available.
// May changed without warning, do not expect stability of outputs.
BASE_EXPORT size_t FastHash(base::span<const uint8_t> data);
inline size_t FastHash(StringPiece str) {
return FastHash(as_bytes(make_span(str)));
}
// Computes a hash of a memory buffer. This hash function must not change so // Computes a hash of a memory buffer. This hash function must not change so
// that code can use the hashed values for persistent storage purposes or // that code can use the hashed values for persistent storage purposes or
// sending across the network. If a new persistent hash function is desired, a // sending across the network. If a new persistent hash function is desired, a
// new version will have to be added in addition. // new version will have to be added in addition.
// //
// WARNING: This hash function should not be used for any cryptographic purpose. // WARNING: This hash function should not be used for any cryptographic purpose.
BASE_EXPORT uint32_t PersistentHash(base::span<const uint8_t> data);
BASE_EXPORT uint32_t PersistentHash(const void* data, size_t length); BASE_EXPORT uint32_t PersistentHash(const void* data, size_t length);
BASE_EXPORT uint32_t PersistentHash(const std::string& str); BASE_EXPORT uint32_t PersistentHash(const std::string& str);
@ -67,4 +83,4 @@ struct IntPairHash<std::pair<Type1, Type2>> {
} // namespace base } // namespace base
#endif // BASE_HASH_H_ #endif // BASE_HASH_HASH_H_

View File

@ -0,0 +1,168 @@
// Copyright 2019 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 BASE_IMMEDIATE_CRASH_H_
#define BASE_IMMEDIATE_CRASH_H_
#include "build/build_config.h"
// Crashes in the fastest possible way with no attempt at logging.
// There are several constraints; see http://crbug.com/664209 for more context.
//
// - TRAP_SEQUENCE_() must be fatal. It should not be possible to ignore the
// resulting exception or simply hit 'continue' to skip over it in a debugger.
// - Different instances of TRAP_SEQUENCE_() must not be folded together, to
// ensure crash reports are debuggable. Unlike __builtin_trap(), asm volatile
// blocks will not be folded together.
// Note: TRAP_SEQUENCE_() previously required an instruction with a unique
// nonce since unlike clang, GCC folds together identical asm volatile
// blocks.
// - TRAP_SEQUENCE_() must produce a signal that is distinct from an invalid
// memory access.
// - TRAP_SEQUENCE_() must be treated as a set of noreturn instructions.
// __builtin_unreachable() is used to provide that hint here. clang also uses
// this as a heuristic to pack the instructions in the function epilogue to
// improve code density.
//
// Additional properties that are nice to have:
// - TRAP_SEQUENCE_() should be as compact as possible.
// - The first instruction of TRAP_SEQUENCE_() should not change, to avoid
// shifting crash reporting clusters. As a consequence of this, explicit
// assembly is preferred over intrinsics.
// Note: this last bullet point may no longer be true, and may be removed in
// the future.
// Note: TRAP_SEQUENCE Is currently split into two macro helpers due to the fact
// that clang emits an actual instruction for __builtin_unreachable() on certain
// platforms (see https://crbug.com/958675). In addition, the int3/bkpt/brk will
// be removed in followups, so splitting it up like this now makes it easy to
// land the followups.
#if defined(COMPILER_GCC)
#if defined(OS_NACL)
// Crash report accuracy is not guaranteed on NaCl.
#define TRAP_SEQUENCE1_() __builtin_trap()
#define TRAP_SEQUENCE2_() asm volatile("")
#elif defined(ARCH_CPU_X86_FAMILY)
// TODO(https://crbug.com/958675): In theory, it should be possible to use just
// int3. However, there are a number of crashes with SIGILL as the exception
// code, so it seems likely that there's a signal handler that allows execution
// to continue after SIGTRAP.
#define TRAP_SEQUENCE1_() asm volatile("int3")
#if defined(OS_MACOSX)
// Intentionally empty: __builtin_unreachable() is always part of the sequence
// (see IMMEDIATE_CRASH below) and already emits a ud2 on Mac.
#define TRAP_SEQUENCE2_() asm volatile("")
#else
#define TRAP_SEQUENCE2_() asm volatile("ud2")
#endif // defined(OS_MACOSX)
#elif defined(ARCH_CPU_ARMEL)
// bkpt will generate a SIGBUS when running on armv7 and a SIGTRAP when running
// as a 32 bit userspace app on arm64. There doesn't seem to be any way to
// cause a SIGTRAP from userspace without using a syscall (which would be a
// problem for sandboxing).
// TODO(https://crbug.com/958675): Remove bkpt from this sequence.
#define TRAP_SEQUENCE1_() asm volatile("bkpt #0")
#define TRAP_SEQUENCE2_() asm volatile("udf #0")
#elif defined(ARCH_CPU_ARM64)
// This will always generate a SIGTRAP on arm64.
// TODO(https://crbug.com/958675): Remove brk from this sequence.
#define TRAP_SEQUENCE1_() asm volatile("brk #0")
#define TRAP_SEQUENCE2_() asm volatile("hlt #0")
#else
// Crash report accuracy will not be guaranteed on other architectures, but at
// least this will crash as expected.
#define TRAP_SEQUENCE1_() __builtin_trap()
#define TRAP_SEQUENCE2_() asm volatile("")
#endif // ARCH_CPU_*
#elif defined(COMPILER_MSVC)
#if !defined(__clang__)
// MSVC x64 doesn't support inline asm, so use the MSVC intrinsic.
#define TRAP_SEQUENCE1_() __debugbreak()
#define TRAP_SEQUENCE2_()
#elif defined(ARCH_CPU_ARM64)
// Windows ARM64 uses "BRK #F000" as its breakpoint instruction, and
// __debugbreak() generates that in both VC++ and clang.
#define TRAP_SEQUENCE1_() __debugbreak()
// Intentionally empty: __builtin_unreachable() is always part of the sequence
// (see IMMEDIATE_CRASH below) and already emits a ud2 on Win64,
// https://crbug.com/958373
#define TRAP_SEQUENCE2_() __asm volatile("")
#else
#define TRAP_SEQUENCE1_() asm volatile("int3")
#define TRAP_SEQUENCE2_() asm volatile("ud2")
#endif // __clang__
#else
#error No supported trap sequence!
#endif // COMPILER_GCC
#define TRAP_SEQUENCE_() \
do { \
TRAP_SEQUENCE1_(); \
TRAP_SEQUENCE2_(); \
} while (false)
// CHECK() and the trap sequence can be invoked from a constexpr function.
// This could make compilation fail on GCC, as it forbids directly using inline
// asm inside a constexpr function. However, it allows calling a lambda
// expression including the same asm.
// The side effect is that the top of the stacktrace will not point to the
// calling function, but to this anonymous lambda. This is still useful as the
// full name of the lambda will typically include the name of the function that
// calls CHECK() and the debugger will still break at the right line of code.
#if !defined(COMPILER_GCC)
#define WRAPPED_TRAP_SEQUENCE_() TRAP_SEQUENCE_()
#else
#define WRAPPED_TRAP_SEQUENCE_() \
do { \
[] { TRAP_SEQUENCE_(); }(); \
} while (false)
#endif // !defined(COMPILER_GCC)
#if defined(__clang__) || defined(COMPILER_GCC)
// __builtin_unreachable() hints to the compiler that this is noreturn and can
// be packed in the function epilogue.
#define IMMEDIATE_CRASH() \
({ \
WRAPPED_TRAP_SEQUENCE_(); \
__builtin_unreachable(); \
})
#else
// This is supporting non-chromium user of logging.h to build with MSVC, like
// pdfium. On MSVC there is no __builtin_unreachable().
#define IMMEDIATE_CRASH() WRAPPED_TRAP_SEQUENCE_()
#endif // defined(__clang__) || defined(COMPILER_GCC)
#endif // BASE_IMMEDIATE_CRASH_H_

View File

@ -43,7 +43,7 @@ Location::Location(const char* function_name,
std::string Location::ToString() const { std::string Location::ToString() const {
if (has_source_info()) { if (has_source_info()) {
return std::string(function_name_) + "@" + file_name_ + ":" + return std::string(function_name_) + "@" + file_name_ + ":" +
IntToString(line_number_); NumberToString(line_number_);
} }
return StringPrintf("pc:%p", program_counter_); return StringPrintf("pc:%p", program_counter_);
} }
@ -69,6 +69,25 @@ NOINLINE Location Location::CreateFromHere(const char* function_name,
return Location(function_name, file_name, line_number, RETURN_ADDRESS()); return Location(function_name, file_name, line_number, RETURN_ADDRESS());
} }
#if SUPPORTS_LOCATION_BUILTINS && BUILDFLAG(ENABLE_LOCATION_SOURCE)
// static
NOINLINE Location Location::Current(const char* function_name,
const char* file_name,
int line_number) {
return Location(function_name, file_name, line_number, RETURN_ADDRESS());
}
#elif SUPPORTS_LOCATION_BUILTINS
// static
NOINLINE Location Location::Current(const char* file_name) {
return Location(file_name, RETURN_ADDRESS());
}
#else
// static
NOINLINE Location Location::Current() {
return Location(nullptr, RETURN_ADDRESS());
}
#endif
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
NOINLINE const void* GetProgramCounter() { NOINLINE const void* GetProgramCounter() {
return RETURN_ADDRESS(); return RETURN_ADDRESS();

View File

@ -8,14 +8,29 @@
#include <stddef.h> #include <stddef.h>
#include <cassert> #include <cassert>
#include <functional>
#include <string> #include <string>
#include "base/base_export.h" #include "base/base_export.h"
#include "base/debug/debugging_buildflags.h" #include "base/debug/debugging_buildflags.h"
#include "base/hash.h" #include "base/hash/hash.h"
#include "build/build_config.h"
namespace base { namespace base {
#if defined(__has_builtin)
// Clang allows detection of these builtins.
#define SUPPORTS_LOCATION_BUILTINS \
(__has_builtin(__builtin_FUNCTION) && __has_builtin(__builtin_FILE) && \
__has_builtin(__builtin_LINE))
#elif defined(COMPILER_GCC) && __GNUC__ >= 7
// GCC has supported these for a long time, but they point at the function
// declaration in the case of default arguments, rather than at the call site.
#define SUPPORTS_LOCATION_BUILTINS 1
#else
#define SUPPORTS_LOCATION_BUILTINS 0
#endif
// Location provides basic info where of an object was constructed, or was // Location provides basic info where of an object was constructed, or was
// significantly brought to life. // significantly brought to life.
class BASE_EXPORT Location { class BASE_EXPORT Location {
@ -73,6 +88,16 @@ class BASE_EXPORT Location {
const char* file_name, const char* file_name,
int line_number); int line_number);
#if SUPPORTS_LOCATION_BUILTINS && BUILDFLAG(ENABLE_LOCATION_SOURCE)
static Location Current(const char* function_name = __builtin_FUNCTION(),
const char* file_name = __builtin_FILE(),
int line_number = __builtin_LINE());
#elif SUPPORTS_LOCATION_BUILTINS
static Location Current(const char* file_name = __builtin_FILE());
#else
static Location Current();
#endif
private: private:
const char* function_name_ = nullptr; const char* function_name_ = nullptr;
const char* file_name_ = nullptr; const char* file_name_ = nullptr;
@ -108,7 +133,7 @@ template <>
struct hash<::base::Location> { struct hash<::base::Location> {
std::size_t operator()(const ::base::Location& loc) const { std::size_t operator()(const ::base::Location& loc) const {
const void* program_counter = loc.program_counter(); const void* program_counter = loc.program_counter();
return base::Hash(&program_counter, sizeof(void*)); return base::FastHash(base::as_bytes(base::make_span(&program_counter, 1)));
} }
}; };

View File

@ -8,6 +8,7 @@
#include <stddef.h> #include <stddef.h>
#include <cassert> #include <cassert>
#include <cstdint>
#include <cstring> #include <cstring>
#include <sstream> #include <sstream>
#include <string> #include <string>
@ -17,13 +18,18 @@
#include "base/base_export.h" #include "base/base_export.h"
#include "base/callback_forward.h" #include "base/callback_forward.h"
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "base/debug/debugger.h" #include "base/immediate_crash.h"
#include "base/logging_buildflags.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/scoped_clear_last_error.h" #include "base/scoped_clear_last_error.h"
#include "base/strings/string_piece_forward.h" #include "base/strings/string_piece_forward.h"
#include "base/template_util.h" #include "base/template_util.h"
#include "build/build_config.h" #include "build/build_config.h"
#if defined(OS_CHROMEOS)
#include <cstdio>
#endif
// //
// Optional message capabilities // Optional message capabilities
// ----------------------------- // -----------------------------
@ -162,27 +168,35 @@ namespace logging {
// TODO(avi): do we want to do a unification of character types here? // TODO(avi): do we want to do a unification of character types here?
#if defined(OS_WIN) #if defined(OS_WIN)
typedef base::char16 PathChar; typedef wchar_t PathChar;
#elif defined(OS_POSIX) || defined(OS_FUCHSIA) #elif defined(OS_POSIX) || defined(OS_FUCHSIA)
typedef char PathChar; typedef char PathChar;
#endif #endif
// Where to record logging output? A flat file and/or system debug log // A bitmask of potential logging destinations.
// via OutputDebugString. using LoggingDestination = uint32_t;
enum LoggingDestination { // Specifies where logs will be written. Multiple destinations can be specified
// with bitwise OR.
// Unless destination is LOG_NONE, all logs with severity ERROR and above will
// be written to stderr in addition to the specified destination.
enum : uint32_t {
LOG_NONE = 0, LOG_NONE = 0,
LOG_TO_FILE = 1 << 0, LOG_TO_FILE = 1 << 0,
LOG_TO_SYSTEM_DEBUG_LOG = 1 << 1, LOG_TO_SYSTEM_DEBUG_LOG = 1 << 1,
LOG_TO_STDERR = 1 << 2,
LOG_TO_ALL = LOG_TO_FILE | LOG_TO_SYSTEM_DEBUG_LOG, LOG_TO_ALL = LOG_TO_FILE | LOG_TO_SYSTEM_DEBUG_LOG | LOG_TO_STDERR,
// On Windows, use a file next to the exe; on POSIX platforms, where // On Windows, use a file next to the exe.
// it may not even be possible to locate the executable on disk, use // On POSIX platforms, where it may not even be possible to locate the
// stderr. // executable on disk, use stderr.
#if defined(OS_WIN) // On Fuchsia, use the Fuchsia logging service.
LOG_DEFAULT = LOG_TO_FILE, #if defined(OS_FUCHSIA) || defined(OS_NACL)
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
LOG_DEFAULT = LOG_TO_SYSTEM_DEBUG_LOG, LOG_DEFAULT = LOG_TO_SYSTEM_DEBUG_LOG,
#elif defined(OS_WIN)
LOG_DEFAULT = LOG_TO_FILE,
#elif defined(OS_POSIX)
LOG_DEFAULT = LOG_TO_SYSTEM_DEBUG_LOG | LOG_TO_STDERR,
#endif #endif
}; };
@ -200,21 +214,22 @@ enum LogLockingState { LOCK_LOG_FILE, DONT_LOCK_LOG_FILE };
enum OldFileDeletionState { DELETE_OLD_LOG_FILE, APPEND_TO_OLD_LOG_FILE }; enum OldFileDeletionState { DELETE_OLD_LOG_FILE, APPEND_TO_OLD_LOG_FILE };
struct BASE_EXPORT LoggingSettings { struct BASE_EXPORT LoggingSettings {
// The defaults values are: // Equivalent to logging destination enum, but allows for multiple
// // destinations.
// logging_dest: LOG_DEFAULT uint32_t logging_dest = LOG_DEFAULT;
// log_file: NULL
// lock_log: LOCK_LOG_FILE
// delete_old: APPEND_TO_OLD_LOG_FILE
LoggingSettings();
LoggingDestination logging_dest; // The four settings below have an effect only when LOG_TO_FILE is
// The three settings below have an effect only when LOG_TO_FILE is
// set in |logging_dest|. // set in |logging_dest|.
const PathChar* log_file; const PathChar* log_file_path = nullptr;
LogLockingState lock_log; LogLockingState lock_log = LOCK_LOG_FILE;
OldFileDeletionState delete_old; OldFileDeletionState delete_old = APPEND_TO_OLD_LOG_FILE;
#if defined(OS_CHROMEOS)
// Contains an optional file that logs should be written to. If present,
// |log_file_path| will be ignored, and the logging system will take ownership
// of the FILE. If there's an error writing to this file, no fallback paths
// will be opened.
FILE* log_file = nullptr;
#endif
}; };
// Define different names for the BaseInitLoggingImpl() function depending on // Define different names for the BaseInitLoggingImpl() function depending on
@ -300,10 +315,10 @@ BASE_EXPORT void SetShowErrorDialogs(bool enable_dialogs);
// however clients can use this function to override with their own handling // however clients can use this function to override with their own handling
// (e.g. a silent one for Unit Tests) // (e.g. a silent one for Unit Tests)
using LogAssertHandlerFunction = using LogAssertHandlerFunction =
base::Callback<void(const char* file, base::RepeatingCallback<void(const char* file,
int line, int line,
const base::StringPiece message, const base::StringPiece message,
const base::StringPiece stack_trace)>; const base::StringPiece stack_trace)>;
class BASE_EXPORT ScopedLogAssertHandler { class BASE_EXPORT ScopedLogAssertHandler {
public: public:
@ -515,9 +530,9 @@ BASE_EXPORT extern std::ostream* g_swallow_stream;
class CheckOpResult { class CheckOpResult {
public: public:
// |message| must be non-null if and only if the check failed. // |message| must be non-null if and only if the check failed.
CheckOpResult(std::string* message) : message_(message) {} constexpr CheckOpResult(std::string* message) : message_(message) {}
// Returns true if the check succeeded. // Returns true if the check succeeded.
operator bool() const { return !message_; } constexpr operator bool() const { return !message_; }
// Returns the message. // Returns the message.
std::string* message() { return message_; } std::string* message() { return message_; }
@ -525,110 +540,6 @@ class CheckOpResult {
std::string* message_; std::string* message_;
}; };
// Crashes in the fastest possible way with no attempt at logging.
// There are different constraints to satisfy here, see http://crbug.com/664209
// for more context:
// - The trap instructions, and hence the PC value at crash time, have to be
// distinct and not get folded into the same opcode by the compiler.
// On Linux/Android this is tricky because GCC still folds identical
// asm volatile blocks. The workaround is generating distinct opcodes for
// each CHECK using the __COUNTER__ macro.
// - The debug info for the trap instruction has to be attributed to the source
// line that has the CHECK(), to make crash reports actionable. This rules
// out the ability of using a inline function, at least as long as clang
// doesn't support attribute(artificial).
// - Failed CHECKs should produce a signal that is distinguishable from an
// invalid memory access, to improve the actionability of crash reports.
// - The compiler should treat the CHECK as no-return instructions, so that the
// trap code can be efficiently packed in the prologue of the function and
// doesn't interfere with the main execution flow.
// - When debugging, developers shouldn't be able to accidentally step over a
// CHECK. This is achieved by putting opcodes that will cause a non
// continuable exception after the actual trap instruction.
// - Don't cause too much binary bloat.
#if defined(COMPILER_GCC)
#if defined(ARCH_CPU_X86_FAMILY) && !defined(OS_NACL)
// int 3 will generate a SIGTRAP.
#define TRAP_SEQUENCE() \
asm volatile( \
"int3; ud2; push %0;" ::"i"(static_cast<unsigned char>(__COUNTER__)))
#elif defined(ARCH_CPU_ARMEL) && !defined(OS_NACL)
// bkpt will generate a SIGBUS when running on armv7 and a SIGTRAP when running
// as a 32 bit userspace app on arm64. There doesn't seem to be any way to
// cause a SIGTRAP from userspace without using a syscall (which would be a
// problem for sandboxing).
#define TRAP_SEQUENCE() \
asm volatile("bkpt #0; udf %0;" ::"i"(__COUNTER__ % 256))
#elif defined(ARCH_CPU_ARM64) && !defined(OS_NACL)
// This will always generate a SIGTRAP on arm64.
#define TRAP_SEQUENCE() \
asm volatile("brk #0; hlt %0;" ::"i"(__COUNTER__ % 65536))
#else
// Crash report accuracy will not be guaranteed on other architectures, but at
// least this will crash as expected.
#define TRAP_SEQUENCE() __builtin_trap()
#endif // ARCH_CPU_*
#elif defined(COMPILER_MSVC)
// Clang is cleverer about coalescing int3s, so we need to add a unique-ish
// instruction following the __debugbreak() to have it emit distinct locations
// for CHECKs rather than collapsing them all together. It would be nice to use
// a short intrinsic to do this (and perhaps have only one implementation for
// both clang and MSVC), however clang-cl currently does not support intrinsics.
// On the flip side, MSVC x64 doesn't support inline asm. So, we have to have
// two implementations. Normally clang-cl's version will be 5 bytes (1 for
// `int3`, 2 for `ud2`, 2 for `push byte imm`, however, TODO(scottmg):
// https://crbug.com/694670 clang-cl doesn't currently support %'ing
// __COUNTER__, so eventually it will emit the dword form of push.
// TODO(scottmg): Reinvestigate a short sequence that will work on both
// compilers once clang supports more intrinsics. See https://crbug.com/693713.
#if !defined(__clang__)
#define TRAP_SEQUENCE() __debugbreak()
#elif defined(ARCH_CPU_ARM64)
#define TRAP_SEQUENCE() \
__asm volatile("brk #0\n hlt %0\n" ::"i"(__COUNTER__ % 65536));
#else
#define TRAP_SEQUENCE() ({ {__asm int 3 __asm ud2 __asm push __COUNTER__}; })
#endif // __clang__
#else
#error Port
#endif // COMPILER_GCC
// CHECK() and the trap sequence can be invoked from a constexpr function.
// This could make compilation fail on GCC, as it forbids directly using inline
// asm inside a constexpr function. However, it allows calling a lambda
// expression including the same asm.
// The side effect is that the top of the stacktrace will not point to the
// calling function, but to this anonymous lambda. This is still useful as the
// full name of the lambda will typically include the name of the function that
// calls CHECK() and the debugger will still break at the right line of code.
#if !defined(COMPILER_GCC)
#define WRAPPED_TRAP_SEQUENCE() TRAP_SEQUENCE()
#else
#define WRAPPED_TRAP_SEQUENCE() \
do { \
[] { TRAP_SEQUENCE(); }(); \
} while (false)
#endif
#if defined(__clang__) || defined(COMPILER_GCC)
#define IMMEDIATE_CRASH() \
({ \
WRAPPED_TRAP_SEQUENCE(); \
__builtin_unreachable(); \
})
#else
// This is supporting non-chromium user of logging.h to build with MSVC, like
// pdfium. On MSVC there is no __builtin_unreachable().
#define IMMEDIATE_CRASH() WRAPPED_TRAP_SEQUENCE()
#endif
// CHECK dies with a fatal error if condition is not true. It is *not* // CHECK dies with a fatal error if condition is not true. It is *not*
// controlled by NDEBUG, so the check will be executed regardless of // controlled by NDEBUG, so the check will be executed regardless of
// compilation mode. // compilation mode.
@ -659,26 +570,6 @@ class CheckOpResult {
#else // !(OFFICIAL_BUILD && NDEBUG) #else // !(OFFICIAL_BUILD && NDEBUG)
#if defined(_PREFAST_) && defined(OS_WIN)
// Use __analysis_assume to tell the VC++ static analysis engine that
// assert conditions are true, to suppress warnings. The LAZY_STREAM
// parameter doesn't reference 'condition' in /analyze builds because
// this evaluation confuses /analyze. The !! before condition is because
// __analysis_assume gets confused on some conditions:
// http://randomascii.wordpress.com/2011/09/13/analyze-for-visual-studio-the-ugly-part-5/
#define CHECK(condition) \
__analysis_assume(!!(condition)), \
LAZY_STREAM(LOG_STREAM(FATAL), false) \
<< "Check failed: " #condition ". "
#define PCHECK(condition) \
__analysis_assume(!!(condition)), \
LAZY_STREAM(PLOG_STREAM(FATAL), false) \
<< "Check failed: " #condition ". "
#else // _PREFAST_
// Do as much work as possible out of line to reduce inline code size. // Do as much work as possible out of line to reduce inline code size.
#define CHECK(condition) \ #define CHECK(condition) \
LAZY_STREAM(::logging::LogMessage(__FILE__, __LINE__, #condition).stream(), \ LAZY_STREAM(::logging::LogMessage(__FILE__, __LINE__, #condition).stream(), \
@ -688,8 +579,6 @@ class CheckOpResult {
LAZY_STREAM(PLOG_STREAM(FATAL), !ANALYZER_ASSUME_TRUE(condition)) \ LAZY_STREAM(PLOG_STREAM(FATAL), !ANALYZER_ASSUME_TRUE(condition)) \
<< "Check failed: " #condition ". " << "Check failed: " #condition ". "
#endif // _PREFAST_
// Helper macro for binary operators. // Helper macro for binary operators.
// Don't use this macro directly in your code, use CHECK_EQ et al below. // Don't use this macro directly in your code, use CHECK_EQ et al below.
// The 'switch' is used to prevent the 'else' from being ambiguous when the // The 'switch' is used to prevent the 'else' from being ambiguous when the
@ -718,6 +607,16 @@ MakeCheckOpValueString(std::ostream* os, const T& v) {
(*os) << v; (*os) << v;
} }
// Overload for types that no operator<< but do have .ToString() defined.
template <typename T>
inline typename std::enable_if<
!base::internal::SupportsOstreamOperator<const T&>::value &&
base::internal::SupportsToString<const T&>::value,
void>::type
MakeCheckOpValueString(std::ostream* os, const T& v) {
(*os) << v.ToString();
}
// Provide an overload for functions and function pointers. Function pointers // Provide an overload for functions and function pointers. Function pointers
// don't implicitly convert to void* but do implicitly convert to bool, so // don't implicitly convert to void* but do implicitly convert to bool, so
// without this function pointers are always printed as 1 or 0. (MSVC isn't // without this function pointers are always printed as 1 or 0. (MSVC isn't
@ -786,20 +685,21 @@ std::string* MakeCheckOpString<std::string, std::string>(
// The checked condition is wrapped with ANALYZER_ASSUME_TRUE, which under // The checked condition is wrapped with ANALYZER_ASSUME_TRUE, which under
// static analysis builds, blocks analysis of the current path if the // static analysis builds, blocks analysis of the current path if the
// condition is false. // condition is false.
#define DEFINE_CHECK_OP_IMPL(name, op) \ #define DEFINE_CHECK_OP_IMPL(name, op) \
template <class t1, class t2> \ template <class t1, class t2> \
inline std::string* Check##name##Impl(const t1& v1, const t2& v2, \ constexpr std::string* Check##name##Impl(const t1& v1, const t2& v2, \
const char* names) { \ const char* names) { \
if (ANALYZER_ASSUME_TRUE(v1 op v2)) \ if (ANALYZER_ASSUME_TRUE(v1 op v2)) \
return NULL; \ return nullptr; \
else \ else \
return ::logging::MakeCheckOpString(v1, v2, names); \ return ::logging::MakeCheckOpString(v1, v2, names); \
} \ } \
inline std::string* Check##name##Impl(int v1, int v2, const char* names) { \ constexpr std::string* Check##name##Impl(int v1, int v2, \
if (ANALYZER_ASSUME_TRUE(v1 op v2)) \ const char* names) { \
return NULL; \ if (ANALYZER_ASSUME_TRUE(v1 op v2)) \
else \ return nullptr; \
return ::logging::MakeCheckOpString(v1, v2, names); \ else \
return ::logging::MakeCheckOpString(v1, v2, names); \
} }
DEFINE_CHECK_OP_IMPL(EQ, ==) DEFINE_CHECK_OP_IMPL(EQ, ==)
DEFINE_CHECK_OP_IMPL(NE, !=) DEFINE_CHECK_OP_IMPL(NE, !=)
@ -817,9 +717,9 @@ DEFINE_CHECK_OP_IMPL(GT, > )
#define CHECK_GT(val1, val2) CHECK_OP(GT, > , val1, val2) #define CHECK_GT(val1, val2) CHECK_OP(GT, > , val1, val2)
#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON) #if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
#define DCHECK_IS_ON() 0 #define DCHECK_IS_ON() false
#else #else
#define DCHECK_IS_ON() 1 #define DCHECK_IS_ON() true
#endif #endif
// Definitions for DLOG et al. // Definitions for DLOG et al.
@ -885,21 +785,6 @@ const LogSeverity LOG_DCHECK = LOG_FATAL;
// DCHECK_IS_ON() is true. When DCHECK_IS_ON() is false, the macros use // DCHECK_IS_ON() is true. When DCHECK_IS_ON() is false, the macros use
// EAT_STREAM_PARAMETERS to avoid expressions that would create temporaries. // EAT_STREAM_PARAMETERS to avoid expressions that would create temporaries.
#if defined(_PREFAST_) && defined(OS_WIN)
// See comments on the previous use of __analysis_assume.
#define DCHECK(condition) \
__analysis_assume(!!(condition)), \
LAZY_STREAM(LOG_STREAM(DCHECK), false) \
<< "Check failed: " #condition ". "
#define DPCHECK(condition) \
__analysis_assume(!!(condition)), \
LAZY_STREAM(PLOG_STREAM(DCHECK), false) \
<< "Check failed: " #condition ". "
#else // !(defined(_PREFAST_) && defined(OS_WIN))
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
#define DCHECK(condition) \ #define DCHECK(condition) \
@ -916,8 +801,6 @@ const LogSeverity LOG_DCHECK = LOG_FATAL;
#endif // DCHECK_IS_ON() #endif // DCHECK_IS_ON()
#endif // defined(_PREFAST_) && defined(OS_WIN)
// Helper macro for binary operators. // Helper macro for binary operators.
// Don't use this macro directly in your code, use DCHECK_EQ et al below. // Don't use this macro directly in your code, use DCHECK_EQ et al below.
// The 'switch' is used to prevent the 'else' from being ambiguous when the // The 'switch' is used to prevent the 'else' from being ambiguous when the
@ -982,7 +865,7 @@ const LogSeverity LOG_DCHECK = LOG_FATAL;
#define DCHECK_GE(val1, val2) DCHECK_OP(GE, >=, val1, val2) #define DCHECK_GE(val1, val2) DCHECK_OP(GE, >=, val1, val2)
#define DCHECK_GT(val1, val2) DCHECK_OP(GT, > , val1, val2) #define DCHECK_GT(val1, val2) DCHECK_OP(GT, > , val1, val2)
#if !DCHECK_IS_ON() && defined(OS_CHROMEOS) #if BUILDFLAG(ENABLE_LOG_ERROR_NOT_REACHED)
// Implement logging of NOTREACHED() as a dedicated function to get function // Implement logging of NOTREACHED() as a dedicated function to get function
// call overhead down to a minimum. // call overhead down to a minimum.
void LogErrorNotReached(const char* file, int line); void LogErrorNotReached(const char* file, int line);
@ -1038,6 +921,7 @@ class BASE_EXPORT LogMessage {
// The file and line information passed in to the constructor. // The file and line information passed in to the constructor.
const char* file_; const char* file_;
const int line_; const int line_;
const char* file_basename_;
// This is useful since the LogMessage class uses a lot of Win32 calls // This is useful since the LogMessage class uses a lot of Win32 calls
// that will lose the value of GLE and the code that called the log function // that will lose the value of GLE and the code that called the log function
@ -1117,6 +1001,14 @@ class BASE_EXPORT ErrnoLogMessage {
// after this call. // after this call.
BASE_EXPORT void CloseLogFile(); BASE_EXPORT void CloseLogFile();
#if defined(OS_CHROMEOS)
// Returns a new file handle that will write to the same destination as the
// currently open log file. Returns nullptr if logging to a file is disabled,
// or if opening the file failed. This is intended to be used to initialize
// logging in child processes that are unable to open files.
BASE_EXPORT FILE* DuplicateLogFILE();
#endif
// Async signal safe logging mechanism. // Async signal safe logging mechanism.
BASE_EXPORT void RawLog(int level, const char* message); BASE_EXPORT void RawLog(int level, const char* message);
@ -1135,7 +1027,7 @@ BASE_EXPORT void RawLog(int level, const char* message);
BASE_EXPORT bool IsLoggingToFileEnabled(); BASE_EXPORT bool IsLoggingToFileEnabled();
// Returns the default log file path. // Returns the default log file path.
BASE_EXPORT base::string16 GetLogFileFullPath(); BASE_EXPORT std::wstring GetLogFileFullPath();
#endif #endif
} // namespace logging } // namespace logging
@ -1173,18 +1065,13 @@ inline std::ostream& operator<<(std::ostream& out, const std::wstring& wstr) {
#define NOTIMPLEMENTED_MSG "NOT IMPLEMENTED" #define NOTIMPLEMENTED_MSG "NOT IMPLEMENTED"
#endif #endif
#if defined(OS_ANDROID) && defined(OFFICIAL_BUILD) #define NOTIMPLEMENTED() DLOG(ERROR) << NOTIMPLEMENTED_MSG
#define NOTIMPLEMENTED() EAT_STREAM_PARAMETERS #define NOTIMPLEMENTED_LOG_ONCE() \
#define NOTIMPLEMENTED_LOG_ONCE() EAT_STREAM_PARAMETERS do { \
#else static bool logged_once = false; \
#define NOTIMPLEMENTED() LOG(ERROR) << NOTIMPLEMENTED_MSG DLOG_IF(ERROR, !logged_once) << NOTIMPLEMENTED_MSG; \
#define NOTIMPLEMENTED_LOG_ONCE() \ logged_once = true; \
do { \ } while (0); \
static bool logged_once = false; \
LOG_IF(ERROR, !logged_once) << NOTIMPLEMENTED_MSG; \
logged_once = true; \
} while (0); \
EAT_STREAM_PARAMETERS EAT_STREAM_PARAMETERS
#endif
#endif // BASE_LOGGING_H_ #endif // BASE_LOGGING_H_

View File

@ -10,6 +10,10 @@
#ifndef BASE_MACROS_H_ #ifndef BASE_MACROS_H_
#define BASE_MACROS_H_ #define BASE_MACROS_H_
// ALL DISALLOW_xxx MACROS ARE DEPRECATED; DO NOT USE IN NEW CODE.
// Use explicit deletions instead. See the section on copyability/movability in
// //styleguide/c++/c++-dos-and-donts.md for more information.
// Put this in the declarations for a class to be uncopyable. // Put this in the declarations for a class to be uncopyable.
#define DISALLOW_COPY(TypeName) \ #define DISALLOW_COPY(TypeName) \
TypeName(const TypeName&) = delete TypeName(const TypeName&) = delete

View File

@ -0,0 +1,62 @@
// Copyright 2018 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.
#include "base/memory/platform_shared_memory_region.h"
#include "base/memory/shared_memory_mapping.h"
#include "base/numerics/checked_math.h"
namespace base {
namespace subtle {
// static
PlatformSharedMemoryRegion PlatformSharedMemoryRegion::CreateWritable(
size_t size) {
return Create(Mode::kWritable, size);
}
// static
PlatformSharedMemoryRegion PlatformSharedMemoryRegion::CreateUnsafe(
size_t size) {
return Create(Mode::kUnsafe, size);
}
PlatformSharedMemoryRegion::PlatformSharedMemoryRegion() = default;
PlatformSharedMemoryRegion::PlatformSharedMemoryRegion(
PlatformSharedMemoryRegion&& other) = default;
PlatformSharedMemoryRegion& PlatformSharedMemoryRegion::operator=(
PlatformSharedMemoryRegion&& other) = default;
PlatformSharedMemoryRegion::~PlatformSharedMemoryRegion() = default;
PlatformSharedMemoryRegion::ScopedPlatformHandle
PlatformSharedMemoryRegion::PassPlatformHandle() {
return std::move(handle_);
}
bool PlatformSharedMemoryRegion::MapAt(off_t offset,
size_t size,
void** memory,
size_t* mapped_size) const {
if (!IsValid())
return false;
if (size == 0)
return false;
size_t end_byte;
if (!CheckAdd(offset, size).AssignIfValid(&end_byte) || end_byte > size_) {
return false;
}
bool success = MapAtInternal(offset, size, memory, mapped_size);
if (success) {
DCHECK_EQ(
0U, reinterpret_cast<uintptr_t>(*memory) & (kMapMinimumAlignment - 1));
}
return success;
}
} // namespace subtle
} // namespace base

View File

@ -0,0 +1,301 @@
// Copyright 2018 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 BASE_MEMORY_PLATFORM_SHARED_MEMORY_REGION_H_
#define BASE_MEMORY_PLATFORM_SHARED_MEMORY_REGION_H_
#include <utility>
#include "base/compiler_specific.h"
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/unguessable_token.h"
#include "build/build_config.h"
#if defined(OS_MACOSX) && !defined(OS_IOS)
#include <mach/mach.h>
#include "base/mac/scoped_mach_port.h"
#elif defined(OS_FUCHSIA)
#include <lib/zx/vmo.h>
#elif defined(OS_WIN)
#include "base/win/scoped_handle.h"
#include "base/win/windows_types.h"
#elif defined(OS_POSIX)
#include <sys/types.h>
#include "base/file_descriptor_posix.h"
#include "base/files/scoped_file.h"
#endif
#if defined(OS_LINUX)
namespace content {
class SandboxIPCHandler;
}
#endif
namespace base {
namespace subtle {
#if defined(OS_POSIX) && (!defined(OS_MACOSX) || defined(OS_IOS)) && \
!defined(OS_ANDROID)
// Helper structs to keep two descriptors on POSIX. It's needed to support
// ConvertToReadOnly().
struct BASE_EXPORT FDPair {
// The main shared memory descriptor that is used for mapping. May be either
// writable or read-only, depending on region's mode.
int fd;
// The read-only descriptor, valid only in kWritable mode. Replaces |fd| when
// a region is converted to read-only.
int readonly_fd;
};
struct BASE_EXPORT ScopedFDPair {
ScopedFDPair();
ScopedFDPair(ScopedFD in_fd, ScopedFD in_readonly_fd);
ScopedFDPair(ScopedFDPair&&);
ScopedFDPair& operator=(ScopedFDPair&&);
~ScopedFDPair();
FDPair get() const;
ScopedFD fd;
ScopedFD readonly_fd;
};
#endif
// Implementation class for shared memory regions.
//
// This class does the following:
//
// - Wraps and owns a shared memory region platform handle.
// - Provides a way to allocate a new region of platform shared memory of given
// size.
// - Provides a way to create mapping of the region in the current process'
// address space, under special access-control constraints (see Mode).
// - Provides methods to help transferring the handle across process boundaries.
// - Holds a 128-bit unique identifier used to uniquely identify the same
// kernel region resource across processes (used for memory tracking).
// - Has a method to retrieve the region's size in bytes.
//
// IMPORTANT NOTE: Users should never use this directly, but
// ReadOnlySharedMemoryRegion, WritableSharedMemoryRegion or
// UnsafeSharedMemoryRegion since this is an implementation class.
class BASE_EXPORT PlatformSharedMemoryRegion {
public:
// Permission mode of the platform handle. Each mode corresponds to one of the
// typed shared memory classes:
//
// * ReadOnlySharedMemoryRegion: A region that can only create read-only
// mappings.
//
// * WritableSharedMemoryRegion: A region that can only create writable
// mappings. The region can be demoted to ReadOnlySharedMemoryRegion without
// the possibility of promoting back to writable.
//
// * UnsafeSharedMemoryRegion: A region that can only create writable
// mappings. The region cannot be demoted to ReadOnlySharedMemoryRegion.
enum class Mode {
kReadOnly, // ReadOnlySharedMemoryRegion
kWritable, // WritableSharedMemoryRegion
kUnsafe, // UnsafeSharedMemoryRegion
kMaxValue = kUnsafe
};
// Errors that can occur during Shared Memory construction.
// These match tools/metrics/histograms/enums.xml.
// This enum is append-only.
enum class CreateError {
SUCCESS = 0,
SIZE_ZERO = 1,
SIZE_TOO_LARGE = 2,
INITIALIZE_ACL_FAILURE = 3,
INITIALIZE_SECURITY_DESC_FAILURE = 4,
SET_SECURITY_DESC_FAILURE = 5,
CREATE_FILE_MAPPING_FAILURE = 6,
REDUCE_PERMISSIONS_FAILURE = 7,
ALREADY_EXISTS = 8,
ALLOCATE_FILE_REGION_FAILURE = 9,
FSTAT_FAILURE = 10,
INODES_MISMATCH = 11,
GET_SHMEM_TEMP_DIR_FAILURE = 12,
kMaxValue = GET_SHMEM_TEMP_DIR_FAILURE
};
#if defined(OS_LINUX)
// Structure to limit access to executable region creation.
struct ExecutableRegion {
private:
// Creates a new shared memory region the unsafe mode (writable and not and
// convertible to read-only), and in addition marked executable. A ScopedFD
// to this region is returned. Any any mapping will have to be done
// manually, including setting executable permissions if necessary
//
// This is only used to support sandbox_ipc_linux.cc, and should not be used
// anywhere else in chrome. This is restricted via AllowCreateExecutable.
// TODO(crbug.com/982879): remove this when NaCl is unshipped.
//
// Returns an invalid ScopedFD if the call fails.
static ScopedFD CreateFD(size_t size);
friend class content::SandboxIPCHandler;
};
#endif
// Platform-specific shared memory type used by this class.
#if defined(OS_MACOSX) && !defined(OS_IOS)
using PlatformHandle = mach_port_t;
using ScopedPlatformHandle = mac::ScopedMachSendRight;
#elif defined(OS_FUCHSIA)
using PlatformHandle = zx::unowned_vmo;
using ScopedPlatformHandle = zx::vmo;
#elif defined(OS_WIN)
using PlatformHandle = HANDLE;
using ScopedPlatformHandle = win::ScopedHandle;
#elif defined(OS_ANDROID)
using PlatformHandle = int;
using ScopedPlatformHandle = ScopedFD;
#else
using PlatformHandle = FDPair;
using ScopedPlatformHandle = ScopedFDPair;
#endif
// The minimum alignment in bytes that any mapped address produced by Map()
// and MapAt() is guaranteed to have.
enum { kMapMinimumAlignment = 32 };
// Creates a new PlatformSharedMemoryRegion with corresponding mode and size.
// Creating in kReadOnly mode isn't supported because then there will be no
// way to modify memory content.
static PlatformSharedMemoryRegion CreateWritable(size_t size);
static PlatformSharedMemoryRegion CreateUnsafe(size_t size);
// Returns a new PlatformSharedMemoryRegion that takes ownership of the
// |handle|. All parameters must be taken from another valid
// PlatformSharedMemoryRegion instance, e.g. |size| must be equal to the
// actual region size as allocated by the kernel.
// Closes the |handle| and returns an invalid instance if passed parameters
// are invalid.
static PlatformSharedMemoryRegion Take(ScopedPlatformHandle handle,
Mode mode,
size_t size,
const UnguessableToken& guid);
#if defined(OS_POSIX) && !defined(OS_ANDROID) && \
!(defined(OS_MACOSX) && !defined(OS_IOS))
// Specialized version of Take() for POSIX that takes only one file descriptor
// instead of pair. Cannot be used with kWritable |mode|.
static PlatformSharedMemoryRegion Take(ScopedFD handle,
Mode mode,
size_t size,
const UnguessableToken& guid);
#endif
// Default constructor initializes an invalid instance, i.e. an instance that
// doesn't wrap any valid platform handle.
PlatformSharedMemoryRegion();
// Move operations are allowed.
PlatformSharedMemoryRegion(PlatformSharedMemoryRegion&&);
PlatformSharedMemoryRegion& operator=(PlatformSharedMemoryRegion&&);
// Destructor closes the platform handle. Does nothing if the handle is
// invalid.
~PlatformSharedMemoryRegion();
// Passes ownership of the platform handle to the caller. The current instance
// becomes invalid. It's the responsibility of the caller to close the
// handle. If the current instance is invalid, ScopedPlatformHandle will also
// be invalid.
ScopedPlatformHandle PassPlatformHandle() WARN_UNUSED_RESULT;
// Returns the platform handle. The current instance keeps ownership of this
// handle.
PlatformHandle GetPlatformHandle() const;
// Whether the platform handle is valid.
bool IsValid() const;
// Duplicates the platform handle and creates a new PlatformSharedMemoryRegion
// with the same |mode_|, |size_| and |guid_| that owns this handle. Returns
// invalid region on failure, the current instance remains valid.
// Can be called only in kReadOnly and kUnsafe modes, CHECK-fails if is
// called in kWritable mode.
PlatformSharedMemoryRegion Duplicate() const;
// Converts the region to read-only. Returns whether the operation succeeded.
// Makes the current instance invalid on failure. Can be called only in
// kWritable mode, all other modes will CHECK-fail. The object will have
// kReadOnly mode after this call on success.
bool ConvertToReadOnly();
#if defined(OS_MACOSX) && !defined(OS_IOS)
// Same as above, but |mapped_addr| is used as a hint to avoid additional
// mapping of the memory object.
// |mapped_addr| must be mapped location of |memory_object_|. If the location
// is unknown, |mapped_addr| should be |nullptr|.
bool ConvertToReadOnly(void* mapped_addr);
#endif // defined(OS_MACOSX) && !defined(OS_IOS)
// Converts the region to unsafe. Returns whether the operation succeeded.
// Makes the current instance invalid on failure. Can be called only in
// kWritable mode, all other modes will CHECK-fail. The object will have
// kUnsafe mode after this call on success.
bool ConvertToUnsafe();
// Maps |size| bytes of the shared memory region starting with the given
// |offset| into the caller's address space. |offset| must be aligned to value
// of |SysInfo::VMAllocationGranularity()|. Fails if requested bytes are out
// of the region limits.
// Returns true and sets |memory| and |mapped_size| on success, returns false
// and leaves output parameters in unspecified state otherwise. The mapped
// address is guaranteed to have an alignment of at least
// |kMapMinimumAlignment|.
bool MapAt(off_t offset,
size_t size,
void** memory,
size_t* mapped_size) const;
const UnguessableToken& GetGUID() const { return guid_; }
size_t GetSize() const { return size_; }
Mode GetMode() const { return mode_; }
private:
FRIEND_TEST_ALL_PREFIXES(PlatformSharedMemoryRegionTest,
CreateReadOnlyRegionDeathTest);
FRIEND_TEST_ALL_PREFIXES(PlatformSharedMemoryRegionTest,
CheckPlatformHandlePermissionsCorrespondToMode);
static PlatformSharedMemoryRegion Create(Mode mode,
size_t size
#if defined(OS_LINUX)
,
bool executable = false
#endif
);
static bool CheckPlatformHandlePermissionsCorrespondToMode(
PlatformHandle handle,
Mode mode,
size_t size);
PlatformSharedMemoryRegion(ScopedPlatformHandle handle,
Mode mode,
size_t size,
const UnguessableToken& guid);
bool MapAtInternal(off_t offset,
size_t size,
void** memory,
size_t* mapped_size) const;
ScopedPlatformHandle handle_;
Mode mode_ = Mode::kReadOnly;
size_t size_ = 0;
UnguessableToken guid_;
DISALLOW_COPY_AND_ASSIGN(PlatformSharedMemoryRegion);
};
} // namespace subtle
} // namespace base
#endif // BASE_MEMORY_PLATFORM_SHARED_MEMORY_REGION_H_

View File

@ -0,0 +1,343 @@
// Copyright 2018 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.
#include "base/memory/platform_shared_memory_region.h"
#include <aclapi.h>
#include <stddef.h>
#include <stdint.h>
#include "base/allocator/partition_allocator/page_allocator.h"
#include "base/bits.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/process/process_handle.h"
#include "base/rand_util.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/windows_version.h"
namespace base {
namespace subtle {
namespace {
// Emits UMA metrics about encountered errors. Pass zero (0) for |winerror|
// if there is no associated Windows error.
void LogError(PlatformSharedMemoryRegion::CreateError error, DWORD winerror) {
UMA_HISTOGRAM_ENUMERATION("SharedMemory.CreateError", error);
static_assert(ERROR_SUCCESS == 0, "Windows error code changed!");
if (winerror != ERROR_SUCCESS)
UmaHistogramSparse("SharedMemory.CreateWinError", winerror);
}
typedef enum _SECTION_INFORMATION_CLASS {
SectionBasicInformation,
} SECTION_INFORMATION_CLASS;
typedef struct _SECTION_BASIC_INFORMATION {
PVOID BaseAddress;
ULONG Attributes;
LARGE_INTEGER Size;
} SECTION_BASIC_INFORMATION, *PSECTION_BASIC_INFORMATION;
typedef ULONG(__stdcall* NtQuerySectionType)(
HANDLE SectionHandle,
SECTION_INFORMATION_CLASS SectionInformationClass,
PVOID SectionInformation,
ULONG SectionInformationLength,
PULONG ResultLength);
// Returns the length of the memory section starting at the supplied address.
size_t GetMemorySectionSize(void* address) {
MEMORY_BASIC_INFORMATION memory_info;
if (!::VirtualQuery(address, &memory_info, sizeof(memory_info)))
return 0;
return memory_info.RegionSize -
(static_cast<char*>(address) -
static_cast<char*>(memory_info.AllocationBase));
}
// Checks if the section object is safe to map. At the moment this just means
// it's not an image section.
bool IsSectionSafeToMap(HANDLE handle) {
static NtQuerySectionType nt_query_section_func =
reinterpret_cast<NtQuerySectionType>(
::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), "NtQuerySection"));
DCHECK(nt_query_section_func);
// The handle must have SECTION_QUERY access for this to succeed.
SECTION_BASIC_INFORMATION basic_information = {};
ULONG status =
nt_query_section_func(handle, SectionBasicInformation, &basic_information,
sizeof(basic_information), nullptr);
if (status)
return false;
return (basic_information.Attributes & SEC_IMAGE) != SEC_IMAGE;
}
// Returns a HANDLE on success and |nullptr| on failure.
// This function is similar to CreateFileMapping, but removes the permissions
// WRITE_DAC, WRITE_OWNER, READ_CONTROL, and DELETE.
//
// A newly created file mapping has two sets of permissions. It has access
// control permissions (WRITE_DAC, WRITE_OWNER, READ_CONTROL, and DELETE) and
// file permissions (FILE_MAP_READ, FILE_MAP_WRITE, etc.). The Chrome sandbox
// prevents HANDLEs with the WRITE_DAC permission from being duplicated into
// unprivileged processes.
//
// In order to remove the access control permissions, after being created the
// handle is duplicated with only the file access permissions.
HANDLE CreateFileMappingWithReducedPermissions(SECURITY_ATTRIBUTES* sa,
size_t rounded_size,
LPCWSTR name) {
HANDLE h = CreateFileMapping(INVALID_HANDLE_VALUE, sa, PAGE_READWRITE, 0,
static_cast<DWORD>(rounded_size), name);
if (!h) {
LogError(
PlatformSharedMemoryRegion::CreateError::CREATE_FILE_MAPPING_FAILURE,
GetLastError());
return nullptr;
}
HANDLE h2;
ProcessHandle process = GetCurrentProcess();
BOOL success = ::DuplicateHandle(
process, h, process, &h2, FILE_MAP_READ | FILE_MAP_WRITE | SECTION_QUERY,
FALSE, 0);
BOOL rv = ::CloseHandle(h);
DCHECK(rv);
if (!success) {
LogError(
PlatformSharedMemoryRegion::CreateError::REDUCE_PERMISSIONS_FAILURE,
GetLastError());
return nullptr;
}
return h2;
}
} // namespace
// static
PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Take(
win::ScopedHandle handle,
Mode mode,
size_t size,
const UnguessableToken& guid) {
if (!handle.IsValid())
return {};
if (size == 0)
return {};
if (size > static_cast<size_t>(std::numeric_limits<int>::max()))
return {};
if (!IsSectionSafeToMap(handle.Get()))
return {};
CHECK(
CheckPlatformHandlePermissionsCorrespondToMode(handle.Get(), mode, size));
return PlatformSharedMemoryRegion(std::move(handle), mode, size, guid);
}
HANDLE PlatformSharedMemoryRegion::GetPlatformHandle() const {
return handle_.Get();
}
bool PlatformSharedMemoryRegion::IsValid() const {
return handle_.IsValid();
}
PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Duplicate() const {
if (!IsValid())
return {};
CHECK_NE(mode_, Mode::kWritable)
<< "Duplicating a writable shared memory region is prohibited";
HANDLE duped_handle;
ProcessHandle process = GetCurrentProcess();
BOOL success =
::DuplicateHandle(process, handle_.Get(), process, &duped_handle, 0,
FALSE, DUPLICATE_SAME_ACCESS);
if (!success)
return {};
return PlatformSharedMemoryRegion(win::ScopedHandle(duped_handle), mode_,
size_, guid_);
}
bool PlatformSharedMemoryRegion::ConvertToReadOnly() {
if (!IsValid())
return false;
CHECK_EQ(mode_, Mode::kWritable)
<< "Only writable shared memory region can be converted to read-only";
win::ScopedHandle handle_copy(handle_.Take());
HANDLE duped_handle;
ProcessHandle process = GetCurrentProcess();
BOOL success =
::DuplicateHandle(process, handle_copy.Get(), process, &duped_handle,
FILE_MAP_READ | SECTION_QUERY, FALSE, 0);
if (!success)
return false;
handle_.Set(duped_handle);
mode_ = Mode::kReadOnly;
return true;
}
bool PlatformSharedMemoryRegion::ConvertToUnsafe() {
if (!IsValid())
return false;
CHECK_EQ(mode_, Mode::kWritable)
<< "Only writable shared memory region can be converted to unsafe";
mode_ = Mode::kUnsafe;
return true;
}
bool PlatformSharedMemoryRegion::MapAtInternal(off_t offset,
size_t size,
void** memory,
size_t* mapped_size) const {
bool write_allowed = mode_ != Mode::kReadOnly;
// Try to map the shared memory. On the first failure, release any reserved
// address space for a single entry.
for (int i = 0; i < 2; ++i) {
*memory = MapViewOfFile(
handle_.Get(), FILE_MAP_READ | (write_allowed ? FILE_MAP_WRITE : 0),
static_cast<uint64_t>(offset) >> 32, static_cast<DWORD>(offset), size);
if (*memory)
break;
ReleaseReservation();
}
if (!*memory) {
DPLOG(ERROR) << "Failed executing MapViewOfFile";
return false;
}
*mapped_size = GetMemorySectionSize(*memory);
return true;
}
// static
PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Create(Mode mode,
size_t size) {
// TODO(crbug.com/210609): NaCl forces us to round up 64k here, wasting 32k
// per mapping on average.
static const size_t kSectionSize = 65536;
if (size == 0) {
LogError(CreateError::SIZE_ZERO, 0);
return {};
}
// Aligning may overflow so check that the result doesn't decrease.
size_t rounded_size = bits::Align(size, kSectionSize);
if (rounded_size < size ||
rounded_size > static_cast<size_t>(std::numeric_limits<int>::max())) {
LogError(CreateError::SIZE_TOO_LARGE, 0);
return {};
}
CHECK_NE(mode, Mode::kReadOnly) << "Creating a region in read-only mode will "
"lead to this region being non-modifiable";
// Add an empty DACL to enforce anonymous read-only sections.
ACL dacl;
SECURITY_DESCRIPTOR sd;
if (!InitializeAcl(&dacl, sizeof(dacl), ACL_REVISION)) {
LogError(CreateError::INITIALIZE_ACL_FAILURE, GetLastError());
return {};
}
if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
LogError(CreateError::INITIALIZE_SECURITY_DESC_FAILURE, GetLastError());
return {};
}
if (!SetSecurityDescriptorDacl(&sd, TRUE, &dacl, FALSE)) {
LogError(CreateError::SET_SECURITY_DESC_FAILURE, GetLastError());
return {};
}
string16 name;
if (win::GetVersion() < win::Version::WIN8_1) {
// Windows < 8.1 ignores DACLs on certain unnamed objects (like shared
// sections). So, we generate a random name when we need to enforce
// read-only.
uint64_t rand_values[4];
RandBytes(&rand_values, sizeof(rand_values));
name = ASCIIToUTF16(StringPrintf("CrSharedMem_%016llx%016llx%016llx%016llx",
rand_values[0], rand_values[1],
rand_values[2], rand_values[3]));
DCHECK(!name.empty());
}
SECURITY_ATTRIBUTES sa = {sizeof(sa), &sd, FALSE};
// Ask for the file mapping with reduced permisions to avoid passing the
// access control permissions granted by default into unpriviledged process.
HANDLE h = CreateFileMappingWithReducedPermissions(
&sa, rounded_size, name.empty() ? nullptr : as_wcstr(name));
if (h == nullptr) {
// The error is logged within CreateFileMappingWithReducedPermissions().
return {};
}
win::ScopedHandle scoped_h(h);
// Check if the shared memory pre-exists.
if (GetLastError() == ERROR_ALREADY_EXISTS) {
LogError(CreateError::ALREADY_EXISTS, ERROR_ALREADY_EXISTS);
return {};
}
LogError(CreateError::SUCCESS, ERROR_SUCCESS);
return PlatformSharedMemoryRegion(std::move(scoped_h), mode, size,
UnguessableToken::Create());
}
// static
bool PlatformSharedMemoryRegion::CheckPlatformHandlePermissionsCorrespondToMode(
PlatformHandle handle,
Mode mode,
size_t size) {
// Call ::DuplicateHandle() with FILE_MAP_WRITE as a desired access to check
// if the |handle| has a write access.
ProcessHandle process = GetCurrentProcess();
HANDLE duped_handle;
BOOL success = ::DuplicateHandle(process, handle, process, &duped_handle,
FILE_MAP_WRITE, FALSE, 0);
if (success) {
BOOL rv = ::CloseHandle(duped_handle);
DCHECK(rv);
}
bool is_read_only = !success;
bool expected_read_only = mode == Mode::kReadOnly;
if (is_read_only != expected_read_only) {
DLOG(ERROR) << "File mapping handle has wrong access rights: it is"
<< (is_read_only ? " " : " not ") << "read-only but it should"
<< (expected_read_only ? " " : " not ") << "be";
return false;
}
return true;
}
PlatformSharedMemoryRegion::PlatformSharedMemoryRegion(
win::ScopedHandle handle,
Mode mode,
size_t size,
const UnguessableToken& guid)
: handle_(std::move(handle)), mode_(mode), size_(size), guid_(guid) {}
} // namespace subtle
} // namespace base

View File

@ -4,6 +4,9 @@
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include <limits>
#include <type_traits>
#include "base/threading/thread_collision_warner.h" #include "base/threading/thread_collision_warner.h"
namespace base { namespace base {
@ -32,15 +35,35 @@ RefCountedThreadSafeBase::~RefCountedThreadSafeBase() {
} }
#endif #endif
// This is a security check. In 32-bit-archs, an attacker would run out of // For security and correctness, we check the arithmetic on ref counts.
// address space after allocating at most 2^32 scoped_refptrs. This replicates //
// that boundary for 64-bit-archs. // In an attempt to avoid binary bloat (from inlining the `CHECK`), we define
// these functions out-of-line. However, compilers are wily. Further testing may
// show that `NOINLINE` helps or hurts.
//
#if defined(ARCH_CPU_64_BITS) #if defined(ARCH_CPU_64_BITS)
void RefCountedBase::AddRefImpl() const { void RefCountedBase::AddRefImpl() const {
// Check if |ref_count_| overflow only on 64 bit archs since the number of // An attacker could induce use-after-free bugs, and potentially exploit them,
// objects may exceed 2^32. // by creating so many references to a ref-counted object that the reference
// To avoid the binary size bloat, use non-inline function here. // count overflows. On 32-bit architectures, there is not enough address space
CHECK(++ref_count_ > 0); // to succeed. But on 64-bit architectures, it might indeed be possible.
// Therefore, we can elide the check for arithmetic overflow on 32-bit, but we
// must check on 64-bit.
//
// Make sure the addition didn't wrap back around to 0. This form of check
// works because we assert that `ref_count_` is an unsigned integer type.
CHECK(++ref_count_ != 0);
}
void RefCountedBase::ReleaseImpl() const {
// Make sure the subtraction didn't wrap back around from 0 to the max value.
// That could cause memory leaks, and may induce application-semantic
// correctness or safety bugs. (E.g. what if we really needed that object to
// be destroyed at the right time?)
//
// Note that unlike with overflow, underflow could also happen on 32-bit
// architectures. Arguably, we should do this check on32-bit machines too.
CHECK(--ref_count_ != std::numeric_limits<decltype(ref_count_)>::max());
} }
#endif #endif

View File

@ -69,7 +69,7 @@ class BASE_EXPORT RefCountedBase {
// Returns true if the object should self-delete. // Returns true if the object should self-delete.
bool Release() const { bool Release() const {
--ref_count_; ReleaseImpl();
// TODO(maruel): Add back once it doesn't assert 500 times/sec. // TODO(maruel): Add back once it doesn't assert 500 times/sec.
// Current thread books the critical section "AddRelease" // Current thread books the critical section "AddRelease"
@ -126,8 +126,10 @@ class BASE_EXPORT RefCountedBase {
#if defined(ARCH_CPU_64_BITS) #if defined(ARCH_CPU_64_BITS)
void AddRefImpl() const; void AddRefImpl() const;
void ReleaseImpl() const;
#else #else
void AddRefImpl() const { ++ref_count_; } void AddRefImpl() const { ++ref_count_; }
void ReleaseImpl() const { --ref_count_; }
#endif #endif
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
@ -135,6 +137,8 @@ class BASE_EXPORT RefCountedBase {
#endif #endif
mutable uint32_t ref_count_ = 0; mutable uint32_t ref_count_ = 0;
static_assert(std::is_unsigned<decltype(ref_count_)>::value,
"ref_count_ must be an unsigned type.");
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
mutable bool needs_adopt_ref_ = false; mutable bool needs_adopt_ref_ = false;
@ -444,6 +448,16 @@ class RefCountedData
~RefCountedData() = default; ~RefCountedData() = default;
}; };
template <typename T>
bool operator==(const RefCountedData<T>& lhs, const RefCountedData<T>& rhs) {
return lhs.data == rhs.data;
}
template <typename T>
bool operator!=(const RefCountedData<T>& lhs, const RefCountedData<T>& rhs) {
return !(lhs == rhs);
}
} // namespace base } // namespace base
#endif // BASE_MEMORY_REF_COUNTED_H_ #endif // BASE_MEMORY_REF_COUNTED_H_

View File

@ -25,10 +25,17 @@ class RefCounted;
template <class, typename> template <class, typename>
class RefCountedThreadSafe; class RefCountedThreadSafe;
class SequencedTaskRunner; class SequencedTaskRunner;
class WrappedPromise;
template <typename T> template <typename T>
scoped_refptr<T> AdoptRef(T* t); scoped_refptr<T> AdoptRef(T* t);
namespace internal {
class BasePromise;
} // namespace internal
namespace subtle { namespace subtle {
enum AdoptRefTag { kAdoptRefTag }; enum AdoptRefTag { kAdoptRefTag };
@ -171,8 +178,16 @@ class scoped_refptr {
constexpr scoped_refptr() = default; constexpr scoped_refptr() = default;
// Constructs from raw pointer. constexpr if |p| is null. // Allow implicit construction from nullptr.
constexpr scoped_refptr(T* p) : ptr_(p) { constexpr scoped_refptr(std::nullptr_t) {}
// Constructs from a raw pointer. Note that this constructor allows implicit
// conversion from T* to scoped_refptr<T> which is strongly discouraged. If
// you are creating a new ref-counted object please use
// base::MakeRefCounted<T>() or base::WrapRefCounted<T>(). Otherwise you
// should move or copy construct from an existing scoped_refptr<T> to the
// ref-counted object.
scoped_refptr(T* p) : ptr_(p) {
if (ptr_) if (ptr_)
AddRef(ptr_); AddRef(ptr_);
} }
@ -221,6 +236,11 @@ class scoped_refptr {
return ptr_; return ptr_;
} }
scoped_refptr& operator=(std::nullptr_t) {
reset();
return *this;
}
scoped_refptr& operator=(T* p) { return *this = scoped_refptr(p); } scoped_refptr& operator=(T* p) { return *this = scoped_refptr(p); }
// Unified assignment operator. // Unified assignment operator.
@ -260,6 +280,11 @@ class scoped_refptr {
friend scoped_refptr<U> base::AdoptRef(U*); friend scoped_refptr<U> base::AdoptRef(U*);
friend class ::base::SequencedTaskRunner; friend class ::base::SequencedTaskRunner;
// Friend access so these classes can use the constructor below as part of a
// binary size optimization.
friend class ::base::internal::BasePromise;
friend class ::base::WrappedPromise;
// Returns the owned pointer (if any), releasing ownership to the caller. The // Returns the owned pointer (if any), releasing ownership to the caller. The
// caller is responsible for managing the lifetime of the reference. // caller is responsible for managing the lifetime of the reference.
T* release(); T* release();

View File

@ -1,245 +0,0 @@
// Copyright (c) 2012 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 BASE_MEMORY_SHARED_MEMORY_H_
#define BASE_MEMORY_SHARED_MEMORY_H_
#include <stddef.h>
#include <string>
#include "base/base_export.h"
#include "base/hash.h"
#include "base/macros.h"
#include "base/memory/shared_memory_handle.h"
#include "base/process/process_handle.h"
#include "base/strings/string16.h"
#include "build/build_config.h"
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
#include <stdio.h>
#include <sys/types.h>
#include <semaphore.h>
#include "base/file_descriptor_posix.h"
#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#endif
#if defined(OS_WIN)
#include "base/win/scoped_handle.h"
#endif
namespace base {
class FilePath;
// Options for creating a shared memory object.
struct BASE_EXPORT SharedMemoryCreateOptions {
#if !defined(OS_FUCHSIA)
// DEPRECATED (crbug.com/345734):
// If NULL, the object is anonymous. This pointer is owned by the caller
// and must live through the call to Create().
const std::string* name_deprecated = nullptr;
// DEPRECATED (crbug.com/345734):
// If true, and the shared memory already exists, Create() will open the
// existing shared memory and ignore the size parameter. If false,
// shared memory must not exist. This flag is meaningless unless
// name_deprecated is non-NULL.
bool open_existing_deprecated = false;
#endif
// Size of the shared memory object to be created.
// When opening an existing object, this has no effect.
size_t size = 0;
// If true, mappings might need to be made executable later.
bool executable = false;
// If true, the file can be shared read-only to a process.
bool share_read_only = false;
};
// Platform abstraction for shared memory.
// SharedMemory consumes a SharedMemoryHandle [potentially one that it created]
// to map a shared memory OS resource into the virtual address space of the
// current process.
class BASE_EXPORT SharedMemory {
public:
SharedMemory();
#if defined(OS_WIN)
// Similar to the default constructor, except that this allows for
// calling LockDeprecated() to acquire the named mutex before either Create or
// Open are called on Windows.
explicit SharedMemory(const string16& name);
#endif
// Create a new SharedMemory object from an existing, open
// shared memory file.
//
// WARNING: This does not reduce the OS-level permissions on the handle; it
// only affects how the SharedMemory will be mmapped. Use
// GetReadOnlyHandle to drop permissions. TODO(jln,jyasskin): DCHECK
// that |read_only| matches the permissions of the handle.
SharedMemory(const SharedMemoryHandle& handle, bool read_only);
// Closes any open files.
~SharedMemory();
// Return true iff the given handle is valid (i.e. not the distingished
// invalid value; NULL for a HANDLE and -1 for a file descriptor)
static bool IsHandleValid(const SharedMemoryHandle& handle);
// Closes a shared memory handle.
static void CloseHandle(const SharedMemoryHandle& handle);
// Returns the maximum number of handles that can be open at once per process.
static size_t GetHandleLimit();
// Duplicates The underlying OS primitive. Returns an invalid handle on
// failure. The caller is responsible for destroying the duplicated OS
// primitive.
static SharedMemoryHandle DuplicateHandle(const SharedMemoryHandle& handle);
#if defined(OS_POSIX) && !(defined(OS_MACOSX) && !defined(OS_IOS))
// This method requires that the SharedMemoryHandle is backed by a POSIX fd.
static int GetFdFromSharedMemoryHandle(const SharedMemoryHandle& handle);
#endif
// Creates a shared memory object as described by the options struct.
// Returns true on success and false on failure.
bool Create(const SharedMemoryCreateOptions& options);
// Creates and maps an anonymous shared memory segment of size size.
// Returns true on success and false on failure.
bool CreateAndMapAnonymous(size_t size);
// Creates an anonymous shared memory segment of size size.
// Returns true on success and false on failure.
bool CreateAnonymous(size_t size) {
SharedMemoryCreateOptions options;
options.size = size;
return Create(options);
}
#if (!defined(OS_MACOSX) || defined(OS_IOS)) && !defined(OS_FUCHSIA)
// DEPRECATED (crbug.com/345734):
// Creates or opens a shared memory segment based on a name.
// If open_existing is true, and the shared memory already exists,
// opens the existing shared memory and ignores the size parameter.
// If open_existing is false, shared memory must not exist.
// size is the size of the block to be created.
// Returns true on success, false on failure.
bool CreateNamedDeprecated(
const std::string& name, bool open_existing, size_t size) {
SharedMemoryCreateOptions options;
options.name_deprecated = &name;
options.open_existing_deprecated = open_existing;
options.size = size;
return Create(options);
}
// Deletes resources associated with a shared memory segment based on name.
// Not all platforms require this call.
bool Delete(const std::string& name);
// Opens a shared memory segment based on a name.
// If read_only is true, opens for read-only access.
// Returns true on success, false on failure.
bool Open(const std::string& name, bool read_only);
#endif // !defined(OS_MACOSX) || defined(OS_IOS)
// Maps the shared memory into the caller's address space.
// Returns true on success, false otherwise. The memory address
// is accessed via the memory() accessor. The mapped address is guaranteed to
// have an alignment of at least MAP_MINIMUM_ALIGNMENT. This method will fail
// if this object is currently mapped.
bool Map(size_t bytes) {
return MapAt(0, bytes);
}
// Same as above, but with |offset| to specify from begining of the shared
// memory block to map.
// |offset| must be alignent to value of |SysInfo::VMAllocationGranularity()|.
bool MapAt(off_t offset, size_t bytes);
enum { MAP_MINIMUM_ALIGNMENT = 32 };
// Unmaps the shared memory from the caller's address space.
// Returns true if successful; returns false on error or if the
// memory is not mapped.
bool Unmap();
// The size requested when the map is first created.
size_t requested_size() const { return requested_size_; }
// The actual size of the mapped memory (may be larger than requested).
size_t mapped_size() const { return mapped_size_; }
// Gets a pointer to the opened memory space if it has been
// Mapped via Map(). Returns NULL if it is not mapped.
void* memory() const { return memory_; }
// Returns the underlying OS handle for this segment.
// Use of this handle for anything other than an opaque
// identifier is not portable.
SharedMemoryHandle handle() const;
// Returns the underlying OS handle for this segment. The caller takes
// ownership of the handle and memory is unmapped. This is equivalent to
// duplicating the handle and then calling Unmap() and Close() on this object,
// without the overhead of duplicating the handle.
SharedMemoryHandle TakeHandle();
// Closes the open shared memory segment. The memory will remain mapped if
// it was previously mapped.
// It is safe to call Close repeatedly.
void Close();
// Returns a read-only handle to this shared memory region. The caller takes
// ownership of the handle. For POSIX handles, CHECK-fails if the region
// wasn't Created or Opened with share_read_only=true, which is required to
// make the handle read-only. When the handle is passed to the IPC subsystem,
// that takes ownership of the handle. As such, it's not valid to pass the
// sample handle to the IPC subsystem twice. Returns an invalid handle on
// failure.
SharedMemoryHandle GetReadOnlyHandle() const;
// Returns an ID for the mapped region. This is ID of the SharedMemoryHandle
// that was mapped. The ID is valid even after the SharedMemoryHandle is
// Closed, as long as the region is not unmapped.
const UnguessableToken& mapped_id() const { return mapped_id_; }
private:
#if defined(OS_POSIX) && !defined(OS_NACL) && !defined(OS_ANDROID) && \
(!defined(OS_MACOSX) || defined(OS_IOS))
bool FilePathForMemoryName(const std::string& mem_name, FilePath* path);
#endif
#if defined(OS_WIN)
// If true indicates this came from an external source so needs extra checks
// before being mapped.
bool external_section_ = false;
string16 name_;
#elif !defined(OS_ANDROID) && !defined(OS_FUCHSIA)
// If valid, points to the same memory region as shm_, but with readonly
// permissions.
SharedMemoryHandle readonly_shm_;
#endif
// The OS primitive that backs the shared memory region.
SharedMemoryHandle shm_;
size_t mapped_size_ = 0;
void* memory_ = nullptr;
bool read_only_ = false;
size_t requested_size_ = 0;
base::UnguessableToken mapped_id_;
DISALLOW_COPY_AND_ASSIGN(SharedMemory);
};
} // namespace base
#endif // BASE_MEMORY_SHARED_MEMORY_H_

View File

@ -1,23 +0,0 @@
// Copyright 2017 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.
#include "base/memory/shared_memory_handle.h"
namespace base {
SharedMemoryHandle::SharedMemoryHandle(const SharedMemoryHandle& handle) =
default;
SharedMemoryHandle& SharedMemoryHandle::operator=(
const SharedMemoryHandle& handle) = default;
base::UnguessableToken SharedMemoryHandle::GetGUID() const {
return guid_;
}
size_t SharedMemoryHandle::GetSize() const {
return size_;
}
} // namespace base

View File

@ -1,220 +0,0 @@
// Copyright 2015 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 BASE_MEMORY_SHARED_MEMORY_HANDLE_H_
#define BASE_MEMORY_SHARED_MEMORY_HANDLE_H_
#include <stddef.h>
#include "base/unguessable_token.h"
#include "build/build_config.h"
#if defined(OS_WIN)
#include "base/process/process_handle.h"
#include "base/win/windows_types.h"
#elif defined(OS_MACOSX) && !defined(OS_IOS)
#include <mach/mach.h>
#include "base/base_export.h"
#include "base/macros.h"
#include "base/process/process_handle.h"
#elif defined(OS_POSIX)
#include <sys/types.h>
#include "base/file_descriptor_posix.h"
#elif defined(OS_FUCHSIA)
#include <zircon/types.h>
#endif
namespace base {
// SharedMemoryHandle is the smallest possible IPC-transportable "reference" to
// a shared memory OS resource. A "reference" can be consumed exactly once [by
// base::SharedMemory] to map the shared memory OS resource into the virtual
// address space of the current process.
// TODO(erikchen): This class should have strong ownership semantics to prevent
// leaks of the underlying OS resource. https://crbug.com/640840.
class BASE_EXPORT SharedMemoryHandle {
public:
// The default constructor returns an invalid SharedMemoryHandle.
SharedMemoryHandle();
// Standard copy constructor. The new instance shares the underlying OS
// primitives.
SharedMemoryHandle(const SharedMemoryHandle& handle);
// Standard assignment operator. The updated instance shares the underlying
// OS primitives.
SharedMemoryHandle& operator=(const SharedMemoryHandle& handle);
// Closes the underlying OS resource.
// The fact that this method needs to be "const" is an artifact of the
// original interface for base::SharedMemory::CloseHandle.
// TODO(erikchen): This doesn't clear the underlying reference, which seems
// like a bug, but is how this class has always worked. Fix this:
// https://crbug.com/716072.
void Close() const;
// Whether ownership of the underlying OS resource is implicitly passed to
// the IPC subsystem during serialization.
void SetOwnershipPassesToIPC(bool ownership_passes);
bool OwnershipPassesToIPC() const;
// Whether the underlying OS resource is valid.
bool IsValid() const;
// Duplicates the underlying OS resource. Using the return value as a
// parameter to an IPC message will cause the IPC subsystem to consume the OS
// resource.
SharedMemoryHandle Duplicate() const;
// Uniques identifies the shared memory region that the underlying OS resource
// points to. Multiple SharedMemoryHandles that point to the same shared
// memory region will have the same GUID. Preserved across IPC.
base::UnguessableToken GetGUID() const;
// Returns the size of the memory region that SharedMemoryHandle points to.
size_t GetSize() const;
#if defined(OS_WIN)
// Takes implicit ownership of |h|.
// |guid| uniquely identifies the shared memory region pointed to by the
// underlying OS resource. If the HANDLE is associated with another
// SharedMemoryHandle, the caller must pass the |guid| of that
// SharedMemoryHandle. Otherwise, the caller should generate a new
// UnguessableToken.
// Passing the wrong |size| has no immediate consequence, but may cause errors
// when trying to map the SharedMemoryHandle at a later point in time.
SharedMemoryHandle(HANDLE h, size_t size, const base::UnguessableToken& guid);
HANDLE GetHandle() const;
#elif defined(OS_FUCHSIA)
// Takes implicit ownership of |h|.
// |guid| uniquely identifies the shared memory region pointed to by the
// underlying OS resource. If the zx_handle_t is associated with another
// SharedMemoryHandle, the caller must pass the |guid| of that
// SharedMemoryHandle. Otherwise, the caller should generate a new
// UnguessableToken.
// Passing the wrong |size| has no immediate consequence, but may cause errors
// when trying to map the SharedMemoryHandle at a later point in time.
SharedMemoryHandle(zx_handle_t h,
size_t size,
const base::UnguessableToken& guid);
zx_handle_t GetHandle() const;
#elif defined(OS_MACOSX) && !defined(OS_IOS)
// Makes a Mach-based SharedMemoryHandle of the given size. On error,
// subsequent calls to IsValid() return false.
// Passing the wrong |size| has no immediate consequence, but may cause errors
// when trying to map the SharedMemoryHandle at a later point in time.
SharedMemoryHandle(mach_vm_size_t size, const base::UnguessableToken& guid);
// Makes a Mach-based SharedMemoryHandle from |memory_object|, a named entry
// in the current task. The memory region has size |size|.
// Passing the wrong |size| has no immediate consequence, but may cause errors
// when trying to map the SharedMemoryHandle at a later point in time.
SharedMemoryHandle(mach_port_t memory_object,
mach_vm_size_t size,
const base::UnguessableToken& guid);
// Exposed so that the SharedMemoryHandle can be transported between
// processes.
mach_port_t GetMemoryObject() const;
// The SharedMemoryHandle must be valid.
// Returns whether the SharedMemoryHandle was successfully mapped into memory.
// On success, |memory| is an output variable that contains the start of the
// mapped memory.
bool MapAt(off_t offset, size_t bytes, void** memory, bool read_only);
#elif defined(OS_POSIX)
// Creates a SharedMemoryHandle from an |fd| supplied from an external
// service.
// Passing the wrong |size| has no immediate consequence, but may cause errors
// when trying to map the SharedMemoryHandle at a later point in time.
static SharedMemoryHandle ImportHandle(int fd, size_t size);
// Returns the underlying OS resource.
int GetHandle() const;
// Invalidates [but doesn't close] the underlying OS resource. This will leak
// unless the caller is careful.
int Release();
#endif
#if defined(OS_ANDROID)
// Marks the current file descriptor as read-only, for the purpose of
// mapping. This is independent of the region's read-only status.
void SetReadOnly() { read_only_ = true; }
// Returns true iff the descriptor is to be used for read-only
// mappings.
bool IsReadOnly() const { return read_only_; }
// Returns true iff the corresponding region is read-only.
bool IsRegionReadOnly() const;
// Try to set the region read-only. This will fail any future attempt
// at read-write mapping.
bool SetRegionReadOnly() const;
#endif
#if defined(OS_POSIX) && !(defined(OS_MACOSX) && !defined(OS_IOS))
// Constructs a SharedMemoryHandle backed by a FileDescriptor. The newly
// created instance has the same ownership semantics as base::FileDescriptor.
// This typically means that the SharedMemoryHandle takes ownership of the
// |fd| if |auto_close| is true. Unfortunately, it's common for existing code
// to make shallow copies of SharedMemoryHandle, and the one that is finally
// passed into a base::SharedMemory is the one that "consumes" the fd.
//
// |guid| uniquely identifies the shared memory region pointed to by the
// underlying OS resource. If |file_descriptor| is associated with another
// SharedMemoryHandle, the caller must pass the |guid| of that
// SharedMemoryHandle. Otherwise, the caller should generate a new
// UnguessableToken.
// Passing the wrong |size| has no immediate consequence, but may cause errors
// when trying to map the SharedMemoryHandle at a later point in time.
SharedMemoryHandle(const base::FileDescriptor& file_descriptor,
size_t size,
const base::UnguessableToken& guid);
#endif
private:
#if defined(OS_WIN)
HANDLE handle_ = nullptr;
// Whether passing this object as a parameter to an IPC message passes
// ownership of |handle_| to the IPC stack. This is meant to mimic the
// behavior of the |auto_close| parameter of FileDescriptor. This member only
// affects attachment-brokered SharedMemoryHandles.
// Defaults to |false|.
bool ownership_passes_to_ipc_ = false;
#elif defined(OS_FUCHSIA)
zx_handle_t handle_ = ZX_HANDLE_INVALID;
bool ownership_passes_to_ipc_ = false;
#elif defined(OS_MACOSX) && !defined(OS_IOS)
friend class SharedMemory;
friend bool CheckReadOnlySharedMemoryHandleForTesting(
SharedMemoryHandle handle);
mach_port_t memory_object_ = MACH_PORT_NULL;
// Whether passing this object as a parameter to an IPC message passes
// ownership of |memory_object_| to the IPC stack. This is meant to mimic
// the behavior of the |auto_close| parameter of FileDescriptor.
// Defaults to |false|.
bool ownership_passes_to_ipc_ = false;
#elif defined(OS_ANDROID)
friend class SharedMemory;
FileDescriptor file_descriptor_;
bool read_only_ = false;
#elif defined(OS_POSIX)
FileDescriptor file_descriptor_;
#endif
base::UnguessableToken guid_;
// The size of the region referenced by the SharedMemoryHandle.
size_t size_ = 0;
};
} // namespace base
#endif // BASE_MEMORY_SHARED_MEMORY_HANDLE_H_

View File

@ -1,55 +0,0 @@
// Copyright 2015 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.
#include "base/memory/shared_memory_handle.h"
#include "base/logging.h"
#include "base/unguessable_token.h"
#include <windows.h>
namespace base {
SharedMemoryHandle::SharedMemoryHandle() {}
SharedMemoryHandle::SharedMemoryHandle(HANDLE h,
size_t size,
const base::UnguessableToken& guid)
: handle_(h), guid_(guid), size_(size) {}
void SharedMemoryHandle::Close() const {
DCHECK(handle_ != nullptr);
::CloseHandle(handle_);
}
bool SharedMemoryHandle::IsValid() const {
return handle_ != nullptr;
}
SharedMemoryHandle SharedMemoryHandle::Duplicate() const {
HANDLE duped_handle;
ProcessHandle process = GetCurrentProcess();
BOOL success = ::DuplicateHandle(process, handle_, process, &duped_handle, 0,
FALSE, DUPLICATE_SAME_ACCESS);
if (!success)
return SharedMemoryHandle();
base::SharedMemoryHandle handle(duped_handle, GetSize(), GetGUID());
handle.SetOwnershipPassesToIPC(true);
return handle;
}
HANDLE SharedMemoryHandle::GetHandle() const {
return handle_;
}
void SharedMemoryHandle::SetOwnershipPassesToIPC(bool ownership_passes) {
ownership_passes_to_ipc_ = ownership_passes;
}
bool SharedMemoryHandle::OwnershipPassesToIPC() const {
return ownership_passes_to_ipc_;
}
} // namespace base

View File

@ -0,0 +1,115 @@
// Copyright 2018 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.
#include "base/memory/shared_memory_mapping.h"
#include <utility>
#include "base/logging.h"
#include "base/memory/shared_memory_tracker.h"
#include "base/unguessable_token.h"
#include "build/build_config.h"
#if defined(OS_POSIX)
#include <sys/mman.h>
#endif
#if defined(OS_WIN)
#include <aclapi.h>
#endif
#if defined(OS_MACOSX) && !defined(OS_IOS)
#include <mach/mach_vm.h>
#include "base/mac/mach_logging.h"
#endif
#if defined(OS_FUCHSIA)
#include <lib/zx/vmar.h>
#include "base/fuchsia/fuchsia_logging.h"
#endif
namespace base {
SharedMemoryMapping::SharedMemoryMapping() = default;
SharedMemoryMapping::SharedMemoryMapping(SharedMemoryMapping&& mapping) noexcept
: memory_(mapping.memory_),
size_(mapping.size_),
mapped_size_(mapping.mapped_size_),
guid_(mapping.guid_) {
mapping.memory_ = nullptr;
}
SharedMemoryMapping& SharedMemoryMapping::operator=(
SharedMemoryMapping&& mapping) noexcept {
Unmap();
memory_ = mapping.memory_;
size_ = mapping.size_;
mapped_size_ = mapping.mapped_size_;
guid_ = mapping.guid_;
mapping.memory_ = nullptr;
return *this;
}
SharedMemoryMapping::~SharedMemoryMapping() {
Unmap();
}
SharedMemoryMapping::SharedMemoryMapping(void* memory,
size_t size,
size_t mapped_size,
const UnguessableToken& guid)
: memory_(memory), size_(size), mapped_size_(mapped_size), guid_(guid) {
SharedMemoryTracker::GetInstance()->IncrementMemoryUsage(*this);
}
void SharedMemoryMapping::Unmap() {
if (!IsValid())
return;
SharedMemoryTracker::GetInstance()->DecrementMemoryUsage(*this);
#if defined(OS_WIN)
if (!UnmapViewOfFile(memory_))
DPLOG(ERROR) << "UnmapViewOfFile";
#elif defined(OS_FUCHSIA)
uintptr_t addr = reinterpret_cast<uintptr_t>(memory_);
zx_status_t status = zx::vmar::root_self()->unmap(addr, mapped_size_);
if (status != ZX_OK)
ZX_DLOG(ERROR, status) << "zx_vmar_unmap";
#elif defined(OS_MACOSX) && !defined(OS_IOS)
kern_return_t kr = mach_vm_deallocate(
mach_task_self(), reinterpret_cast<mach_vm_address_t>(memory_),
mapped_size_);
MACH_DLOG_IF(ERROR, kr != KERN_SUCCESS, kr) << "mach_vm_deallocate";
#else
if (munmap(memory_, mapped_size_) < 0)
DPLOG(ERROR) << "munmap";
#endif
}
ReadOnlySharedMemoryMapping::ReadOnlySharedMemoryMapping() = default;
ReadOnlySharedMemoryMapping::ReadOnlySharedMemoryMapping(
ReadOnlySharedMemoryMapping&&) noexcept = default;
ReadOnlySharedMemoryMapping& ReadOnlySharedMemoryMapping::operator=(
ReadOnlySharedMemoryMapping&&) noexcept = default;
ReadOnlySharedMemoryMapping::ReadOnlySharedMemoryMapping(
void* address,
size_t size,
size_t mapped_size,
const UnguessableToken& guid)
: SharedMemoryMapping(address, size, mapped_size, guid) {}
WritableSharedMemoryMapping::WritableSharedMemoryMapping() = default;
WritableSharedMemoryMapping::WritableSharedMemoryMapping(
WritableSharedMemoryMapping&&) noexcept = default;
WritableSharedMemoryMapping& WritableSharedMemoryMapping::operator=(
WritableSharedMemoryMapping&&) noexcept = default;
WritableSharedMemoryMapping::WritableSharedMemoryMapping(
void* address,
size_t size,
size_t mapped_size,
const UnguessableToken& guid)
: SharedMemoryMapping(address, size, mapped_size, guid) {}
} // namespace base

View File

@ -0,0 +1,252 @@
// Copyright 2018 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 BASE_MEMORY_SHARED_MEMORY_MAPPING_H_
#define BASE_MEMORY_SHARED_MEMORY_MAPPING_H_
#include <cstddef>
#include <type_traits>
#include "base/containers/buffer_iterator.h"
#include "base/containers/span.h"
#include "base/macros.h"
#include "base/unguessable_token.h"
namespace base {
namespace subtle {
class PlatformSharedMemoryRegion;
} // namespace subtle
// Base class for scoped handles to a shared memory mapping created from a
// shared memory region. Created shared memory mappings remain valid even if the
// creator region is transferred or destroyed.
//
// Each mapping has an UnguessableToken that identifies the shared memory region
// it was created from. This is used for memory metrics, to avoid overcounting
// shared memory.
class BASE_EXPORT SharedMemoryMapping {
public:
// Default constructor initializes an invalid instance.
SharedMemoryMapping();
// Move operations are allowed.
SharedMemoryMapping(SharedMemoryMapping&& mapping) noexcept;
SharedMemoryMapping& operator=(SharedMemoryMapping&& mapping) noexcept;
// Unmaps the region if the mapping is valid.
virtual ~SharedMemoryMapping();
// Returns true iff the mapping is valid. False means there is no
// corresponding area of memory.
bool IsValid() const { return memory_ != nullptr; }
// Returns the logical size of the mapping in bytes. This is precisely the
// size requested by whoever created the mapping, and it is always less than
// or equal to |mapped_size()|. This is undefined for invalid instances.
size_t size() const {
DCHECK(IsValid());
return size_;
}
// Returns the actual size of the mapping in bytes. This is always at least
// as large as |size()| but may be larger due to platform mapping alignment
// constraints. This is undefined for invalid instances.
size_t mapped_size() const {
DCHECK(IsValid());
return mapped_size_;
}
// Returns 128-bit GUID of the region this mapping belongs to.
const UnguessableToken& guid() const {
DCHECK(IsValid());
return guid_;
}
protected:
SharedMemoryMapping(void* address,
size_t size,
size_t mapped_size,
const UnguessableToken& guid);
void* raw_memory_ptr() const { return memory_; }
private:
friend class SharedMemoryTracker;
void Unmap();
void* memory_ = nullptr;
size_t size_ = 0;
size_t mapped_size_ = 0;
UnguessableToken guid_;
DISALLOW_COPY_AND_ASSIGN(SharedMemoryMapping);
};
// Class modeling a read-only mapping of a shared memory region into the
// current process' address space. This is created by ReadOnlySharedMemoryRegion
// instances.
class BASE_EXPORT ReadOnlySharedMemoryMapping : public SharedMemoryMapping {
public:
// Default constructor initializes an invalid instance.
ReadOnlySharedMemoryMapping();
// Move operations are allowed.
ReadOnlySharedMemoryMapping(ReadOnlySharedMemoryMapping&&) noexcept;
ReadOnlySharedMemoryMapping& operator=(
ReadOnlySharedMemoryMapping&&) noexcept;
// Returns the base address of the mapping. This is read-only memory. This is
// page-aligned. This is nullptr for invalid instances.
const void* memory() const { return raw_memory_ptr(); }
// Returns a pointer to a page-aligned const T if the mapping is valid and
// large enough to contain a T, or nullptr otherwise.
template <typename T>
const T* GetMemoryAs() const {
static_assert(std::is_trivially_copyable<T>::value,
"Copying non-trivially-copyable object across memory spaces "
"is dangerous");
if (!IsValid())
return nullptr;
if (sizeof(T) > size())
return nullptr;
return static_cast<const T*>(raw_memory_ptr());
}
// Returns a span of const T. The number of elements is autodeduced from the
// size of the shared memory mapping. The number of elements may be
// autodeduced as zero, i.e. the mapping is invalid or the size of the mapping
// isn't large enough to contain even one T: in that case, an empty span
// will be returned. The first element, if any, is guaranteed to be
// page-aligned.
template <typename T>
span<const T> GetMemoryAsSpan() const {
static_assert(std::is_trivially_copyable<T>::value,
"Copying non-trivially-copyable object across memory spaces "
"is dangerous");
if (!IsValid())
return span<const T>();
size_t count = size() / sizeof(T);
return GetMemoryAsSpan<T>(count);
}
// Returns a span of const T with |count| elements if the mapping is valid and
// large enough to contain |count| elements, or an empty span otherwise. The
// first element, if any, is guaranteed to be page-aligned.
template <typename T>
span<const T> GetMemoryAsSpan(size_t count) const {
static_assert(std::is_trivially_copyable<T>::value,
"Copying non-trivially-copyable object across memory spaces "
"is dangerous");
if (!IsValid())
return span<const T>();
if (size() / sizeof(T) < count)
return span<const T>();
return span<const T>(static_cast<const T*>(raw_memory_ptr()), count);
}
// Returns a BufferIterator of const T.
template <typename T>
BufferIterator<const T> GetMemoryAsBufferIterator() const {
return BufferIterator<const T>(GetMemoryAsSpan<T>());
}
private:
friend class ReadOnlySharedMemoryRegion;
ReadOnlySharedMemoryMapping(void* address,
size_t size,
size_t mapped_size,
const UnguessableToken& guid);
DISALLOW_COPY_AND_ASSIGN(ReadOnlySharedMemoryMapping);
};
// Class modeling a writable mapping of a shared memory region into the
// current process' address space. This is created by *SharedMemoryRegion
// instances.
class BASE_EXPORT WritableSharedMemoryMapping : public SharedMemoryMapping {
public:
// Default constructor initializes an invalid instance.
WritableSharedMemoryMapping();
// Move operations are allowed.
WritableSharedMemoryMapping(WritableSharedMemoryMapping&&) noexcept;
WritableSharedMemoryMapping& operator=(
WritableSharedMemoryMapping&&) noexcept;
// Returns the base address of the mapping. This is writable memory. This is
// page-aligned. This is nullptr for invalid instances.
void* memory() const { return raw_memory_ptr(); }
// Returns a pointer to a page-aligned T if the mapping is valid and large
// enough to contain a T, or nullptr otherwise.
template <typename T>
T* GetMemoryAs() const {
static_assert(std::is_trivially_copyable<T>::value,
"Copying non-trivially-copyable object across memory spaces "
"is dangerous");
if (!IsValid())
return nullptr;
if (sizeof(T) > size())
return nullptr;
return static_cast<T*>(raw_memory_ptr());
}
// Returns a span of T. The number of elements is autodeduced from the size of
// the shared memory mapping. The number of elements may be autodeduced as
// zero, i.e. the mapping is invalid or the size of the mapping isn't large
// enough to contain even one T: in that case, an empty span will be returned.
// The first element, if any, is guaranteed to be page-aligned.
template <typename T>
span<T> GetMemoryAsSpan() const {
static_assert(std::is_trivially_copyable<T>::value,
"Copying non-trivially-copyable object across memory spaces "
"is dangerous");
if (!IsValid())
return span<T>();
size_t count = size() / sizeof(T);
return GetMemoryAsSpan<T>(count);
}
// Returns a span of T with |count| elements if the mapping is valid and large
// enough to contain |count| elements, or an empty span otherwise. The first
// element, if any, is guaranteed to be page-aligned.
template <typename T>
span<T> GetMemoryAsSpan(size_t count) const {
static_assert(std::is_trivially_copyable<T>::value,
"Copying non-trivially-copyable object across memory spaces "
"is dangerous");
if (!IsValid())
return span<T>();
if (size() / sizeof(T) < count)
return span<T>();
return span<T>(static_cast<T*>(raw_memory_ptr()), count);
}
// Returns a BufferIterator of T.
template <typename T>
BufferIterator<T> GetMemoryAsBufferIterator() {
return BufferIterator<T>(GetMemoryAsSpan<T>());
}
private:
friend WritableSharedMemoryMapping MapAtForTesting(
subtle::PlatformSharedMemoryRegion* region,
off_t offset,
size_t size);
friend class ReadOnlySharedMemoryRegion;
friend class WritableSharedMemoryRegion;
friend class UnsafeSharedMemoryRegion;
WritableSharedMemoryMapping(void* address,
size_t size,
size_t mapped_size,
const UnguessableToken& guid);
DISALLOW_COPY_AND_ASSIGN(WritableSharedMemoryMapping);
};
} // namespace base
#endif // BASE_MEMORY_SHARED_MEMORY_MAPPING_H_

View File

@ -1,388 +0,0 @@
// Copyright (c) 2011 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.
#include "base/memory/shared_memory.h"
#include <aclapi.h>
#include <stddef.h>
#include <stdint.h>
#include "base/allocator/partition_allocator/page_allocator.h"
#include "base/logging.h"
#include "base/memory/shared_memory_tracker.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/rand_util.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/unguessable_token.h"
#include "base/win/windows_version.h"
namespace base {
namespace {
// Errors that can occur during Shared Memory construction.
// These match tools/metrics/histograms/histograms.xml.
// This enum is append-only.
enum CreateError {
SUCCESS = 0,
SIZE_ZERO = 1,
SIZE_TOO_LARGE = 2,
INITIALIZE_ACL_FAILURE = 3,
INITIALIZE_SECURITY_DESC_FAILURE = 4,
SET_SECURITY_DESC_FAILURE = 5,
CREATE_FILE_MAPPING_FAILURE = 6,
REDUCE_PERMISSIONS_FAILURE = 7,
ALREADY_EXISTS = 8,
CREATE_ERROR_LAST = ALREADY_EXISTS
};
// Emits UMA metrics about encountered errors. Pass zero (0) for |winerror|
// if there is no associated Windows error.
void LogError(CreateError error, DWORD winerror) {
UMA_HISTOGRAM_ENUMERATION("SharedMemory.CreateError", error,
CREATE_ERROR_LAST + 1);
static_assert(ERROR_SUCCESS == 0, "Windows error code changed!");
if (winerror != ERROR_SUCCESS)
UmaHistogramSparse("SharedMemory.CreateWinError", winerror);
}
typedef enum _SECTION_INFORMATION_CLASS {
SectionBasicInformation,
} SECTION_INFORMATION_CLASS;
typedef struct _SECTION_BASIC_INFORMATION {
PVOID BaseAddress;
ULONG Attributes;
LARGE_INTEGER Size;
} SECTION_BASIC_INFORMATION, *PSECTION_BASIC_INFORMATION;
typedef ULONG(__stdcall* NtQuerySectionType)(
HANDLE SectionHandle,
SECTION_INFORMATION_CLASS SectionInformationClass,
PVOID SectionInformation,
ULONG SectionInformationLength,
PULONG ResultLength);
// Returns the length of the memory section starting at the supplied address.
size_t GetMemorySectionSize(void* address) {
MEMORY_BASIC_INFORMATION memory_info;
if (!::VirtualQuery(address, &memory_info, sizeof(memory_info)))
return 0;
return memory_info.RegionSize - (static_cast<char*>(address) -
static_cast<char*>(memory_info.AllocationBase));
}
// Checks if the section object is safe to map. At the moment this just means
// it's not an image section.
bool IsSectionSafeToMap(HANDLE handle) {
static NtQuerySectionType nt_query_section_func;
if (!nt_query_section_func) {
nt_query_section_func = reinterpret_cast<NtQuerySectionType>(
::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), "NtQuerySection"));
DCHECK(nt_query_section_func);
}
// The handle must have SECTION_QUERY access for this to succeed.
SECTION_BASIC_INFORMATION basic_information = {};
ULONG status =
nt_query_section_func(handle, SectionBasicInformation, &basic_information,
sizeof(basic_information), nullptr);
if (status)
return false;
return (basic_information.Attributes & SEC_IMAGE) != SEC_IMAGE;
}
// Returns a HANDLE on success and |nullptr| on failure.
// This function is similar to CreateFileMapping, but removes the permissions
// WRITE_DAC, WRITE_OWNER, READ_CONTROL, and DELETE.
//
// A newly created file mapping has two sets of permissions. It has access
// control permissions (WRITE_DAC, WRITE_OWNER, READ_CONTROL, and DELETE) and
// file permissions (FILE_MAP_READ, FILE_MAP_WRITE, etc.). ::DuplicateHandle()
// with the parameter DUPLICATE_SAME_ACCESS copies both sets of permissions.
//
// The Chrome sandbox prevents HANDLEs with the WRITE_DAC permission from being
// duplicated into unprivileged processes. But the only way to copy file
// permissions is with the parameter DUPLICATE_SAME_ACCESS. This means that
// there is no way for a privileged process to duplicate a file mapping into an
// unprivileged process while maintaining the previous file permissions.
//
// By removing all access control permissions of a file mapping immediately
// after creation, ::DuplicateHandle() effectively only copies the file
// permissions.
HANDLE CreateFileMappingWithReducedPermissions(SECURITY_ATTRIBUTES* sa,
size_t rounded_size,
LPCWSTR name) {
HANDLE h = CreateFileMapping(INVALID_HANDLE_VALUE, sa, PAGE_READWRITE, 0,
static_cast<DWORD>(rounded_size), name);
if (!h) {
LogError(CREATE_FILE_MAPPING_FAILURE, GetLastError());
return nullptr;
}
HANDLE h2;
BOOL success = ::DuplicateHandle(
GetCurrentProcess(), h, GetCurrentProcess(), &h2,
FILE_MAP_READ | FILE_MAP_WRITE | SECTION_QUERY, FALSE, 0);
BOOL rv = ::CloseHandle(h);
DCHECK(rv);
if (!success) {
LogError(REDUCE_PERMISSIONS_FAILURE, GetLastError());
return nullptr;
}
return h2;
}
} // namespace.
SharedMemory::SharedMemory() {}
SharedMemory::SharedMemory(const string16& name) : name_(name) {}
SharedMemory::SharedMemory(const SharedMemoryHandle& handle, bool read_only)
: external_section_(true), shm_(handle), read_only_(read_only) {}
SharedMemory::~SharedMemory() {
Unmap();
Close();
}
// static
bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) {
return handle.IsValid();
}
// static
void SharedMemory::CloseHandle(const SharedMemoryHandle& handle) {
handle.Close();
}
// static
size_t SharedMemory::GetHandleLimit() {
// Rounded down from value reported here:
// http://blogs.technet.com/b/markrussinovich/archive/2009/09/29/3283844.aspx
return static_cast<size_t>(1 << 23);
}
// static
SharedMemoryHandle SharedMemory::DuplicateHandle(
const SharedMemoryHandle& handle) {
return handle.Duplicate();
}
bool SharedMemory::CreateAndMapAnonymous(size_t size) {
return CreateAnonymous(size) && Map(size);
}
bool SharedMemory::Create(const SharedMemoryCreateOptions& options) {
// TODO(crbug.com/210609): NaCl forces us to round up 64k here, wasting 32k
// per mapping on average.
static const size_t kSectionMask = 65536 - 1;
DCHECK(!options.executable);
DCHECK(!shm_.IsValid());
if (options.size == 0) {
LogError(SIZE_ZERO, 0);
return false;
}
// Check maximum accounting for overflow.
if (options.size >
static_cast<size_t>(std::numeric_limits<int>::max()) - kSectionMask) {
LogError(SIZE_TOO_LARGE, 0);
return false;
}
size_t rounded_size = (options.size + kSectionMask) & ~kSectionMask;
name_ = options.name_deprecated ? ASCIIToUTF16(*options.name_deprecated)
: string16();
SECURITY_ATTRIBUTES sa = {sizeof(sa), nullptr, FALSE};
SECURITY_DESCRIPTOR sd;
ACL dacl;
if (name_.empty()) {
// Add an empty DACL to enforce anonymous read-only sections.
sa.lpSecurityDescriptor = &sd;
if (!InitializeAcl(&dacl, sizeof(dacl), ACL_REVISION)) {
LogError(INITIALIZE_ACL_FAILURE, GetLastError());
return false;
}
if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
LogError(INITIALIZE_SECURITY_DESC_FAILURE, GetLastError());
return false;
}
if (!SetSecurityDescriptorDacl(&sd, TRUE, &dacl, FALSE)) {
LogError(SET_SECURITY_DESC_FAILURE, GetLastError());
return false;
}
if (win::GetVersion() < win::VERSION_WIN8_1) {
// Windows < 8.1 ignores DACLs on certain unnamed objects (like shared
// sections). So, we generate a random name when we need to enforce
// read-only.
uint64_t rand_values[4];
RandBytes(&rand_values, sizeof(rand_values));
name_ = ASCIIToUTF16(StringPrintf(
"CrSharedMem_%016llx%016llx%016llx%016llx", rand_values[0],
rand_values[1], rand_values[2], rand_values[3]));
DCHECK(!name_.empty());
}
}
shm_ = SharedMemoryHandle(
CreateFileMappingWithReducedPermissions(
&sa, rounded_size, name_.empty() ? nullptr : as_wcstr(name_)),
rounded_size, UnguessableToken::Create());
if (!shm_.IsValid()) {
// The error is logged within CreateFileMappingWithReducedPermissions().
return false;
}
requested_size_ = options.size;
// Check if the shared memory pre-exists.
if (GetLastError() == ERROR_ALREADY_EXISTS) {
// If the file already existed, set requested_size_ to 0 to show that
// we don't know the size.
requested_size_ = 0;
external_section_ = true;
if (!options.open_existing_deprecated) {
Close();
// From "if" above: GetLastError() == ERROR_ALREADY_EXISTS.
LogError(ALREADY_EXISTS, ERROR_ALREADY_EXISTS);
return false;
}
}
LogError(SUCCESS, ERROR_SUCCESS);
return true;
}
bool SharedMemory::Delete(const std::string& name) {
// intentionally empty -- there is nothing for us to do on Windows.
return true;
}
bool SharedMemory::Open(const std::string& name, bool read_only) {
DCHECK(!shm_.IsValid());
DWORD access = FILE_MAP_READ | SECTION_QUERY;
if (!read_only)
access |= FILE_MAP_WRITE;
name_ = ASCIIToUTF16(name);
read_only_ = read_only;
// This form of sharing shared memory is deprecated. https://crbug.com/345734.
// However, we can't get rid of it without a significant refactor because its
// used to communicate between two versions of the same service process, very
// early in the life cycle.
// Technically, we should also pass the GUID from the original shared memory
// region. We don't do that - this means that we will overcount this memory,
// which thankfully isn't relevant since Chrome only communicates with a
// single version of the service process.
// We pass the size |0|, which is a dummy size and wrong, but otherwise
// harmless.
shm_ = SharedMemoryHandle(
OpenFileMapping(access, false, name_.empty() ? nullptr : as_wcstr(name_)),
0u, UnguessableToken::Create());
if (!shm_.IsValid())
return false;
// If a name specified assume it's an external section.
if (!name_.empty())
external_section_ = true;
// Note: size_ is not set in this case.
return true;
}
bool SharedMemory::MapAt(off_t offset, size_t bytes) {
if (!shm_.IsValid()) {
DLOG(ERROR) << "Invalid SharedMemoryHandle.";
return false;
}
if (bytes > static_cast<size_t>(std::numeric_limits<int>::max())) {
DLOG(ERROR) << "Bytes required exceeds the 2G limitation.";
return false;
}
if (memory_) {
DLOG(ERROR) << "The SharedMemory has been mapped already.";
return false;
}
if (external_section_ && !IsSectionSafeToMap(shm_.GetHandle())) {
DLOG(ERROR) << "SharedMemoryHandle is not safe to be mapped.";
return false;
}
// Try to map the shared memory. On the first failure, release any reserved
// address space for a single retry.
for (int i = 0; i < 2; ++i) {
memory_ = MapViewOfFile(
shm_.GetHandle(),
read_only_ ? FILE_MAP_READ : FILE_MAP_READ | FILE_MAP_WRITE,
static_cast<uint64_t>(offset) >> 32, static_cast<DWORD>(offset), bytes);
if (memory_)
break;
ReleaseReservation();
}
if (!memory_) {
DPLOG(ERROR) << "Failed executing MapViewOfFile";
return false;
}
DCHECK_EQ(0U, reinterpret_cast<uintptr_t>(memory_) &
(SharedMemory::MAP_MINIMUM_ALIGNMENT - 1));
mapped_size_ = GetMemorySectionSize(memory_);
mapped_id_ = shm_.GetGUID();
SharedMemoryTracker::GetInstance()->IncrementMemoryUsage(*this);
return true;
}
bool SharedMemory::Unmap() {
if (!memory_)
return false;
SharedMemoryTracker::GetInstance()->DecrementMemoryUsage(*this);
UnmapViewOfFile(memory_);
memory_ = nullptr;
mapped_id_ = UnguessableToken();
return true;
}
SharedMemoryHandle SharedMemory::GetReadOnlyHandle() const {
HANDLE result;
ProcessHandle process = GetCurrentProcess();
if (!::DuplicateHandle(process, shm_.GetHandle(), process, &result,
FILE_MAP_READ | SECTION_QUERY, FALSE, 0)) {
return SharedMemoryHandle();
}
SharedMemoryHandle handle =
SharedMemoryHandle(result, shm_.GetSize(), shm_.GetGUID());
handle.SetOwnershipPassesToIPC(true);
return handle;
}
void SharedMemory::Close() {
if (shm_.IsValid()) {
shm_.Close();
shm_ = SharedMemoryHandle();
}
}
SharedMemoryHandle SharedMemory::handle() const {
return shm_;
}
SharedMemoryHandle SharedMemory::TakeHandle() {
SharedMemoryHandle handle(shm_);
handle.SetOwnershipPassesToIPC(true);
Unmap();
shm_ = SharedMemoryHandle();
return handle;
}
} // namespace base

View File

@ -0,0 +1,80 @@
// Copyright 2018 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.
#include "base/memory/unsafe_shared_memory_region.h"
#include <utility>
namespace base {
UnsafeSharedMemoryRegion::CreateFunction*
UnsafeSharedMemoryRegion::create_hook_ = nullptr;
// static
UnsafeSharedMemoryRegion UnsafeSharedMemoryRegion::Create(size_t size) {
if (create_hook_)
return create_hook_(size);
subtle::PlatformSharedMemoryRegion handle =
subtle::PlatformSharedMemoryRegion::CreateUnsafe(size);
return UnsafeSharedMemoryRegion(std::move(handle));
}
// static
UnsafeSharedMemoryRegion UnsafeSharedMemoryRegion::Deserialize(
subtle::PlatformSharedMemoryRegion handle) {
return UnsafeSharedMemoryRegion(std::move(handle));
}
// static
subtle::PlatformSharedMemoryRegion
UnsafeSharedMemoryRegion::TakeHandleForSerialization(
UnsafeSharedMemoryRegion region) {
return std::move(region.handle_);
}
UnsafeSharedMemoryRegion::UnsafeSharedMemoryRegion() = default;
UnsafeSharedMemoryRegion::UnsafeSharedMemoryRegion(
UnsafeSharedMemoryRegion&& region) = default;
UnsafeSharedMemoryRegion& UnsafeSharedMemoryRegion::operator=(
UnsafeSharedMemoryRegion&& region) = default;
UnsafeSharedMemoryRegion::~UnsafeSharedMemoryRegion() = default;
UnsafeSharedMemoryRegion UnsafeSharedMemoryRegion::Duplicate() const {
return UnsafeSharedMemoryRegion(handle_.Duplicate());
}
WritableSharedMemoryMapping UnsafeSharedMemoryRegion::Map() const {
return MapAt(0, handle_.GetSize());
}
WritableSharedMemoryMapping UnsafeSharedMemoryRegion::MapAt(off_t offset,
size_t size) const {
if (!IsValid())
return {};
void* memory = nullptr;
size_t mapped_size = 0;
if (!handle_.MapAt(offset, size, &memory, &mapped_size))
return {};
return WritableSharedMemoryMapping(memory, size, mapped_size,
handle_.GetGUID());
}
bool UnsafeSharedMemoryRegion::IsValid() const {
return handle_.IsValid();
}
UnsafeSharedMemoryRegion::UnsafeSharedMemoryRegion(
subtle::PlatformSharedMemoryRegion handle)
: handle_(std::move(handle)) {
if (handle_.IsValid()) {
CHECK_EQ(handle_.GetMode(),
subtle::PlatformSharedMemoryRegion::Mode::kUnsafe);
}
}
} // namespace base

View File

@ -0,0 +1,127 @@
// Copyright 2018 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 BASE_MEMORY_UNSAFE_SHARED_MEMORY_REGION_H_
#define BASE_MEMORY_UNSAFE_SHARED_MEMORY_REGION_H_
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/memory/platform_shared_memory_region.h"
#include "base/memory/shared_memory_mapping.h"
namespace base {
// Scoped move-only handle to a region of platform shared memory. The instance
// owns the platform handle it wraps. Mappings created by this region are
// writable. These mappings remain valid even after the region handle is moved
// or destroyed.
//
// NOTE: UnsafeSharedMemoryRegion cannot be converted to a read-only region. Use
// with caution as the region will be writable to any process with a handle to
// the region.
//
// Use this if and only if the following is true:
// - You do not need to share the region as read-only, and,
// - You need to have several instances of the region simultaneously, possibly
// in different processes, that can produce writable mappings.
class BASE_EXPORT UnsafeSharedMemoryRegion {
public:
using MappingType = WritableSharedMemoryMapping;
// Creates a new UnsafeSharedMemoryRegion instance of a given size that can be
// used for mapping writable shared memory into the virtual address space.
//
// This call will fail if the process does not have sufficient permissions to
// create a shared memory region itself. See
// mojo::CreateUnsafeSharedMemoryRegion in
// mojo/public/cpp/base/shared_memory_utils.h for creating a shared memory
// region from a an unprivileged process where a broker must be used.
static UnsafeSharedMemoryRegion Create(size_t size);
using CreateFunction = decltype(Create);
// Returns an UnsafeSharedMemoryRegion built from a platform-specific handle
// that was taken from another UnsafeSharedMemoryRegion instance. Returns an
// invalid region iff the |handle| is invalid. CHECK-fails if the |handle|
// isn't unsafe.
// This should be used only by the code passing a handle across
// process boundaries.
static UnsafeSharedMemoryRegion Deserialize(
subtle::PlatformSharedMemoryRegion handle);
// Extracts a platform handle from the region. Ownership is transferred to the
// returned region object.
// This should be used only for sending the handle from the current
// process to another.
static subtle::PlatformSharedMemoryRegion TakeHandleForSerialization(
UnsafeSharedMemoryRegion region);
// Default constructor initializes an invalid instance.
UnsafeSharedMemoryRegion();
// Move operations are allowed.
UnsafeSharedMemoryRegion(UnsafeSharedMemoryRegion&&);
UnsafeSharedMemoryRegion& operator=(UnsafeSharedMemoryRegion&&);
// Destructor closes shared memory region if valid.
// All created mappings will remain valid.
~UnsafeSharedMemoryRegion();
// Duplicates the underlying platform handle and creates a new
// UnsafeSharedMemoryRegion instance that owns the newly created handle.
// Returns a valid UnsafeSharedMemoryRegion on success, invalid otherwise.
// The current region instance remains valid in any case.
UnsafeSharedMemoryRegion Duplicate() const;
// Maps the shared memory region into the caller's address space with write
// access. The mapped address is guaranteed to have an alignment of
// at least |subtle::PlatformSharedMemoryRegion::kMapMinimumAlignment|.
// Returns a valid WritableSharedMemoryMapping instance on success, invalid
// otherwise.
WritableSharedMemoryMapping Map() const;
// Same as above, but maps only |size| bytes of the shared memory region
// starting with the given |offset|. |offset| must be aligned to value of
// |SysInfo::VMAllocationGranularity()|. Returns an invalid mapping if
// requested bytes are out of the region limits.
WritableSharedMemoryMapping MapAt(off_t offset, size_t size) const;
// Whether the underlying platform handle is valid.
bool IsValid() const;
// Returns the maximum mapping size that can be created from this region.
size_t GetSize() const {
DCHECK(IsValid());
return handle_.GetSize();
}
// Returns 128-bit GUID of the region.
const UnguessableToken& GetGUID() const {
DCHECK(IsValid());
return handle_.GetGUID();
}
// Returns a platform shared memory handle. |this| remains the owner of the
// handle.
subtle::PlatformSharedMemoryRegion::PlatformHandle GetPlatformHandle() const {
DCHECK(IsValid());
return handle_.GetPlatformHandle();
}
private:
friend class SharedMemoryHooks;
explicit UnsafeSharedMemoryRegion(subtle::PlatformSharedMemoryRegion handle);
static void set_create_hook(CreateFunction* hook) { create_hook_ = hook; }
static CreateFunction* create_hook_;
subtle::PlatformSharedMemoryRegion handle_;
DISALLOW_COPY_AND_ASSIGN(UnsafeSharedMemoryRegion);
};
} // namespace base
#endif // BASE_MEMORY_UNSAFE_SHARED_MEMORY_REGION_H_

View File

@ -16,14 +16,13 @@
// //
// class Controller { // class Controller {
// public: // public:
// Controller() : weak_factory_(this) {}
// void SpawnWorker() { Worker::StartNew(weak_factory_.GetWeakPtr()); } // void SpawnWorker() { Worker::StartNew(weak_factory_.GetWeakPtr()); }
// void WorkComplete(const Result& result) { ... } // void WorkComplete(const Result& result) { ... }
// private: // private:
// // Member variables should appear before the WeakPtrFactory, to ensure // // Member variables should appear before the WeakPtrFactory, to ensure
// // that any WeakPtrs to Controller are invalidated before its members // // that any WeakPtrs to Controller are invalidated before its members
// // variable's destructors are executed, rendering them invalid. // // variable's destructors are executed, rendering them invalid.
// WeakPtrFactory<Controller> weak_factory_; // WeakPtrFactory<Controller> weak_factory_{this};
// }; // };
// //
// class Worker { // class Worker {
@ -117,9 +116,9 @@ class BASE_EXPORT WeakReference {
explicit WeakReference(const scoped_refptr<Flag>& flag); explicit WeakReference(const scoped_refptr<Flag>& flag);
~WeakReference(); ~WeakReference();
WeakReference(WeakReference&& other); WeakReference(WeakReference&& other) noexcept;
WeakReference(const WeakReference& other); WeakReference(const WeakReference& other);
WeakReference& operator=(WeakReference&& other) = default; WeakReference& operator=(WeakReference&& other) noexcept = default;
WeakReference& operator=(const WeakReference& other) = default; WeakReference& operator=(const WeakReference& other) = default;
bool IsValid() const; bool IsValid() const;
@ -141,7 +140,7 @@ class BASE_EXPORT WeakReferenceOwner {
void Invalidate(); void Invalidate();
private: private:
mutable scoped_refptr<WeakReference::Flag> flag_; scoped_refptr<WeakReference::Flag> flag_;
}; };
// This class simplifies the implementation of WeakPtr's type conversion // This class simplifies the implementation of WeakPtr's type conversion
@ -154,9 +153,9 @@ class BASE_EXPORT WeakPtrBase {
~WeakPtrBase(); ~WeakPtrBase();
WeakPtrBase(const WeakPtrBase& other) = default; WeakPtrBase(const WeakPtrBase& other) = default;
WeakPtrBase(WeakPtrBase&& other) = default; WeakPtrBase(WeakPtrBase&& other) noexcept = default;
WeakPtrBase& operator=(const WeakPtrBase& other) = default; WeakPtrBase& operator=(const WeakPtrBase& other) = default;
WeakPtrBase& operator=(WeakPtrBase&& other) = default; WeakPtrBase& operator=(WeakPtrBase&& other) noexcept = default;
void reset() { void reset() {
ref_ = internal::WeakReference(); ref_ = internal::WeakReference();
@ -237,7 +236,7 @@ class WeakPtr : public internal::WeakPtrBase {
ptr_ = reinterpret_cast<uintptr_t>(t); ptr_ = reinterpret_cast<uintptr_t>(t);
} }
template <typename U> template <typename U>
WeakPtr(WeakPtr<U>&& other) : WeakPtrBase(std::move(other)) { WeakPtr(WeakPtr<U>&& other) noexcept : WeakPtrBase(std::move(other)) {
// Need to cast from U* to T* to do pointer adjustment in case of multiple // Need to cast from U* to T* to do pointer adjustment in case of multiple
// inheritance. This also enforces the "U is a T" rule. // inheritance. This also enforces the "U is a T" rule.
T* t = reinterpret_cast<U*>(other.ptr_); T* t = reinterpret_cast<U*>(other.ptr_);

View File

@ -192,12 +192,14 @@ constexpr ClampedNumeric<typename UnderlyingType<T>::type> MakeClampedNum(
return value; return value;
} }
#if !BASE_NUMERICS_DISABLE_OSTREAM_OPERATORS
// Overload the ostream output operator to make logging work nicely. // Overload the ostream output operator to make logging work nicely.
template <typename T> template <typename T>
std::ostream& operator<<(std::ostream& os, const ClampedNumeric<T>& value) { std::ostream& operator<<(std::ostream& os, const ClampedNumeric<T>& value) {
os << static_cast<T>(value); os << static_cast<T>(value);
return os; return os;
} }
#endif
// These implement the variadic wrapper for the math operations. // These implement the variadic wrapper for the math operations.
template <template <typename, typename, typename> class M, template <template <typename, typename, typename> class M,

View File

@ -8,7 +8,6 @@
#include <stddef.h> #include <stddef.h>
#include <limits> #include <limits>
#include <ostream>
#include <type_traits> #include <type_traits>
#include "base/numerics/safe_conversions_impl.h" #include "base/numerics/safe_conversions_impl.h"
@ -20,6 +19,10 @@
#define BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS (0) #define BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS (0)
#endif #endif
#if !BASE_NUMERICS_DISABLE_OSTREAM_OPERATORS
#include <ostream>
#endif
namespace base { namespace base {
namespace internal { namespace internal {
@ -308,12 +311,14 @@ constexpr StrictNumeric<typename UnderlyingType<T>::type> MakeStrictNum(
return value; return value;
} }
#if !BASE_NUMERICS_DISABLE_OSTREAM_OPERATORS
// Overload the ostream output operator to make logging work nicely. // Overload the ostream output operator to make logging work nicely.
template <typename T> template <typename T>
std::ostream& operator<<(std::ostream& os, const StrictNumeric<T>& value) { std::ostream& operator<<(std::ostream& os, const StrictNumeric<T>& value) {
os << static_cast<T>(value); os << static_cast<T>(value);
return os; return os;
} }
#endif
#define BASE_NUMERIC_COMPARISON_OPERATORS(CLASS, NAME, OP) \ #define BASE_NUMERIC_COMPARISON_OPERATORS(CLASS, NAME, OP) \
template <typename L, typename R, \ template <typename L, typename R, \

View File

@ -80,8 +80,9 @@ template <typename T>
constexpr typename std::make_unsigned<T>::type SafeUnsignedAbs(T value) { constexpr typename std::make_unsigned<T>::type SafeUnsignedAbs(T value) {
static_assert(std::is_integral<T>::value, "Type must be integral"); static_assert(std::is_integral<T>::value, "Type must be integral");
using UnsignedT = typename std::make_unsigned<T>::type; using UnsignedT = typename std::make_unsigned<T>::type;
return IsValueNegative(value) ? 0 - static_cast<UnsignedT>(value) return IsValueNegative(value)
: static_cast<UnsignedT>(value); ? static_cast<UnsignedT>(0u - static_cast<UnsignedT>(value))
: static_cast<UnsignedT>(value);
} }
// This allows us to switch paths on known compile-time constants. // This allows us to switch paths on known compile-time constants.

View File

@ -17,12 +17,15 @@
#include "base/numerics/safe_conversions.h" #include "base/numerics/safe_conversions.h"
#ifdef __asmjs__
// Optimized safe math instructions are incompatible with asmjs.
#define BASE_HAS_OPTIMIZED_SAFE_MATH (0)
// Where available use builtin math overflow support on Clang and GCC. // Where available use builtin math overflow support on Clang and GCC.
#if !defined(__native_client__) && \ #elif !defined(__native_client__) && \
((defined(__clang__) && \ ((defined(__clang__) && \
((__clang_major__ > 3) || \ ((__clang_major__ > 3) || \
(__clang_major__ == 3 && __clang_minor__ >= 4))) || \ (__clang_major__ == 3 && __clang_minor__ >= 4))) || \
(defined(__GNUC__) && __GNUC__ >= 5)) (defined(__GNUC__) && __GNUC__ >= 5))
#include "base/numerics/safe_math_clang_gcc_impl.h" #include "base/numerics/safe_math_clang_gcc_impl.h"
#define BASE_HAS_OPTIMIZED_SAFE_MATH (1) #define BASE_HAS_OPTIMIZED_SAFE_MATH (1)
#else #else

View File

@ -5,12 +5,12 @@
#ifndef BASE_OPTIONAL_H_ #ifndef BASE_OPTIONAL_H_
#define BASE_OPTIONAL_H_ #define BASE_OPTIONAL_H_
#include <functional>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#include "base/logging.h" #include "base/logging.h"
#include "base/template_util.h" #include "base/template_util.h"
#include "base/thread_annotations.h"
namespace base { namespace base {
@ -270,9 +270,7 @@ class OptionalBase {
storage_.Init(std::forward<U>(value)); storage_.Init(std::forward<U>(value));
} }
// TODO(lukasza): Figure out how to remove the NO_THREAD_SAFETY_ANALYSIS void FreeIfNeeded() {
// annotation below. See https://crbug.com/881875#c1 for details.
void FreeIfNeeded() NO_THREAD_SAFETY_ANALYSIS {
if (!storage_.is_populated_) if (!storage_.is_populated_)
return; return;
storage_.value_.~T(); storage_.value_.~T();
@ -429,6 +427,28 @@ class OPTIONAL_DECLSPEC_EMPTY_BASES Optional
std::is_copy_assignable<T>::value>, std::is_copy_assignable<T>::value>,
public internal::MoveAssignable<std::is_move_constructible<T>::value && public internal::MoveAssignable<std::is_move_constructible<T>::value &&
std::is_move_assignable<T>::value> { std::is_move_assignable<T>::value> {
private:
// Disable some versions of T that are ill-formed.
// See: https://timsong-cpp.github.io/cppwp/n4659/optional#syn-1
static_assert(
!std::is_same<internal::RemoveCvRefT<T>, in_place_t>::value,
"instantiation of base::Optional with in_place_t is ill-formed");
static_assert(!std::is_same<internal::RemoveCvRefT<T>, nullopt_t>::value,
"instantiation of base::Optional with nullopt_t is ill-formed");
static_assert(
!std::is_reference<T>::value,
"instantiation of base::Optional with a reference type is ill-formed");
// See: https://timsong-cpp.github.io/cppwp/n4659/optional#optional-3
static_assert(std::is_destructible<T>::value,
"instantiation of base::Optional with a non-destructible type "
"is ill-formed");
// Arrays are explicitly disallowed because for arrays of known bound
// is_destructible is of undefined value.
// See: https://en.cppreference.com/w/cpp/types/is_destructible
static_assert(
!std::is_array<T>::value,
"instantiation of base::Optional with an array type is ill-formed");
public: public:
#undef OPTIONAL_DECLSPEC_EMPTY_BASES #undef OPTIONAL_DECLSPEC_EMPTY_BASES
using value_type = T; using value_type = T;
@ -570,32 +590,32 @@ class OPTIONAL_DECLSPEC_EMPTY_BASES Optional
} }
constexpr const T* operator->() const { constexpr const T* operator->() const {
DCHECK(storage_.is_populated_); CHECK(storage_.is_populated_);
return &storage_.value_; return &storage_.value_;
} }
constexpr T* operator->() { constexpr T* operator->() {
DCHECK(storage_.is_populated_); CHECK(storage_.is_populated_);
return &storage_.value_; return &storage_.value_;
} }
constexpr const T& operator*() const & { constexpr const T& operator*() const & {
DCHECK(storage_.is_populated_); CHECK(storage_.is_populated_);
return storage_.value_; return storage_.value_;
} }
constexpr T& operator*() & { constexpr T& operator*() & {
DCHECK(storage_.is_populated_); CHECK(storage_.is_populated_);
return storage_.value_; return storage_.value_;
} }
constexpr const T&& operator*() const && { constexpr const T&& operator*() const && {
DCHECK(storage_.is_populated_); CHECK(storage_.is_populated_);
return std::move(storage_.value_); return std::move(storage_.value_);
} }
constexpr T&& operator*() && { constexpr T&& operator*() && {
DCHECK(storage_.is_populated_); CHECK(storage_.is_populated_);
return std::move(storage_.value_); return std::move(storage_.value_);
} }

View File

@ -0,0 +1,128 @@
// Copyright 2019 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.
#include "base/process/environment_internal.h"
#include <stddef.h>
#include <vector>
namespace base {
namespace internal {
namespace {
#if defined(OS_POSIX) || defined(OS_FUCHSIA) || defined(OS_WIN)
// Parses a null-terminated input string of an environment block. The key is
// placed into the given string, and the total length of the line, including
// the terminating null, is returned.
size_t ParseEnvLine(const NativeEnvironmentString::value_type* input,
NativeEnvironmentString* key) {
// Skip to the equals or end of the string, this is the key.
size_t cur = 0;
while (input[cur] && input[cur] != '=')
cur++;
*key = NativeEnvironmentString(&input[0], cur);
// Now just skip to the end of the string.
while (input[cur])
cur++;
return cur + 1;
}
#endif
} // namespace
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
std::unique_ptr<char* []> AlterEnvironment(const char* const* const env,
const EnvironmentMap& changes) {
std::string value_storage; // Holds concatenated null-terminated strings.
std::vector<size_t> result_indices; // Line indices into value_storage.
// First build up all of the unchanged environment strings. These are
// null-terminated of the form "key=value".
std::string key;
for (size_t i = 0; env[i]; i++) {
size_t line_length = ParseEnvLine(env[i], &key);
// Keep only values not specified in the change vector.
auto found_change = changes.find(key);
if (found_change == changes.end()) {
result_indices.push_back(value_storage.size());
value_storage.append(env[i], line_length);
}
}
// Now append all modified and new values.
for (const auto& i : changes) {
if (!i.second.empty()) {
result_indices.push_back(value_storage.size());
value_storage.append(i.first);
value_storage.push_back('=');
value_storage.append(i.second);
value_storage.push_back(0);
}
}
size_t pointer_count_required =
result_indices.size() + 1 + // Null-terminated array of pointers.
(value_storage.size() + sizeof(char*) - 1) / sizeof(char*); // Buffer.
std::unique_ptr<char*[]> result(new char*[pointer_count_required]);
// The string storage goes after the array of pointers.
char* storage_data =
reinterpret_cast<char*>(&result.get()[result_indices.size() + 1]);
if (!value_storage.empty())
memcpy(storage_data, value_storage.data(), value_storage.size());
// Fill array of pointers at the beginning of the result.
for (size_t i = 0; i < result_indices.size(); i++)
result[i] = &storage_data[result_indices[i]];
result[result_indices.size()] = 0; // Null terminator.
return result;
}
#elif defined(OS_WIN)
NativeEnvironmentString AlterEnvironment(const wchar_t* env,
const EnvironmentMap& changes) {
NativeEnvironmentString result;
// First build up all of the unchanged environment strings.
const wchar_t* ptr = env;
while (*ptr) {
std::wstring key;
size_t line_length = ParseEnvLine(ptr, &key);
// Keep only values not specified in the change vector.
if (changes.find(key) == changes.end()) {
result.append(ptr, line_length);
}
ptr += line_length;
}
// Now append all modified and new values.
for (const auto& i : changes) {
// Windows environment blocks cannot handle keys or values with NULs.
CHECK_EQ(std::wstring::npos, i.first.find(L'\0'));
CHECK_EQ(std::wstring::npos, i.second.find(L'\0'));
if (!i.second.empty()) {
result += i.first;
result.push_back('=');
result += i.second;
result.push_back('\0');
}
}
// Add the terminating NUL.
result.push_back('\0');
return result;
}
#endif // OS_POSIX || OS_FUCHSIA
} // namespace internal
} // namespace base

View File

@ -0,0 +1,52 @@
// Copyright 2019 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.
// This file contains internal routines that are called by other files in
// base/process/.
#ifndef BASE_PROCESS_ENVIRONMENT_INTERNAL_H_
#define BASE_PROCESS_ENVIRONMENT_INTERNAL_H_
#include <memory>
#include "base/environment.h"
#include "build/build_config.h"
namespace base {
namespace internal {
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
// Returns a modified environment vector constructed from the given environment
// and the list of changes given in |changes|. Each key in the environment is
// matched against the first element of the pairs. In the event of a match, the
// value is replaced by the second of the pair, unless the second is empty, in
// which case the key-value is removed.
//
// This POSIX version takes and returns a POSIX-style environment block, which
// is a null-terminated list of pointers to null-terminated strings. The
// returned array will have appended to it the storage for the array itself so
// there is only one pointer to manage, but this means that you can't copy the
// array without keeping the original around.
BASE_EXPORT std::unique_ptr<char*[]> AlterEnvironment(
const char* const* env,
const EnvironmentMap& changes);
#elif defined(OS_WIN)
// Returns a modified environment vector constructed from the given environment
// and the list of changes given in |changes|. Each key in the environment is
// matched against the first element of the pairs. In the event of a match, the
// value is replaced by the second of the pair, unless the second is empty, in
// which case the key-value is removed.
//
// This Windows version takes and returns a Windows-style environment block,
// which is a string containing several null-terminated strings followed by an
// extra terminating null character. So, e.g., the environment A=1 B=2 is
// represented as L"A=1\0B=2\0\0".
BASE_EXPORT NativeEnvironmentString
AlterEnvironment(const wchar_t* env, const EnvironmentMap& changes);
#endif // OS_*
} // namespace internal
} // namespace base
#endif // BASE_PROCESS_ENVIRONMENT_INTERNAL_H_

View File

@ -29,6 +29,7 @@ const DWORD kNormalTerminationExitCode = 0;
const DWORD kDebuggerInactiveExitCode = 0xC0000354; const DWORD kDebuggerInactiveExitCode = 0xC0000354;
const DWORD kKeyboardInterruptExitCode = 0xC000013A; const DWORD kKeyboardInterruptExitCode = 0xC000013A;
const DWORD kDebuggerTerminatedExitCode = 0x40010004; const DWORD kDebuggerTerminatedExitCode = 0x40010004;
const DWORD kStatusInvalidImageHashExitCode = 0xC0000428;
// This exit code is used by the Windows task manager when it kills a // This exit code is used by the Windows task manager when it kills a
// process. It's value is obviously not that unique, and it's // process. It's value is obviously not that unique, and it's
@ -46,6 +47,7 @@ const DWORD kProcessKilledExitCode = 1;
// exit code arguments to KillProcess*(), use platform/application // exit code arguments to KillProcess*(), use platform/application
// specific values instead. // specific values instead.
enum TerminationStatus { enum TerminationStatus {
// clang-format off
TERMINATION_STATUS_NORMAL_TERMINATION, // zero exit status TERMINATION_STATUS_NORMAL_TERMINATION, // zero exit status
TERMINATION_STATUS_ABNORMAL_TERMINATION, // non-zero exit status TERMINATION_STATUS_ABNORMAL_TERMINATION, // non-zero exit status
TERMINATION_STATUS_PROCESS_WAS_KILLED, // e.g. SIGKILL or task manager kill TERMINATION_STATUS_PROCESS_WAS_KILLED, // e.g. SIGKILL or task manager kill
@ -64,7 +66,12 @@ enum TerminationStatus {
#endif #endif
TERMINATION_STATUS_LAUNCH_FAILED, // child process never launched TERMINATION_STATUS_LAUNCH_FAILED, // child process never launched
TERMINATION_STATUS_OOM, // Process died due to oom TERMINATION_STATUS_OOM, // Process died due to oom
#if defined(OS_WIN)
// On Windows, the OS terminated process due to code integrity failure.
TERMINATION_STATUS_INTEGRITY_FAILURE,
#endif
TERMINATION_STATUS_MAX_ENUM TERMINATION_STATUS_MAX_ENUM
// clang-format on
}; };
// Attempts to kill all the processes on the current machine that were launched // Attempts to kill all the processes on the current machine that were launched

View File

@ -1,412 +0,0 @@
// Copyright 2013 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.
// This file contains functions for launching subprocesses.
#ifndef BASE_PROCESS_LAUNCH_H_
#define BASE_PROCESS_LAUNCH_H_
#include <stddef.h>
#include <string>
#include <utility>
#include <vector>
#include "base/base_export.h"
#include "base/environment.h"
#include "base/macros.h"
#include "base/process/process.h"
#include "base/process/process_handle.h"
#include "base/strings/string_piece.h"
#include "build/build_config.h"
#if defined(OS_WIN)
#include <windows.h>
#elif defined(OS_FUCHSIA)
#include <lib/fdio/spawn.h>
#include <zircon/types.h>
#endif
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
#include "base/posix/file_descriptor_shuffle.h"
#endif
#if defined(OS_MACOSX) && !defined(OS_IOS)
#include "base/mac/mach_port_rendezvous.h"
#endif
namespace base {
class CommandLine;
#if defined(OS_WIN)
typedef std::vector<HANDLE> HandlesToInheritVector;
#elif defined(OS_FUCHSIA)
struct PathToTransfer {
base::FilePath path;
zx_handle_t handle;
};
struct HandleToTransfer {
uint32_t id;
zx_handle_t handle;
};
typedef std::vector<HandleToTransfer> HandlesToTransferVector;
typedef std::vector<std::pair<int, int>> FileHandleMappingVector;
#elif defined(OS_POSIX)
typedef std::vector<std::pair<int, int>> FileHandleMappingVector;
#endif // defined(OS_WIN)
// Options for launching a subprocess that are passed to LaunchProcess().
// The default constructor constructs the object with default options.
struct BASE_EXPORT LaunchOptions {
#if (defined(OS_POSIX) || defined(OS_FUCHSIA)) && !defined(OS_MACOSX)
// Delegate to be run in between fork and exec in the subprocess (see
// pre_exec_delegate below)
class BASE_EXPORT PreExecDelegate {
public:
PreExecDelegate() = default;
virtual ~PreExecDelegate() = default;
// Since this is to be run between fork and exec, and fork may have happened
// while multiple threads were running, this function needs to be async
// safe.
virtual void RunAsyncSafe() = 0;
private:
DISALLOW_COPY_AND_ASSIGN(PreExecDelegate);
};
#endif // defined(OS_POSIX)
LaunchOptions();
LaunchOptions(const LaunchOptions&);
~LaunchOptions();
// If true, wait for the process to complete.
bool wait = false;
// If not empty, change to this directory before executing the new process.
base::FilePath current_directory;
#if defined(OS_WIN)
bool start_hidden = false;
// Sets STARTF_FORCEOFFFEEDBACK so that the feedback cursor is forced off
// while the process is starting.
bool feedback_cursor_off = false;
// Windows can inherit handles when it launches child processes.
// See https://blogs.msdn.microsoft.com/oldnewthing/20111216-00/?p=8873
// for a good overview of Windows handle inheritance.
//
// Implementation note: it might be nice to implement in terms of
// base::Optional<>, but then the natural default state (vector not present)
// would be "all inheritable handles" while we want "no inheritance."
enum class Inherit {
// Only those handles in |handles_to_inherit| vector are inherited. If the
// vector is empty, no handles are inherited. The handles in the vector must
// all be inheritable.
kSpecific,
// All handles in the current process which are inheritable are inherited.
// In production code this flag should be used only when running
// short-lived, trusted binaries, because open handles from other libraries
// and subsystems will leak to the child process, causing errors such as
// open socket hangs. There are also race conditions that can cause handle
// over-sharing.
//
// |handles_to_inherit| must be null.
//
// DEPRECATED. THIS SHOULD NOT BE USED. Explicitly map all handles that
// need to be shared in new code.
// TODO(brettw) bug 748258: remove this.
kAll
};
Inherit inherit_mode = Inherit::kSpecific;
HandlesToInheritVector handles_to_inherit;
// If non-null, runs as if the user represented by the token had launched it.
// Whether the application is visible on the interactive desktop depends on
// the token belonging to an interactive logon session.
//
// To avoid hard to diagnose problems, when specified this loads the
// environment variables associated with the user and if this operation fails
// the entire call fails as well.
UserTokenHandle as_user = nullptr;
// If true, use an empty string for the desktop name.
bool empty_desktop_name = false;
// If non-null, launches the application in that job object. The process will
// be terminated immediately and LaunchProcess() will fail if assignment to
// the job object fails.
HANDLE job_handle = nullptr;
// Handles for the redirection of stdin, stdout and stderr. The caller should
// either set all three of them or none (i.e. there is no way to redirect
// stderr without redirecting stdin).
//
// The handles must be inheritable. Pseudo handles are used when stdout and
// stderr redirect to the console. In that case, GetFileType() will return
// FILE_TYPE_CHAR and they're automatically inherited by child processes. See
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms682075.aspx
// Otherwise, the caller must ensure that the |inherit_mode| and/or
// |handles_to_inherit| set so that the handles are inherited.
HANDLE stdin_handle = nullptr;
HANDLE stdout_handle = nullptr;
HANDLE stderr_handle = nullptr;
// If set to true, ensures that the child process is launched with the
// CREATE_BREAKAWAY_FROM_JOB flag which allows it to breakout of the parent
// job if any.
bool force_breakaway_from_job_ = false;
// If set to true, permission to bring windows to the foreground is passed to
// the launched process if the current process has such permission.
bool grant_foreground_privilege = false;
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
// Set/unset environment variables. These are applied on top of the parent
// process environment. Empty (the default) means to inherit the same
// environment. See AlterEnvironment().
EnvironmentMap environ;
// Clear the environment for the new process before processing changes from
// |environ|.
bool clear_environ = false;
// Remap file descriptors according to the mapping of src_fd->dest_fd to
// propagate FDs into the child process.
FileHandleMappingVector fds_to_remap;
#endif // defined(OS_WIN)
#if defined(OS_LINUX)
// If non-zero, start the process using clone(), using flags as provided.
// Unlike in clone, clone_flags may not contain a custom termination signal
// that is sent to the parent when the child dies. The termination signal will
// always be set to SIGCHLD.
int clone_flags = 0;
// By default, child processes will have the PR_SET_NO_NEW_PRIVS bit set. If
// true, then this bit will not be set in the new child process.
bool allow_new_privs = false;
// Sets parent process death signal to SIGKILL.
bool kill_on_parent_death = false;
#endif // defined(OS_LINUX)
#if defined(OS_MACOSX) && !defined(OS_IOS)
// Mach ports that will be accessible to the child process. These are not
// directly inherited across process creation, but they are stored by a Mach
// IPC server that a child process can communicate with to retrieve them.
//
// After calling LaunchProcess(), any rights that were transferred with MOVE
// dispositions will be consumed, even on failure.
//
// See base/mac/mach_port_rendezvous.h for details.
MachPortsForRendezvous mach_ports_for_rendezvous;
#endif
#if defined(OS_FUCHSIA)
// If valid, launches the application in that job object.
zx_handle_t job_handle = ZX_HANDLE_INVALID;
// Specifies additional handles to transfer (not duplicate) to the child
// process. Each entry is an <id,handle> pair, with an |id| created using the
// PA_HND() macro. The child retrieves the handle
// |zx_take_startup_handle(id)|. The supplied handles are consumed by
// LaunchProcess() even on failure.
// Note that PA_USER1 ids are reserved for use by AddHandleToTransfer(), below
// and by convention PA_USER0 is reserved for use by the embedding
// application.
HandlesToTransferVector handles_to_transfer;
// Allocates a unique id for |handle| in |handles_to_transfer|, inserts it,
// and returns the generated id.
static uint32_t AddHandleToTransfer(
HandlesToTransferVector* handles_to_transfer,
zx_handle_t handle);
// Specifies which basic capabilities to grant to the child process.
// By default the child process will receive the caller's complete namespace,
// access to the current base::fuchsia::DefaultJob(), handles for stdio and
// access to the dynamic library loader.
// Note that the child is always provided access to the loader service.
uint32_t spawn_flags = FDIO_SPAWN_CLONE_NAMESPACE | FDIO_SPAWN_CLONE_STDIO |
FDIO_SPAWN_CLONE_JOB;
// Specifies paths to clone from the calling process' namespace into that of
// the child process. If |paths_to_clone| is empty then the process will
// receive either a full copy of the parent's namespace, or an empty one,
// depending on whether FDIO_SPAWN_CLONE_NAMESPACE is set.
std::vector<FilePath> paths_to_clone;
// Specifies handles which will be installed as files or directories in the
// child process' namespace. Paths installed by |paths_to_clone| will be
// overridden by these entries.
std::vector<PathToTransfer> paths_to_transfer;
#endif // defined(OS_FUCHSIA)
#if defined(OS_POSIX)
// If not empty, launch the specified executable instead of
// cmdline.GetProgram(). This is useful when it is necessary to pass a custom
// argv[0].
base::FilePath real_path;
#if !defined(OS_MACOSX)
// If non-null, a delegate to be run immediately prior to executing the new
// program in the child process.
//
// WARNING: If LaunchProcess is called in the presence of multiple threads,
// code running in this delegate essentially needs to be async-signal safe
// (see man 7 signal for a list of allowed functions).
PreExecDelegate* pre_exec_delegate = nullptr;
#endif // !defined(OS_MACOSX)
// Each element is an RLIMIT_* constant that should be raised to its
// rlim_max. This pointer is owned by the caller and must live through
// the call to LaunchProcess().
const std::vector<int>* maximize_rlimits = nullptr;
// If true, start the process in a new process group, instead of
// inheriting the parent's process group. The pgid of the child process
// will be the same as its pid.
bool new_process_group = false;
#endif // defined(OS_POSIX)
#if defined(OS_CHROMEOS)
// If non-negative, the specified file descriptor will be set as the launched
// process' controlling terminal.
int ctrl_terminal_fd = -1;
#endif // defined(OS_CHROMEOS)
};
// Launch a process via the command line |cmdline|.
// See the documentation of LaunchOptions for details on |options|.
//
// Returns a valid Process upon success.
//
// Unix-specific notes:
// - All file descriptors open in the parent process will be closed in the
// child process except for any preserved by options::fds_to_remap, and
// stdin, stdout, and stderr. If not remapped by options::fds_to_remap,
// stdin is reopened as /dev/null, and the child is allowed to inherit its
// parent's stdout and stderr.
// - If the first argument on the command line does not contain a slash,
// PATH will be searched. (See man execvp.)
BASE_EXPORT Process LaunchProcess(const CommandLine& cmdline,
const LaunchOptions& options);
#if defined(OS_WIN)
// Windows-specific LaunchProcess that takes the command line as a
// string. Useful for situations where you need to control the
// command line arguments directly, but prefer the CommandLine version
// if launching Chrome itself.
//
// The first command line argument should be the path to the process,
// and don't forget to quote it.
//
// Example (including literal quotes)
// cmdline = "c:\windows\explorer.exe" -foo "c:\bar\"
BASE_EXPORT Process LaunchProcess(const string16& cmdline,
const LaunchOptions& options);
// Launches a process with elevated privileges. This does not behave exactly
// like LaunchProcess as it uses ShellExecuteEx instead of CreateProcess to
// create the process. This means the process will have elevated privileges
// and thus some common operations like OpenProcess will fail. Currently the
// only supported LaunchOptions are |start_hidden| and |wait|.
BASE_EXPORT Process LaunchElevatedProcess(const CommandLine& cmdline,
const LaunchOptions& options);
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
// A POSIX-specific version of LaunchProcess that takes an argv array
// instead of a CommandLine. Useful for situations where you need to
// control the command line arguments directly, but prefer the
// CommandLine version if launching Chrome itself.
BASE_EXPORT Process LaunchProcess(const std::vector<std::string>& argv,
const LaunchOptions& options);
#if !defined(OS_MACOSX)
// Close all file descriptors, except those which are a destination in the
// given multimap. Only call this function in a child process where you know
// that there aren't any other threads.
BASE_EXPORT void CloseSuperfluousFds(const InjectiveMultimap& saved_map);
#endif // defined(OS_MACOSX)
#endif // defined(OS_WIN)
#if defined(OS_WIN)
// Set |job_object|'s JOBOBJECT_EXTENDED_LIMIT_INFORMATION
// BasicLimitInformation.LimitFlags to |limit_flags|.
BASE_EXPORT bool SetJobObjectLimitFlags(HANDLE job_object, DWORD limit_flags);
// Output multi-process printf, cout, cerr, etc to the cmd.exe console that ran
// chrome. This is not thread-safe: only call from main thread.
BASE_EXPORT void RouteStdioToConsole(bool create_console_if_not_found);
#endif // defined(OS_WIN)
// Executes the application specified by |cl| and wait for it to exit. Stores
// the output (stdout) in |output|. Redirects stderr to /dev/null. Returns true
// on success (application launched and exited cleanly, with exit code
// indicating success).
BASE_EXPORT bool GetAppOutput(const CommandLine& cl, std::string* output);
// Like GetAppOutput, but also includes stderr.
BASE_EXPORT bool GetAppOutputAndError(const CommandLine& cl,
std::string* output);
// A version of |GetAppOutput()| which also returns the exit code of the
// executed command. Returns true if the application runs and exits cleanly. If
// this is the case the exit code of the application is available in
// |*exit_code|.
BASE_EXPORT bool GetAppOutputWithExitCode(const CommandLine& cl,
std::string* output, int* exit_code);
#if defined(OS_WIN)
// A Windows-specific version of GetAppOutput that takes a command line string
// instead of a CommandLine object. Useful for situations where you need to
// control the command line arguments directly.
BASE_EXPORT bool GetAppOutput(const StringPiece16& cl, std::string* output);
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
// A POSIX-specific version of GetAppOutput that takes an argv array
// instead of a CommandLine. Useful for situations where you need to
// control the command line arguments directly.
BASE_EXPORT bool GetAppOutput(const std::vector<std::string>& argv,
std::string* output);
// Like the above POSIX-specific version of GetAppOutput, but also includes
// stderr.
BASE_EXPORT bool GetAppOutputAndError(const std::vector<std::string>& argv,
std::string* output);
#endif // defined(OS_WIN)
// If supported on the platform, and the user has sufficent rights, increase
// the current process's scheduling priority to a high priority.
BASE_EXPORT void RaiseProcessToHighPriority();
// Creates a LaunchOptions object suitable for launching processes in a test
// binary. This should not be called in production/released code.
BASE_EXPORT LaunchOptions LaunchOptionsForTest();
#if defined(OS_LINUX) || defined(OS_NACL_NONSFI)
// A wrapper for clone with fork-like behavior, meaning that it returns the
// child's pid in the parent and 0 in the child. |flags|, |ptid|, and |ctid| are
// as in the clone system call (the CLONE_VM flag is not supported).
//
// This function uses the libc clone wrapper (which updates libc's pid cache)
// internally, so callers may expect things like getpid() to work correctly
// after in both the child and parent.
//
// As with fork(), callers should be extremely careful when calling this while
// multiple threads are running, since at the time the fork happened, the
// threads could have been in any state (potentially holding locks, etc.).
// Callers should most likely call execve() in the child soon after calling
// this.
//
// It is unsafe to use any pthread APIs after ForkWithFlags().
// However, performing an exec() will lift this restriction.
BASE_EXPORT pid_t ForkWithFlags(unsigned long flags, pid_t* ptid, pid_t* ctid);
#endif
} // namespace base
#endif // BASE_PROCESS_LAUNCH_H_

View File

@ -0,0 +1,89 @@
// Copyright (c) 2013 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 BASE_PROCESS_MEMORY_H_
#define BASE_PROCESS_MEMORY_H_
#include <stddef.h>
#include "base/base_export.h"
#include "base/process/process_handle.h"
#include "build/build_config.h"
namespace base {
// Enables 'terminate on heap corruption' flag. Helps protect against heap
// overflow. Has no effect if the OS doesn't provide the necessary facility.
BASE_EXPORT void EnableTerminationOnHeapCorruption();
// Turns on process termination if memory runs out.
BASE_EXPORT void EnableTerminationOnOutOfMemory();
// Terminates process. Should be called only for out of memory errors.
// Crash reporting classifies such crashes as OOM.
BASE_EXPORT void TerminateBecauseOutOfMemory(size_t size);
#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_AIX)
BASE_EXPORT extern size_t g_oom_size;
// The maximum allowed value for the OOM score.
const int kMaxOomScore = 1000;
// This adjusts /proc/<pid>/oom_score_adj so the Linux OOM killer will
// prefer to kill certain process types over others. The range for the
// adjustment is [-1000, 1000], with [0, 1000] being user accessible.
// If the Linux system doesn't support the newer oom_score_adj range
// of [0, 1000], then we revert to using the older oom_adj, and
// translate the given value into [0, 15]. Some aliasing of values
// may occur in that case, of course.
BASE_EXPORT bool AdjustOOMScore(ProcessId process, int score);
#endif
namespace internal {
// Returns true if address-space was released. Some configurations reserve part
// of the process address-space for special allocations (e.g. WASM).
bool ReleaseAddressSpaceReservation();
} // namespace internal
#if defined(OS_WIN)
namespace win {
// Custom Windows exception code chosen to indicate an out of memory error.
// See https://msdn.microsoft.com/en-us/library/het71c37.aspx.
// "To make sure that you do not define a code that conflicts with an existing
// exception code" ... "The resulting error code should therefore have the
// highest four bits set to hexadecimal E."
// 0xe0000008 was chosen arbitrarily, as 0x00000008 is ERROR_NOT_ENOUGH_MEMORY.
const DWORD kOomExceptionCode = 0xe0000008;
} // namespace win
#endif
namespace internal {
// Handles out of memory, with the failed allocation |size|, or 0 when it is not
// known.
BASE_EXPORT void OnNoMemoryInternal(size_t size);
} // namespace internal
// Special allocator functions for callers that want to check for OOM.
// These will not abort if the allocation fails even if
// EnableTerminationOnOutOfMemory has been called.
// This can be useful for huge and/or unpredictable size memory allocations.
// Please only use this if you really handle the case when the allocation
// fails. Doing otherwise would risk security.
// These functions may still crash on OOM when running under memory tools,
// specifically ASan and other sanitizers.
// Return value tells whether the allocation succeeded. If it fails |result| is
// set to NULL, otherwise it holds the memory address.
BASE_EXPORT WARN_UNUSED_RESULT bool UncheckedMalloc(size_t size,
void** result);
BASE_EXPORT WARN_UNUSED_RESULT bool UncheckedCalloc(size_t num_items,
size_t size,
void** result);
} // namespace base
#endif // BASE_PROCESS_MEMORY_H_

View File

@ -53,6 +53,46 @@ const ProcessId kNullProcessId = 0;
#define CrPRIdPid "d" #define CrPRIdPid "d"
#endif #endif
class UniqueProcId {
public:
explicit UniqueProcId(ProcessId value) : value_(value) {}
UniqueProcId(const UniqueProcId& other) = default;
UniqueProcId& operator=(const UniqueProcId& other) = default;
// Returns the process PID. WARNING: On some platforms, the pid may not be
// valid within the current process sandbox.
ProcessId GetUnsafeValue() const { return value_; }
bool operator==(const UniqueProcId& other) const {
return value_ == other.value_;
}
bool operator!=(const UniqueProcId& other) const {
return value_ != other.value_;
}
bool operator<(const UniqueProcId& other) const {
return value_ < other.value_;
}
bool operator<=(const UniqueProcId& other) const {
return value_ <= other.value_;
}
bool operator>(const UniqueProcId& other) const {
return value_ > other.value_;
}
bool operator>=(const UniqueProcId& other) const {
return value_ >= other.value_;
}
private:
ProcessId value_;
};
std::ostream& operator<<(std::ostream& os, const UniqueProcId& obj);
// Returns the id of the current process. // Returns the id of the current process.
// Note that on some platforms, this is not guaranteed to be unique across // Note that on some platforms, this is not guaranteed to be unique across
// processes (use GetUniqueIdForProcess if uniqueness is required). // processes (use GetUniqueIdForProcess if uniqueness is required).
@ -60,9 +100,8 @@ BASE_EXPORT ProcessId GetCurrentProcId();
// Returns a unique ID for the current process. The ID will be unique across all // Returns a unique ID for the current process. The ID will be unique across all
// currently running processes within the chrome session, but IDs of terminated // currently running processes within the chrome session, but IDs of terminated
// processes may be reused. This returns an opaque value that is different from // processes may be reused.
// a process's PID. BASE_EXPORT UniqueProcId GetUniqueIdForProcess();
BASE_EXPORT uint32_t GetUniqueIdForProcess();
#if defined(OS_LINUX) #if defined(OS_LINUX)
// When a process is started in a different PID namespace from the browser // When a process is started in a different PID namespace from the browser

View File

@ -21,8 +21,14 @@ ProcessHandle GetCurrentProcessHandle() {
} }
ProcessId GetProcId(ProcessHandle process) { ProcessId GetProcId(ProcessHandle process) {
if (process == base::kNullProcessHandle)
return 0;
// This returns 0 if we have insufficient rights to query the process handle. // This returns 0 if we have insufficient rights to query the process handle.
return GetProcessId(process); // Invalid handles or non-process handles will cause a hard failure.
ProcessId result = GetProcessId(process);
CHECK(result != 0 || GetLastError() != ERROR_INVALID_HANDLE)
<< "process handle = " << process;
return result;
} }
ProcessId GetParentProcessId(ProcessHandle process) { ProcessId GetParentProcessId(ProcessHandle process) {

View File

@ -8,9 +8,12 @@
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/sequence_checker_impl.h" #include "base/sequence_checker_impl.h"
#include "base/strings/string_piece.h"
#include "build/build_config.h"
// SequenceChecker is a helper class used to help verify that some methods of a // SequenceChecker is a helper class used to help verify that some methods of a
// class are called sequentially (for thread-safety). // class are called sequentially (for thread-safety). It supports thread safety
// annotations (see base/thread_annotations.h).
// //
// Use the macros below instead of the SequenceChecker directly so that the // Use the macros below instead of the SequenceChecker directly so that the
// unused member doesn't result in an extra byte (four when padded) per // unused member doesn't result in an extra byte (four when padded) per
@ -42,20 +45,45 @@
// void MyMethod() { // void MyMethod() {
// DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_); // DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
// ... (do stuff) ... // ... (do stuff) ...
// MyOtherMethod();
// }
//
// void MyOtherMethod()
// VALID_CONTEXT_REQUIRED(my_sequence_checker_) {
// foo_ = 42;
// } // }
// //
// private: // private:
// // GUARDED_BY_CONTEXT() enforces that this member is only
// // accessed from a scope that invokes DCHECK_CALLED_ON_VALID_SEQUENCE()
// // or from a function annotated with VALID_CONTEXT_REQUIRED(). A
// // DCHECK build will not compile if the member is accessed and these
// // conditions are not met.
// int foo_ GUARDED_BY_CONTEXT(my_sequence_checker_);
//
// SEQUENCE_CHECKER(my_sequence_checker_); // SEQUENCE_CHECKER(my_sequence_checker_);
// } // }
#define SEQUENCE_CHECKER_INTERNAL_CONCAT2(a, b) a##b
#define SEQUENCE_CHECKER_INTERNAL_CONCAT(a, b) \
SEQUENCE_CHECKER_INTERNAL_CONCAT2(a, b)
#define SEQUENCE_CHECKER_INTERNAL_UID(prefix) \
SEQUENCE_CHECKER_INTERNAL_CONCAT(prefix, __LINE__)
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
#define SEQUENCE_CHECKER(name) base::SequenceChecker name #define SEQUENCE_CHECKER(name) base::SequenceChecker name
#define DCHECK_CALLED_ON_VALID_SEQUENCE(name) \ #define DCHECK_CALLED_ON_VALID_SEQUENCE(name, ...) \
DCHECK((name).CalledOnValidSequence()) base::ScopedValidateSequenceChecker SEQUENCE_CHECKER_INTERNAL_UID( \
scoped_validate_sequence_checker_)(name, ##__VA_ARGS__);
#define DETACH_FROM_SEQUENCE(name) (name).DetachFromSequence() #define DETACH_FROM_SEQUENCE(name) (name).DetachFromSequence()
#else // DCHECK_IS_ON() #else // DCHECK_IS_ON()
#if __OBJC__ && defined(OS_IOS) && !HAS_FEATURE(objc_cxx_static_assert)
// TODO(thakis): Remove this branch once Xcode's clang has clang r356148.
#define SEQUENCE_CHECKER(name) #define SEQUENCE_CHECKER(name)
#define DCHECK_CALLED_ON_VALID_SEQUENCE(name) EAT_STREAM_PARAMETERS #else
#define SEQUENCE_CHECKER(name) static_assert(true, "")
#endif
#define DCHECK_CALLED_ON_VALID_SEQUENCE(name, ...) EAT_STREAM_PARAMETERS
#define DETACH_FROM_SEQUENCE(name) #define DETACH_FROM_SEQUENCE(name)
#endif // DCHECK_IS_ON() #endif // DCHECK_IS_ON()
@ -65,9 +93,18 @@ namespace base {
// //
// Note: You should almost always use the SequenceChecker class (through the // Note: You should almost always use the SequenceChecker class (through the
// above macros) to get the right version for your build configuration. // above macros) to get the right version for your build configuration.
class SequenceCheckerDoNothing { // Note: This is only a check, not a "lock". It is marked "LOCKABLE" only in
// order to support thread_annotations.h.
class LOCKABLE SequenceCheckerDoNothing {
public: public:
SequenceCheckerDoNothing() = default; SequenceCheckerDoNothing() = default;
// Moving between matching sequences is allowed to help classes with
// SequenceCheckers that want a default move-construct/assign.
SequenceCheckerDoNothing(SequenceCheckerDoNothing&& other) = default;
SequenceCheckerDoNothing& operator=(SequenceCheckerDoNothing&& other) =
default;
bool CalledOnValidSequence() const WARN_UNUSED_RESULT { return true; } bool CalledOnValidSequence() const WARN_UNUSED_RESULT { return true; }
void DetachFromSequence() {} void DetachFromSequence() {}
@ -83,6 +120,24 @@ class SequenceChecker : public SequenceCheckerDoNothing {
}; };
#endif // DCHECK_IS_ON() #endif // DCHECK_IS_ON()
class SCOPED_LOCKABLE ScopedValidateSequenceChecker {
public:
explicit ScopedValidateSequenceChecker(const SequenceChecker& checker)
EXCLUSIVE_LOCK_FUNCTION(checker) {
DCHECK(checker.CalledOnValidSequence());
}
explicit ScopedValidateSequenceChecker(const SequenceChecker& checker,
const StringPiece& msg)
EXCLUSIVE_LOCK_FUNCTION(checker) {
DCHECK(checker.CalledOnValidSequence()) << msg;
}
~ScopedValidateSequenceChecker() UNLOCK_FUNCTION() {}
private:
};
} // namespace base } // namespace base
#endif // BASE_SEQUENCE_CHECKER_H_ #endif // BASE_SEQUENCE_CHECKER_H_

View File

@ -11,6 +11,7 @@
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/synchronization/lock.h" #include "base/synchronization/lock.h"
#include "base/thread_annotations.h"
namespace base { namespace base {
@ -20,11 +21,22 @@ namespace base {
// //
// Note: You should almost always use the SequenceChecker class to get the right // Note: You should almost always use the SequenceChecker class to get the right
// version for your build configuration. // version for your build configuration.
class BASE_EXPORT SequenceCheckerImpl { // Note: This is only a check, not a "lock". It is marked "LOCKABLE" only in
// order to support thread_annotations.h.
class LOCKABLE BASE_EXPORT SequenceCheckerImpl {
public: public:
SequenceCheckerImpl(); SequenceCheckerImpl();
~SequenceCheckerImpl(); ~SequenceCheckerImpl();
// Allow move construct/assign. This must be called on |other|'s associated
// sequence and assignment can only be made into a SequenceCheckerImpl which
// is detached or already associated with the current sequence. This isn't
// thread-safe (|this| and |other| shouldn't be in use while this move is
// performed). If the assignment was legal, the resulting SequenceCheckerImpl
// will be bound to the current sequence and |other| will be detached.
SequenceCheckerImpl(SequenceCheckerImpl&& other);
SequenceCheckerImpl& operator=(SequenceCheckerImpl&& other);
// Returns true if called in sequence with previous calls to this method and // Returns true if called in sequence with previous calls to this method and
// the constructor. // the constructor.
bool CalledOnValidSequence() const WARN_UNUSED_RESULT; bool CalledOnValidSequence() const WARN_UNUSED_RESULT;
@ -36,9 +48,12 @@ class BASE_EXPORT SequenceCheckerImpl {
private: private:
class Core; class Core;
// Guards all variables below. // Calls straight to ThreadLocalStorage::HasBeenDestroyed(). Exposed purely
// for 'friend' to work.
static bool HasThreadLocalStorageBeenDestroyed();
mutable Lock lock_; mutable Lock lock_;
mutable std::unique_ptr<Core> core_; mutable std::unique_ptr<Core> core_ GUARDED_BY(lock_);
DISALLOW_COPY_AND_ASSIGN(SequenceCheckerImpl); DISALLOW_COPY_AND_ASSIGN(SequenceCheckerImpl);
}; };

View File

@ -152,6 +152,19 @@ class BASE_EXPORT SequencedTaskRunner : public TaskRunner {
object.release()); object.release());
} }
// Returns true iff tasks posted to this TaskRunner are sequenced
// with this call.
//
// In particular:
// - Returns true if this is a SequencedTaskRunner to which the
// current task was posted.
// - Returns true if this is a SequencedTaskRunner bound to the
// same sequence as the SequencedTaskRunner to which the current
// task was posted.
// - Returns true if this is a SingleThreadTaskRunner bound to
// the current thread.
virtual bool RunsTasksInCurrentSequence() const = 0;
protected: protected:
~SequencedTaskRunner() override = default; ~SequencedTaskRunner() override = default;

View File

@ -17,12 +17,15 @@
#include <map> #include <map>
#include <set> #include <set>
#include <string> #include <string>
#include <type_traits>
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
#include <utility>
#include <vector> #include <vector>
#include "base/logging.h" #include "base/logging.h"
#include "base/optional.h" #include "base/optional.h"
#include "base/template_util.h"
namespace base { namespace base {
@ -39,6 +42,43 @@ void IterateAndEraseIf(Container& container, Predicate pred) {
} }
} }
template <typename Iter>
constexpr bool IsRandomAccessIter =
std::is_same<typename std::iterator_traits<Iter>::iterator_category,
std::random_access_iterator_tag>::value;
// Utility type traits used for specializing base::Contains() below.
template <typename Container, typename Element, typename = void>
struct HasFindWithNpos : std::false_type {};
template <typename Container, typename Element>
struct HasFindWithNpos<
Container,
Element,
void_t<decltype(std::declval<const Container&>().find(
std::declval<const Element&>()) != Container::npos)>>
: std::true_type {};
template <typename Container, typename Element, typename = void>
struct HasFindWithEnd : std::false_type {};
template <typename Container, typename Element>
struct HasFindWithEnd<Container,
Element,
void_t<decltype(std::declval<const Container&>().find(
std::declval<const Element&>()) !=
std::declval<const Container&>().end())>>
: std::true_type {};
template <typename Container, typename Element, typename = void>
struct HasContains : std::false_type {};
template <typename Container, typename Element>
struct HasContains<Container,
Element,
void_t<decltype(std::declval<const Container&>().contains(
std::declval<const Element&>()))>> : std::true_type {};
} // namespace internal } // namespace internal
// C++14 implementation of C++17's std::size(): // C++14 implementation of C++17's std::size():
@ -104,6 +144,30 @@ constexpr const T* data(std::initializer_list<T> il) noexcept {
return il.begin(); return il.begin();
} }
// std::array::data() was not constexpr prior to C++17 [1].
// Hence these overloads are provided.
//
// [1] https://en.cppreference.com/w/cpp/container/array/data
template <typename T, size_t N>
constexpr T* data(std::array<T, N>& array) noexcept {
return !array.empty() ? &array[0] : nullptr;
}
template <typename T, size_t N>
constexpr const T* data(const std::array<T, N>& array) noexcept {
return !array.empty() ? &array[0] : nullptr;
}
// C++14 implementation of C++17's std::as_const():
// https://en.cppreference.com/w/cpp/utility/as_const
template <typename T>
constexpr std::add_const_t<T>& as_const(T& t) noexcept {
return t;
}
template <typename T>
void as_const(const T&& t) = delete;
// Returns a const reference to the underlying container of a container adapter. // Returns a const reference to the underlying container of a container adapter.
// Works for std::priority_queue, std::queue, and std::stack. // Works for std::priority_queue, std::queue, and std::stack.
template <class A> template <class A>
@ -134,39 +198,250 @@ STLCount(const Container& container, const T& val) {
return std::count(container.begin(), container.end(), val); return std::count(container.begin(), container.end(), val);
} }
// Test to see if a set or map contains a particular key. // General purpose implementation to check if |container| contains |value|.
// Returns true if the key is in the collection. template <typename Container,
template <typename Collection, typename Key> typename Value,
bool ContainsKey(const Collection& collection, const Key& key) { std::enable_if_t<
return collection.find(key) != collection.end(); !internal::HasFindWithNpos<Container, Value>::value &&
!internal::HasFindWithEnd<Container, Value>::value &&
!internal::HasContains<Container, Value>::value>* = nullptr>
bool Contains(const Container& container, const Value& value) {
using std::begin;
using std::end;
return std::find(begin(container), end(container), value) != end(container);
}
// Specialized Contains() implementation for when |container| has a find()
// member function and a static npos member, but no contains() member function.
template <typename Container,
typename Value,
std::enable_if_t<internal::HasFindWithNpos<Container, Value>::value &&
!internal::HasContains<Container, Value>::value>* =
nullptr>
bool Contains(const Container& container, const Value& value) {
return container.find(value) != Container::npos;
}
// Specialized Contains() implementation for when |container| has a find()
// and end() member function, but no contains() member function.
template <typename Container,
typename Value,
std::enable_if_t<internal::HasFindWithEnd<Container, Value>::value &&
!internal::HasContains<Container, Value>::value>* =
nullptr>
bool Contains(const Container& container, const Value& value) {
return container.find(value) != container.end();
}
// Specialized Contains() implementation for when |container| has a contains()
// member function.
template <
typename Container,
typename Value,
std::enable_if_t<internal::HasContains<Container, Value>::value>* = nullptr>
bool Contains(const Container& container, const Value& value) {
return container.contains(value);
}
// O(1) implementation of const casting an iterator for any sequence,
// associative or unordered associative container in the STL.
//
// Reference: https://stackoverflow.com/a/10669041
template <typename Container,
typename ConstIter,
std::enable_if_t<!internal::IsRandomAccessIter<ConstIter>>* = nullptr>
constexpr auto ConstCastIterator(Container& c, ConstIter it) {
return c.erase(it, it);
}
// Explicit overload for std::forward_list where erase() is named erase_after().
template <typename T, typename Allocator>
constexpr auto ConstCastIterator(
std::forward_list<T, Allocator>& c,
typename std::forward_list<T, Allocator>::const_iterator it) {
// The erase_after(it, it) trick used below does not work for libstdc++ [1],
// thus we need a different way.
// TODO(crbug.com/972541): Remove this workaround once libstdc++ is fixed on all
// platforms.
//
// [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90857
#if defined(__GLIBCXX__)
return c.insert_after(it, {});
#else
return c.erase_after(it, it);
#endif
}
// Specialized O(1) const casting for random access iterators. This is
// necessary, because erase() is either not available (e.g. array-like
// containers), or has O(n) complexity (e.g. std::deque or std::vector).
template <typename Container,
typename ConstIter,
std::enable_if_t<internal::IsRandomAccessIter<ConstIter>>* = nullptr>
constexpr auto ConstCastIterator(Container& c, ConstIter it) {
using std::begin;
using std::cbegin;
return begin(c) + (it - cbegin(c));
} }
namespace internal { namespace internal {
template <typename Collection> template <typename Map, typename Key, typename Value>
class HasKeyType { std::pair<typename Map::iterator, bool> InsertOrAssignImpl(Map& map,
template <typename C> Key&& key,
static std::true_type test(typename C::key_type*); Value&& value) {
template <typename C> auto lower = map.lower_bound(key);
static std::false_type test(...); if (lower != map.end() && !map.key_comp()(key, lower->first)) {
// key already exists, perform assignment.
lower->second = std::forward<Value>(value);
return {lower, false};
}
public: // key did not yet exist, insert it.
static constexpr bool value = decltype(test<Collection>(nullptr))::value; return {map.emplace_hint(lower, std::forward<Key>(key),
}; std::forward<Value>(value)),
true};
}
template <typename Map, typename Key, typename Value>
typename Map::iterator InsertOrAssignImpl(Map& map,
typename Map::const_iterator hint,
Key&& key,
Value&& value) {
auto&& key_comp = map.key_comp();
if ((hint == map.begin() || key_comp(std::prev(hint)->first, key))) {
if (hint == map.end() || key_comp(key, hint->first)) {
// *(hint - 1) < key < *hint => key did not exist and hint is correct.
return map.emplace_hint(hint, std::forward<Key>(key),
std::forward<Value>(value));
}
if (!key_comp(hint->first, key)) {
// key == *hint => key already exists and hint is correct.
auto mutable_hint = ConstCastIterator(map, hint);
mutable_hint->second = std::forward<Value>(value);
return mutable_hint;
}
}
// hint was not helpful, dispatch to hintless version.
return InsertOrAssignImpl(map, std::forward<Key>(key),
std::forward<Value>(value))
.first;
}
template <typename Map, typename Key, typename... Args>
std::pair<typename Map::iterator, bool> TryEmplaceImpl(Map& map,
Key&& key,
Args&&... args) {
auto lower = map.lower_bound(key);
if (lower != map.end() && !map.key_comp()(key, lower->first)) {
// key already exists, do nothing.
return {lower, false};
}
// key did not yet exist, insert it.
return {map.emplace_hint(lower, std::piecewise_construct,
std::forward_as_tuple(std::forward<Key>(key)),
std::forward_as_tuple(std::forward<Args>(args)...)),
true};
}
template <typename Map, typename Key, typename... Args>
typename Map::iterator TryEmplaceImpl(Map& map,
typename Map::const_iterator hint,
Key&& key,
Args&&... args) {
auto&& key_comp = map.key_comp();
if ((hint == map.begin() || key_comp(std::prev(hint)->first, key))) {
if (hint == map.end() || key_comp(key, hint->first)) {
// *(hint - 1) < key < *hint => key did not exist and hint is correct.
return map.emplace_hint(
hint, std::piecewise_construct,
std::forward_as_tuple(std::forward<Key>(key)),
std::forward_as_tuple(std::forward<Args>(args)...));
}
if (!key_comp(hint->first, key)) {
// key == *hint => no-op, return correct hint.
return ConstCastIterator(map, hint);
}
}
// hint was not helpful, dispatch to hintless version.
return TryEmplaceImpl(map, std::forward<Key>(key),
std::forward<Args>(args)...)
.first;
}
} // namespace internal } // namespace internal
// Test to see if a collection like a vector contains a particular value. // Implementation of C++17's std::map::insert_or_assign as a free function.
// Returns true if the value is in the collection. template <typename Map, typename Value>
// Don't use this on collections such as sets or maps. This is enforced by std::pair<typename Map::iterator, bool>
// disabling this method if the collection defines a key_type. InsertOrAssign(Map& map, const typename Map::key_type& key, Value&& value) {
template <typename Collection, return internal::InsertOrAssignImpl(map, key, std::forward<Value>(value));
typename Value, }
typename std::enable_if<!internal::HasKeyType<Collection>::value,
int>::type = 0> template <typename Map, typename Value>
bool ContainsValue(const Collection& collection, const Value& value) { std::pair<typename Map::iterator, bool>
return std::find(std::begin(collection), std::end(collection), value) != InsertOrAssign(Map& map, typename Map::key_type&& key, Value&& value) {
std::end(collection); return internal::InsertOrAssignImpl(map, std::move(key),
std::forward<Value>(value));
}
// Implementation of C++17's std::map::insert_or_assign with hint as a free
// function.
template <typename Map, typename Value>
typename Map::iterator InsertOrAssign(Map& map,
typename Map::const_iterator hint,
const typename Map::key_type& key,
Value&& value) {
return internal::InsertOrAssignImpl(map, hint, key,
std::forward<Value>(value));
}
template <typename Map, typename Value>
typename Map::iterator InsertOrAssign(Map& map,
typename Map::const_iterator hint,
typename Map::key_type&& key,
Value&& value) {
return internal::InsertOrAssignImpl(map, hint, std::move(key),
std::forward<Value>(value));
}
// Implementation of C++17's std::map::try_emplace as a free function.
template <typename Map, typename... Args>
std::pair<typename Map::iterator, bool>
TryEmplace(Map& map, const typename Map::key_type& key, Args&&... args) {
return internal::TryEmplaceImpl(map, key, std::forward<Args>(args)...);
}
template <typename Map, typename... Args>
std::pair<typename Map::iterator, bool> TryEmplace(Map& map,
typename Map::key_type&& key,
Args&&... args) {
return internal::TryEmplaceImpl(map, std::move(key),
std::forward<Args>(args)...);
}
// Implementation of C++17's std::map::try_emplace with hint as a free
// function.
template <typename Map, typename... Args>
typename Map::iterator TryEmplace(Map& map,
typename Map::const_iterator hint,
const typename Map::key_type& key,
Args&&... args) {
return internal::TryEmplaceImpl(map, hint, key, std::forward<Args>(args)...);
}
template <typename Map, typename... Args>
typename Map::iterator TryEmplace(Map& map,
typename Map::const_iterator hint,
typename Map::key_type&& key,
Args&&... args) {
return internal::TryEmplaceImpl(map, hint, std::move(key),
std::forward<Args>(args)...);
} }
// Returns true if the container is sorted. // Returns true if the container is sorted.

View File

@ -13,10 +13,11 @@
#include <type_traits> #include <type_traits>
#include "base/logging.h" #include "base/logging.h"
#include "base/no_destructor.h"
#include "base/numerics/safe_math.h" #include "base/numerics/safe_math.h"
#include "base/scoped_clear_last_error.h" #include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "base/third_party/dmg_fp/dmg_fp.h" #include "base/third_party/double_conversion/double-conversion/double-conversion.h"
namespace base { namespace base {
@ -360,21 +361,29 @@ string16 NumberToString16(unsigned long long value) {
return IntToStringT<string16, unsigned long long>::IntToString(value); return IntToStringT<string16, unsigned long long>::IntToString(value);
} }
static const double_conversion::DoubleToStringConverter*
GetDoubleToStringConverter() {
static NoDestructor<double_conversion::DoubleToStringConverter> converter(
double_conversion::DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN,
nullptr, nullptr, 'e', -6, 12, 0, 0);
return converter.get();
}
std::string NumberToString(double value) { std::string NumberToString(double value) {
// According to g_fmt.cc, it is sufficient to declare a buffer of size 32.
char buffer[32]; char buffer[32];
dmg_fp::g_fmt(buffer, value); double_conversion::StringBuilder builder(buffer, sizeof(buffer));
return std::string(buffer); GetDoubleToStringConverter()->ToShortest(value, &builder);
return std::string(buffer, builder.position());
} }
base::string16 NumberToString16(double value) { base::string16 NumberToString16(double value) {
// According to g_fmt.cc, it is sufficient to declare a buffer of size 32.
char buffer[32]; char buffer[32];
dmg_fp::g_fmt(buffer, value); double_conversion::StringBuilder builder(buffer, sizeof(buffer));
GetDoubleToStringConverter()->ToShortest(value, &builder);
// The number will be ASCII. This creates the string using the "input // The number will be ASCII. This creates the string using the "input
// iterator" variant which promotes from 8-bit to 16-bit via "=". // iterator" variant which promotes from 8-bit to 16-bit via "=".
return base::string16(&buffer[0], &buffer[strlen(buffer)]); return base::string16(&buffer[0], &buffer[builder.position()]);
} }
bool StringToInt(StringPiece input, int* output) { bool StringToInt(StringPiece input, int* output) {
@ -417,35 +426,37 @@ bool StringToSizeT(StringPiece16 input, size_t* output) {
return String16ToIntImpl(input, output); return String16ToIntImpl(input, output);
} }
bool StringToDouble(const std::string& input, double* output) { template <typename STRING, typename CHAR>
// Thread-safe? It is on at least Mac, Linux, and Windows. bool StringToDoubleImpl(STRING input, const CHAR* data, double* output) {
internal::ScopedClearLastError clear_errno; static NoDestructor<double_conversion::StringToDoubleConverter> converter(
double_conversion::StringToDoubleConverter::ALLOW_LEADING_SPACES |
double_conversion::StringToDoubleConverter::ALLOW_TRAILING_JUNK,
0.0, 0, nullptr, nullptr);
char* endptr = nullptr; int processed_characters_count;
*output = dmg_fp::strtod(input.c_str(), &endptr); *output = converter->StringToDouble(data, input.size(),
&processed_characters_count);
// Cases to return false: // Cases to return false:
// - If errno is ERANGE, there was an overflow or underflow.
// - If the input string is empty, there was nothing to parse. // - If the input string is empty, there was nothing to parse.
// - If endptr does not point to the end of the string, there are either // - If the value saturated to HUGE_VAL.
// characters remaining in the string after a parsed number, or the string // - If the entire string was not processed, there are either characters
// does not begin with a parseable number. endptr is compared to the // remaining in the string after a parsed number, or the string does not
// expected end given the string's stated length to correctly catch cases // begin with a parseable number.
// where the string contains embedded NUL characters.
// - If the first character is a space, there was leading whitespace // - If the first character is a space, there was leading whitespace
return errno == 0 && return !input.empty() && *output != HUGE_VAL && *output != -HUGE_VAL &&
!input.empty() && static_cast<size_t>(processed_characters_count) == input.size() &&
input.c_str() + input.length() == endptr && !IsUnicodeWhitespace(input[0]);
!isspace(input[0]);
} }
// Note: if you need to add String16ToDouble, first ask yourself if it's bool StringToDouble(StringPiece input, double* output) {
// really necessary. If it is, probably the best implementation here is to return StringToDoubleImpl(input, input.data(), output);
// convert to 8-bit and then use the 8-bit version. }
// Note: if you need to add an iterator range version of StringToDouble, first bool StringToDouble(StringPiece16 input, double* output) {
// ask yourself if it's really necessary. If it is, probably the best return StringToDoubleImpl(
// implementation here is to instantiate a string and use the string version. input, reinterpret_cast<const uint16_t*>(input.data()), output);
}
std::string HexEncode(const void* bytes, size_t size) { std::string HexEncode(const void* bytes, size_t size) {
static const char kHexChars[] = "0123456789ABCDEF"; static const char kHexChars[] = "0123456789ABCDEF";
@ -461,6 +472,10 @@ std::string HexEncode(const void* bytes, size_t size) {
return ret; return ret;
} }
std::string HexEncode(base::span<const uint8_t> bytes) {
return HexEncode(bytes.data(), bytes.size());
}
bool HexStringToInt(StringPiece input, int* output) { bool HexStringToInt(StringPiece input, int* output) {
return IteratorRangeToNumber<HexIteratorRangeToIntTraits>::Invoke( return IteratorRangeToNumber<HexIteratorRangeToIntTraits>::Invoke(
input.begin(), input.end(), output); input.begin(), input.end(), output);
@ -481,7 +496,8 @@ bool HexStringToUInt64(StringPiece input, uint64_t* output) {
input.begin(), input.end(), output); input.begin(), input.end(), output);
} }
bool HexStringToBytes(StringPiece input, std::vector<uint8_t>* output) { template <typename Container>
static bool HexStringToByteContainer(StringPiece input, Container* output) {
DCHECK_EQ(output->size(), 0u); DCHECK_EQ(output->size(), 0u);
size_t count = input.size(); size_t count = input.size();
if (count == 0 || (count % 2) != 0) if (count == 0 || (count % 2) != 0)
@ -498,4 +514,32 @@ bool HexStringToBytes(StringPiece input, std::vector<uint8_t>* output) {
return true; return true;
} }
bool HexStringToBytes(StringPiece input, std::vector<uint8_t>* output) {
return HexStringToByteContainer(input, output);
}
bool HexStringToString(StringPiece input, std::string* output) {
return HexStringToByteContainer(input, output);
}
bool HexStringToSpan(StringPiece input, base::span<uint8_t> output) {
size_t count = input.size();
if (count == 0 || (count % 2) != 0)
return false;
if (count / 2 != output.size())
return false;
for (uintptr_t i = 0; i < count / 2; ++i) {
uint8_t msb = 0; // most significant 4 bits
uint8_t lsb = 0; // least significant 4 bits
if (!CharToDigit<16>(input[i * 2], &msb) ||
!CharToDigit<16>(input[i * 2 + 1], &lsb)) {
return false;
}
output[i] = (msb << 4) | lsb;
}
return true;
}
} // namespace base } // namespace base

View File

@ -12,6 +12,7 @@
#include <vector> #include <vector>
#include "base/base_export.h" #include "base/base_export.h"
#include "base/containers/span.h"
#include "base/strings/string16.h" #include "base/strings/string16.h"
#include "base/strings/string_piece.h" #include "base/strings/string_piece.h"
#include "build/build_config.h" #include "build/build_config.h"
@ -56,29 +57,6 @@ BASE_EXPORT string16 NumberToString16(unsigned long long value);
BASE_EXPORT std::string NumberToString(double value); BASE_EXPORT std::string NumberToString(double value);
BASE_EXPORT string16 NumberToString16(double value); BASE_EXPORT string16 NumberToString16(double value);
// Type-specific naming for backwards compatibility.
//
// TODO(brettw) these should be removed and callers converted to the overloaded
// "NumberToString" variant.
inline std::string IntToString(int value) {
return NumberToString(value);
}
inline string16 IntToString16(int value) {
return NumberToString16(value);
}
inline std::string UintToString(unsigned value) {
return NumberToString(value);
}
inline string16 UintToString16(unsigned value) {
return NumberToString16(value);
}
inline std::string Int64ToString(int64_t value) {
return NumberToString(value);
}
inline string16 Int64ToString16(int64_t value) {
return NumberToString16(value);
}
// String -> number conversions ------------------------------------------------ // String -> number conversions ------------------------------------------------
// Perform a best-effort conversion of the input string to a numeric type, // Perform a best-effort conversion of the input string to a numeric type,
@ -120,7 +98,8 @@ BASE_EXPORT bool StringToSizeT(StringPiece16 input, size_t* output);
// If your input is locale specific, use ICU to read the number. // If your input is locale specific, use ICU to read the number.
// WARNING: Will write to |output| even when returning false. // WARNING: Will write to |output| even when returning false.
// Read the comments here and above StringToInt() carefully. // Read the comments here and above StringToInt() carefully.
BASE_EXPORT bool StringToDouble(const std::string& input, double* output); BASE_EXPORT bool StringToDouble(StringPiece input, double* output);
BASE_EXPORT bool StringToDouble(StringPiece16 input, double* output);
// Hex encoding ---------------------------------------------------------------- // Hex encoding ----------------------------------------------------------------
@ -131,6 +110,7 @@ BASE_EXPORT bool StringToDouble(const std::string& input, double* output);
// max size for |size| should be is // max size for |size| should be is
// std::numeric_limits<size_t>::max() / 2 // std::numeric_limits<size_t>::max() / 2
BASE_EXPORT std::string HexEncode(const void* bytes, size_t size); BASE_EXPORT std::string HexEncode(const void* bytes, size_t size);
BASE_EXPORT std::string HexEncode(base::span<const uint8_t> bytes);
// Best effort conversion, see StringToInt above for restrictions. // Best effort conversion, see StringToInt above for restrictions.
// Will only successful parse hex values that will fit into |output|, i.e. // Will only successful parse hex values that will fit into |output|, i.e.
@ -161,6 +141,17 @@ BASE_EXPORT bool HexStringToUInt64(StringPiece input, uint64_t* output);
BASE_EXPORT bool HexStringToBytes(StringPiece input, BASE_EXPORT bool HexStringToBytes(StringPiece input,
std::vector<uint8_t>* output); std::vector<uint8_t>* output);
// Same as HexStringToBytes, but for an std::string.
BASE_EXPORT bool HexStringToString(StringPiece input, std::string* output);
// Decodes the hex string |input| into a presized |output|. The output buffer
// must be sized exactly to |input.size() / 2| or decoding will fail and no
// bytes will be written to |output|. Decoding an empty input is also
// considered a failure. When decoding fails due to encountering invalid input
// characters, |output| will have been filled with the decoded bytes up until
// the failure.
BASE_EXPORT bool HexStringToSpan(StringPiece input, base::span<uint8_t> output);
} // namespace base } // namespace base
#endif // BASE_STRINGS_STRING_NUMBER_CONVERSIONS_H_ #endif // BASE_STRINGS_STRING_NUMBER_CONVERSIONS_H_

View File

@ -52,22 +52,6 @@ std::ostream& operator<<(std::ostream& o, const StringPiece16& piece) {
namespace internal { namespace internal {
template<typename STR>
void CopyToStringT(const BasicStringPiece<STR>& self, STR* target) {
if (self.empty())
target->clear();
else
target->assign(self.data(), self.size());
}
void CopyToString(const StringPiece& self, std::string* target) {
CopyToStringT(self, target);
}
void CopyToString(const StringPiece16& self, string16* target) {
CopyToStringT(self, target);
}
template<typename STR> template<typename STR>
void AppendToStringT(const BasicStringPiece<STR>& self, STR* target) { void AppendToStringT(const BasicStringPiece<STR>& self, STR* target) {
if (!self.empty()) if (!self.empty())
@ -219,8 +203,11 @@ size_t find_first_of(const StringPiece& self,
size_t find_first_of(const StringPiece16& self, size_t find_first_of(const StringPiece16& self,
const StringPiece16& s, const StringPiece16& s,
size_t pos) { size_t pos) {
// Use the faster std::find() if searching for a single character.
StringPiece16::const_iterator found = StringPiece16::const_iterator found =
std::find_first_of(self.begin() + pos, self.end(), s.begin(), s.end()); s.size() == 1 ? std::find(self.begin() + pos, self.end(), s[0])
: std::find_first_of(self.begin() + pos, self.end(),
s.begin(), s.end());
if (found == self.end()) if (found == self.end())
return StringPiece16::npos; return StringPiece16::npos;
return found - self.begin(); return found - self.begin();
@ -435,16 +422,5 @@ StringPiece16 substr(const StringPiece16& self,
return substrT(self, pos, n); return substrT(self, pos, n);
} }
#if DCHECK_IS_ON()
void AssertIteratorsInOrder(std::string::const_iterator begin,
std::string::const_iterator end) {
DCHECK(begin <= end) << "StringPiece iterators swapped or invalid.";
}
void AssertIteratorsInOrder(string16::const_iterator begin,
string16::const_iterator end) {
DCHECK(begin <= end) << "StringPiece iterators swapped or invalid.";
}
#endif
} // namespace internal } // namespace internal
} // namespace base } // namespace base

View File

@ -47,9 +47,6 @@ namespace base {
// template internal to the .cc file. // template internal to the .cc file.
namespace internal { namespace internal {
BASE_EXPORT void CopyToString(const StringPiece& self, std::string* target);
BASE_EXPORT void CopyToString(const StringPiece16& self, string16* target);
BASE_EXPORT void AppendToString(const StringPiece& self, std::string* target); BASE_EXPORT void AppendToString(const StringPiece& self, std::string* target);
BASE_EXPORT void AppendToString(const StringPiece16& self, string16* target); BASE_EXPORT void AppendToString(const StringPiece16& self, string16* target);
@ -141,21 +138,12 @@ BASE_EXPORT StringPiece16 substr(const StringPiece16& self,
size_t pos, size_t pos,
size_t n); size_t n);
#if DCHECK_IS_ON()
// Asserts that begin <= end to catch some errors with iterator usage.
BASE_EXPORT void AssertIteratorsInOrder(std::string::const_iterator begin,
std::string::const_iterator end);
BASE_EXPORT void AssertIteratorsInOrder(string16::const_iterator begin,
string16::const_iterator end);
#endif
} // namespace internal } // namespace internal
// BasicStringPiece ------------------------------------------------------------ // BasicStringPiece ------------------------------------------------------------
// Defines the types, methods, operators, and data members common to both // Defines the types, methods, operators, and data members common to both
// StringPiece and StringPiece16. Do not refer to this class directly, but // StringPiece and StringPiece16.
// rather to BasicStringPiece, StringPiece, or StringPiece16.
// //
// This is templatized by string class type rather than character type, so // This is templatized by string class type rather than character type, so
// BasicStringPiece<std::string> or BasicStringPiece<base::string16>. // BasicStringPiece<std::string> or BasicStringPiece<base::string16>.
@ -190,11 +178,7 @@ template <typename STRING_TYPE> class BasicStringPiece {
: ptr_(offset), length_(len) {} : ptr_(offset), length_(len) {}
BasicStringPiece(const typename STRING_TYPE::const_iterator& begin, BasicStringPiece(const typename STRING_TYPE::const_iterator& begin,
const typename STRING_TYPE::const_iterator& end) { const typename STRING_TYPE::const_iterator& end) {
#if DCHECK_IS_ON() DCHECK(begin <= end) << "StringPiece iterators swapped or invalid.";
// This assertion is done out-of-line to avoid bringing in logging.h and
// instantiating logging macros for every instantiation.
internal::AssertIteratorsInOrder(begin, end);
#endif
length_ = static_cast<size_t>(std::distance(begin, end)); length_ = static_cast<size_t>(std::distance(begin, end));
// The length test before assignment is to avoid dereferencing an iterator // The length test before assignment is to avoid dereferencing an iterator
@ -211,19 +195,6 @@ template <typename STRING_TYPE> class BasicStringPiece {
constexpr size_type length() const noexcept { return length_; } constexpr size_type length() const noexcept { return length_; }
bool empty() const { return length_ == 0; } bool empty() const { return length_ == 0; }
void clear() {
ptr_ = NULL;
length_ = 0;
}
void set(const value_type* data, size_type len) {
ptr_ = data;
length_ = len;
}
void set(const value_type* str) {
ptr_ = str;
length_ = str ? STRING_TYPE::traits_type::length(str) : 0;
}
constexpr value_type operator[](size_type i) const { constexpr value_type operator[](size_type i) const {
CHECK(i < length_); CHECK(i < length_);
return ptr_[i]; return ptr_[i];
@ -280,12 +251,6 @@ template <typename STRING_TYPE> class BasicStringPiece {
size_type max_size() const { return length_; } size_type max_size() const { return length_; }
size_type capacity() const { return length_; } size_type capacity() const { return length_; }
// Sets the value of the given string target type to be the current string.
// This saves a temporary over doing |a = b.as_string()|
void CopyToString(STRING_TYPE* target) const {
internal::CopyToString(*this, target);
}
void AppendToString(STRING_TYPE* target) const { void AppendToString(STRING_TYPE* target) const {
internal::AppendToString(*this, target); internal::AppendToString(*this, target);
} }

View File

@ -14,27 +14,15 @@ namespace base {
namespace { namespace {
// PieceToOutputType converts a StringPiece as needed to a given output type,
// which is either the same type of StringPiece (a NOP) or the corresponding
// non-piece string type.
//
// The default converter is a NOP, it works when the OutputType is the
// correct StringPiece.
template<typename Str, typename OutputType>
OutputType PieceToOutputType(BasicStringPiece<Str> piece) {
return piece;
}
template<> // Convert StringPiece to std::string
std::string PieceToOutputType<std::string, std::string>(StringPiece piece) {
return piece.as_string();
}
template<> // Convert StringPiece16 to string16.
string16 PieceToOutputType<string16, string16>(StringPiece16 piece) {
return piece.as_string();
}
// Returns either the ASCII or UTF-16 whitespace. // Returns either the ASCII or UTF-16 whitespace.
template<typename Str> BasicStringPiece<Str> WhitespaceForType(); template<typename Str> BasicStringPiece<Str> WhitespaceForType();
#if defined(OS_WIN) && defined(BASE_STRING16_IS_STD_U16STRING)
template <>
WStringPiece WhitespaceForType<std::wstring>() {
return kWhitespaceWide;
}
#endif
template<> StringPiece16 WhitespaceForType<string16>() { template<> StringPiece16 WhitespaceForType<string16>() {
return kWhitespaceUTF16; return kWhitespaceUTF16;
} }
@ -42,37 +30,12 @@ template<> StringPiece WhitespaceForType<std::string>() {
return kWhitespaceASCII; return kWhitespaceASCII;
} }
// Optimize the single-character case to call find() on the string instead,
// since this is the common case and can be made faster. This could have been
// done with template specialization too, but would have been less clear.
//
// There is no corresponding FindFirstNotOf because StringPiece already
// implements these different versions that do the optimized searching.
size_t FindFirstOf(StringPiece piece, char c, size_t pos) {
return piece.find(c, pos);
}
size_t FindFirstOf(StringPiece16 piece, char16 c, size_t pos) {
return piece.find(c, pos);
}
size_t FindFirstOf(StringPiece piece, StringPiece one_of, size_t pos) {
return piece.find_first_of(one_of, pos);
}
size_t FindFirstOf(StringPiece16 piece, StringPiece16 one_of, size_t pos) {
return piece.find_first_of(one_of, pos);
}
// General string splitter template. Can take 8- or 16-bit input, can produce // General string splitter template. Can take 8- or 16-bit input, can produce
// the corresponding string or StringPiece output, and can take single- or // the corresponding string or StringPiece output.
// multiple-character delimiters. template <typename OutputStringType, typename Str>
//
// DelimiterType is either a character (Str::value_type) or a string piece of
// multiple characters (BasicStringPiece<Str>). StringPiece has a version of
// find for both of these cases, and the single-character version is the most
// common and can be implemented faster, which is why this is a template.
template<typename Str, typename OutputStringType, typename DelimiterType>
static std::vector<OutputStringType> SplitStringT( static std::vector<OutputStringType> SplitStringT(
BasicStringPiece<Str> str, BasicStringPiece<Str> str,
DelimiterType delimiter, BasicStringPiece<Str> delimiter,
WhitespaceHandling whitespace, WhitespaceHandling whitespace,
SplitResult result_type) { SplitResult result_type) {
std::vector<OutputStringType> result; std::vector<OutputStringType> result;
@ -81,7 +44,7 @@ static std::vector<OutputStringType> SplitStringT(
size_t start = 0; size_t start = 0;
while (start != Str::npos) { while (start != Str::npos) {
size_t end = FindFirstOf(str, delimiter, start); size_t end = str.find_first_of(delimiter, start);
BasicStringPiece<Str> piece; BasicStringPiece<Str> piece;
if (end == Str::npos) { if (end == Str::npos) {
@ -96,7 +59,7 @@ static std::vector<OutputStringType> SplitStringT(
piece = TrimString(piece, WhitespaceForType<Str>(), TRIM_ALL); piece = TrimString(piece, WhitespaceForType<Str>(), TRIM_ALL);
if (result_type == SPLIT_WANT_ALL || !piece.empty()) if (result_type == SPLIT_WANT_ALL || !piece.empty())
result.push_back(PieceToOutputType<Str, OutputStringType>(piece)); result.emplace_back(piece);
} }
return result; return result;
} }
@ -115,7 +78,7 @@ bool AppendStringKeyValue(StringPiece input,
DVLOG(1) << "cannot find delimiter in: " << input; DVLOG(1) << "cannot find delimiter in: " << input;
return false; // No delimiter. return false; // No delimiter.
} }
input.substr(0, end_key_pos).CopyToString(&result_pair.first); result_pair.first = std::string(input.substr(0, end_key_pos));
// Find the value string. // Find the value string.
StringPiece remains = input.substr(end_key_pos, input.size() - end_key_pos); StringPiece remains = input.substr(end_key_pos, input.size() - end_key_pos);
@ -124,22 +87,23 @@ bool AppendStringKeyValue(StringPiece input,
DVLOG(1) << "cannot parse value from input: " << input; DVLOG(1) << "cannot parse value from input: " << input;
return false; // No value. return false; // No value.
} }
remains.substr(begin_value_pos, remains.size() - begin_value_pos)
.CopyToString(&result_pair.second); result_pair.second = std::string(
remains.substr(begin_value_pos, remains.size() - begin_value_pos));
return true; return true;
} }
template <typename Str, typename OutputStringType> template <typename OutputStringType, typename Str>
void SplitStringUsingSubstrT(BasicStringPiece<Str> input, std::vector<OutputStringType> SplitStringUsingSubstrT(
BasicStringPiece<Str> delimiter, BasicStringPiece<Str> input,
WhitespaceHandling whitespace, BasicStringPiece<Str> delimiter,
SplitResult result_type, WhitespaceHandling whitespace,
std::vector<OutputStringType>* result) { SplitResult result_type) {
using Piece = BasicStringPiece<Str>; using Piece = BasicStringPiece<Str>;
using size_type = typename Piece::size_type; using size_type = typename Piece::size_type;
result->clear(); std::vector<OutputStringType> result;
for (size_type begin_index = 0, end_index = 0; end_index != Piece::npos; for (size_type begin_index = 0, end_index = 0; end_index != Piece::npos;
begin_index = end_index + delimiter.size()) { begin_index = end_index + delimiter.size()) {
end_index = input.find(delimiter, begin_index); end_index = input.find(delimiter, begin_index);
@ -151,8 +115,10 @@ void SplitStringUsingSubstrT(BasicStringPiece<Str> input,
term = TrimString(term, WhitespaceForType<Str>(), TRIM_ALL); term = TrimString(term, WhitespaceForType<Str>(), TRIM_ALL);
if (result_type == SPLIT_WANT_ALL || !term.empty()) if (result_type == SPLIT_WANT_ALL || !term.empty())
result->push_back(PieceToOutputType<Str, OutputStringType>(term)); result.emplace_back(term);
} }
return result;
} }
} // namespace } // namespace
@ -161,48 +127,29 @@ std::vector<std::string> SplitString(StringPiece input,
StringPiece separators, StringPiece separators,
WhitespaceHandling whitespace, WhitespaceHandling whitespace,
SplitResult result_type) { SplitResult result_type) {
if (separators.size() == 1) { return SplitStringT<std::string>(input, separators, whitespace, result_type);
return SplitStringT<std::string, std::string, char>(
input, separators[0], whitespace, result_type);
}
return SplitStringT<std::string, std::string, StringPiece>(
input, separators, whitespace, result_type);
} }
std::vector<string16> SplitString(StringPiece16 input, std::vector<string16> SplitString(StringPiece16 input,
StringPiece16 separators, StringPiece16 separators,
WhitespaceHandling whitespace, WhitespaceHandling whitespace,
SplitResult result_type) { SplitResult result_type) {
if (separators.size() == 1) { return SplitStringT<string16>(input, separators, whitespace, result_type);
return SplitStringT<string16, string16, char16>(
input, separators[0], whitespace, result_type);
}
return SplitStringT<string16, string16, StringPiece16>(
input, separators, whitespace, result_type);
} }
std::vector<StringPiece> SplitStringPiece(StringPiece input, std::vector<StringPiece> SplitStringPiece(StringPiece input,
StringPiece separators, StringPiece separators,
WhitespaceHandling whitespace, WhitespaceHandling whitespace,
SplitResult result_type) { SplitResult result_type) {
if (separators.size() == 1) { return SplitStringT<StringPiece>(input, separators, whitespace, result_type);
return SplitStringT<std::string, StringPiece, char>(
input, separators[0], whitespace, result_type);
}
return SplitStringT<std::string, StringPiece, StringPiece>(
input, separators, whitespace, result_type);
} }
std::vector<StringPiece16> SplitStringPiece(StringPiece16 input, std::vector<StringPiece16> SplitStringPiece(StringPiece16 input,
StringPiece16 separators, StringPiece16 separators,
WhitespaceHandling whitespace, WhitespaceHandling whitespace,
SplitResult result_type) { SplitResult result_type) {
if (separators.size() == 1) { return SplitStringT<StringPiece16>(input, separators, whitespace,
return SplitStringT<string16, StringPiece16, char16>( result_type);
input, separators[0], whitespace, result_type);
}
return SplitStringT<string16, StringPiece16, StringPiece16>(
input, separators, whitespace, result_type);
} }
bool SplitStringIntoKeyValuePairs(StringPiece input, bool SplitStringIntoKeyValuePairs(StringPiece input,
@ -240,18 +187,16 @@ std::vector<string16> SplitStringUsingSubstr(StringPiece16 input,
StringPiece16 delimiter, StringPiece16 delimiter,
WhitespaceHandling whitespace, WhitespaceHandling whitespace,
SplitResult result_type) { SplitResult result_type) {
std::vector<string16> result; return SplitStringUsingSubstrT<string16>(input, delimiter, whitespace,
SplitStringUsingSubstrT(input, delimiter, whitespace, result_type, &result); result_type);
return result;
} }
std::vector<std::string> SplitStringUsingSubstr(StringPiece input, std::vector<std::string> SplitStringUsingSubstr(StringPiece input,
StringPiece delimiter, StringPiece delimiter,
WhitespaceHandling whitespace, WhitespaceHandling whitespace,
SplitResult result_type) { SplitResult result_type) {
std::vector<std::string> result; return SplitStringUsingSubstrT<std::string>(input, delimiter, whitespace,
SplitStringUsingSubstrT(input, delimiter, whitespace, result_type, &result); result_type);
return result;
} }
std::vector<StringPiece16> SplitStringPieceUsingSubstr( std::vector<StringPiece16> SplitStringPieceUsingSubstr(
@ -260,8 +205,8 @@ std::vector<StringPiece16> SplitStringPieceUsingSubstr(
WhitespaceHandling whitespace, WhitespaceHandling whitespace,
SplitResult result_type) { SplitResult result_type) {
std::vector<StringPiece16> result; std::vector<StringPiece16> result;
SplitStringUsingSubstrT(input, delimiter, whitespace, result_type, &result); return SplitStringUsingSubstrT<StringPiece16>(input, delimiter, whitespace,
return result; result_type);
} }
std::vector<StringPiece> SplitStringPieceUsingSubstr( std::vector<StringPiece> SplitStringPieceUsingSubstr(
@ -269,9 +214,41 @@ std::vector<StringPiece> SplitStringPieceUsingSubstr(
StringPiece delimiter, StringPiece delimiter,
WhitespaceHandling whitespace, WhitespaceHandling whitespace,
SplitResult result_type) { SplitResult result_type) {
std::vector<StringPiece> result; return SplitStringUsingSubstrT<StringPiece>(input, delimiter, whitespace,
SplitStringUsingSubstrT(input, delimiter, whitespace, result_type, &result); result_type);
return result;
} }
#if defined(OS_WIN) && defined(BASE_STRING16_IS_STD_U16STRING)
std::vector<std::wstring> SplitString(WStringPiece input,
WStringPiece separators,
WhitespaceHandling whitespace,
SplitResult result_type) {
return SplitStringT<std::wstring>(input, separators, whitespace, result_type);
}
std::vector<WStringPiece> SplitStringPiece(WStringPiece input,
WStringPiece separators,
WhitespaceHandling whitespace,
SplitResult result_type) {
return SplitStringT<WStringPiece>(input, separators, whitespace, result_type);
}
std::vector<std::wstring> SplitStringUsingSubstr(WStringPiece input,
WStringPiece delimiter,
WhitespaceHandling whitespace,
SplitResult result_type) {
return SplitStringUsingSubstrT<std::wstring>(input, delimiter, whitespace,
result_type);
}
std::vector<WStringPiece> SplitStringPieceUsingSubstr(
WStringPiece input,
WStringPiece delimiter,
WhitespaceHandling whitespace,
SplitResult result_type) {
return SplitStringUsingSubstrT<WStringPiece>(input, delimiter, whitespace,
result_type);
}
#endif
} // namespace base } // namespace base

View File

@ -12,6 +12,7 @@
#include "base/base_export.h" #include "base/base_export.h"
#include "base/strings/string16.h" #include "base/strings/string16.h"
#include "base/strings/string_piece.h" #include "base/strings/string_piece.h"
#include "build/build_config.h"
namespace base { namespace base {
@ -39,26 +40,31 @@ enum SplitResult {
// Split the given string on ANY of the given separators, returning copies of // Split the given string on ANY of the given separators, returning copies of
// the result. // the result.
// //
// Note this is inverse of JoinString() defined in string_util.h.
//
// To split on either commas or semicolons, keeping all whitespace: // To split on either commas or semicolons, keeping all whitespace:
// //
// std::vector<std::string> tokens = base::SplitString( // std::vector<std::string> tokens = base::SplitString(
// input, ",;", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); // input, ", WARN_UNUSED_RESULT;", base::KEEP_WHITESPACE,
BASE_EXPORT std::vector<std::string> SplitString( // base::SPLIT_WANT_ALL) WARN_UNUSED_RESULT;
StringPiece input, BASE_EXPORT std::vector<std::string> SplitString(StringPiece input,
StringPiece separators, StringPiece separators,
WhitespaceHandling whitespace, WhitespaceHandling whitespace,
SplitResult result_type); SplitResult result_type)
BASE_EXPORT std::vector<string16> SplitString( WARN_UNUSED_RESULT;
StringPiece16 input, BASE_EXPORT std::vector<string16> SplitString(StringPiece16 input,
StringPiece16 separators, StringPiece16 separators,
WhitespaceHandling whitespace, WhitespaceHandling whitespace,
SplitResult result_type); SplitResult result_type)
WARN_UNUSED_RESULT;
// Like SplitString above except it returns a vector of StringPieces which // Like SplitString above except it returns a vector of StringPieces which
// reference the original buffer without copying. Although you have to be // reference the original buffer without copying. Although you have to be
// careful to keep the original string unmodified, this provides an efficient // careful to keep the original string unmodified, this provides an efficient
// way to iterate through tokens in a string. // way to iterate through tokens in a string.
// //
// Note this is inverse of JoinString() defined in string_util.h.
//
// To iterate through all whitespace-separated tokens in an input string: // To iterate through all whitespace-separated tokens in an input string:
// //
// for (const auto& cur : // for (const auto& cur :
@ -70,12 +76,12 @@ BASE_EXPORT std::vector<StringPiece> SplitStringPiece(
StringPiece input, StringPiece input,
StringPiece separators, StringPiece separators,
WhitespaceHandling whitespace, WhitespaceHandling whitespace,
SplitResult result_type); SplitResult result_type) WARN_UNUSED_RESULT;
BASE_EXPORT std::vector<StringPiece16> SplitStringPiece( BASE_EXPORT std::vector<StringPiece16> SplitStringPiece(
StringPiece16 input, StringPiece16 input,
StringPiece16 separators, StringPiece16 separators,
WhitespaceHandling whitespace, WhitespaceHandling whitespace,
SplitResult result_type); SplitResult result_type) WARN_UNUSED_RESULT;
using StringPairs = std::vector<std::pair<std::string, std::string>>; using StringPairs = std::vector<std::pair<std::string, std::string>>;
@ -102,12 +108,12 @@ BASE_EXPORT std::vector<string16> SplitStringUsingSubstr(
StringPiece16 input, StringPiece16 input,
StringPiece16 delimiter, StringPiece16 delimiter,
WhitespaceHandling whitespace, WhitespaceHandling whitespace,
SplitResult result_type); SplitResult result_type) WARN_UNUSED_RESULT;
BASE_EXPORT std::vector<std::string> SplitStringUsingSubstr( BASE_EXPORT std::vector<std::string> SplitStringUsingSubstr(
StringPiece input, StringPiece input,
StringPiece delimiter, StringPiece delimiter,
WhitespaceHandling whitespace, WhitespaceHandling whitespace,
SplitResult result_type); SplitResult result_type) WARN_UNUSED_RESULT;
// Like SplitStringUsingSubstr above except it returns a vector of StringPieces // Like SplitStringUsingSubstr above except it returns a vector of StringPieces
// which reference the original buffer without copying. Although you have to be // which reference the original buffer without copying. Although you have to be
@ -125,12 +131,38 @@ BASE_EXPORT std::vector<StringPiece16> SplitStringPieceUsingSubstr(
StringPiece16 input, StringPiece16 input,
StringPiece16 delimiter, StringPiece16 delimiter,
WhitespaceHandling whitespace, WhitespaceHandling whitespace,
SplitResult result_type); SplitResult result_type) WARN_UNUSED_RESULT;
BASE_EXPORT std::vector<StringPiece> SplitStringPieceUsingSubstr( BASE_EXPORT std::vector<StringPiece> SplitStringPieceUsingSubstr(
StringPiece input, StringPiece input,
StringPiece delimiter, StringPiece delimiter,
WhitespaceHandling whitespace, WhitespaceHandling whitespace,
SplitResult result_type); SplitResult result_type) WARN_UNUSED_RESULT;
#if defined(OS_WIN) && defined(BASE_STRING16_IS_STD_U16STRING)
BASE_EXPORT std::vector<std::wstring> SplitString(WStringPiece input,
WStringPiece separators,
WhitespaceHandling whitespace,
SplitResult result_type)
WARN_UNUSED_RESULT;
BASE_EXPORT std::vector<WStringPiece> SplitStringPiece(
WStringPiece input,
WStringPiece separators,
WhitespaceHandling whitespace,
SplitResult result_type) WARN_UNUSED_RESULT;
BASE_EXPORT std::vector<std::wstring> SplitStringUsingSubstr(
WStringPiece input,
WStringPiece delimiter,
WhitespaceHandling whitespace,
SplitResult result_type) WARN_UNUSED_RESULT;
BASE_EXPORT std::vector<WStringPiece> SplitStringPieceUsingSubstr(
WStringPiece input,
WStringPiece delimiter,
WhitespaceHandling whitespace,
SplitResult result_type) WARN_UNUSED_RESULT;
#endif
} // namespace base } // namespace base

View File

@ -237,17 +237,16 @@ bool ReplaceCharsT(const StringType& input,
bool ReplaceChars(const string16& input, bool ReplaceChars(const string16& input,
StringPiece16 replace_chars, StringPiece16 replace_chars,
const string16& replace_with, StringPiece16 replace_with,
string16* output) { string16* output) {
return ReplaceCharsT(input, replace_chars, StringPiece16(replace_with), return ReplaceCharsT(input, replace_chars, replace_with, output);
output);
} }
bool ReplaceChars(const std::string& input, bool ReplaceChars(const std::string& input,
StringPiece replace_chars, StringPiece replace_chars,
const std::string& replace_with, StringPiece replace_with,
std::string* output) { std::string* output) {
return ReplaceCharsT(input, replace_chars, StringPiece(replace_with), output); return ReplaceCharsT(input, replace_chars, replace_with, output);
} }
bool RemoveChars(const string16& input, bool RemoveChars(const string16& input,
@ -262,8 +261,8 @@ bool RemoveChars(const std::string& input,
return ReplaceCharsT(input, remove_chars, StringPiece(), output); return ReplaceCharsT(input, remove_chars, StringPiece(), output);
} }
template<typename Str> template <typename Str>
TrimPositions TrimStringT(const Str& input, TrimPositions TrimStringT(BasicStringPiece<Str> input,
BasicStringPiece<Str> trim_chars, BasicStringPiece<Str> trim_chars,
TrimPositions positions, TrimPositions positions,
Str* output) { Str* output) {
@ -271,40 +270,40 @@ TrimPositions TrimStringT(const Str& input,
// a StringPiece version of input to be able to call find* on it with the // 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 // StringPiece version of trim_chars (normally the trim_chars will be a
// constant so avoid making a copy). // constant so avoid making a copy).
BasicStringPiece<Str> input_piece(input);
const size_t last_char = input.length() - 1; const size_t last_char = input.length() - 1;
const size_t first_good_char = (positions & TRIM_LEADING) ? const size_t first_good_char =
input_piece.find_first_not_of(trim_chars) : 0; (positions & TRIM_LEADING) ? input.find_first_not_of(trim_chars) : 0;
const size_t last_good_char = (positions & TRIM_TRAILING) ? const size_t last_good_char = (positions & TRIM_TRAILING)
input_piece.find_last_not_of(trim_chars) : last_char; ? input.find_last_not_of(trim_chars)
: last_char;
// When the string was all trimmed, report that we stripped off characters // When the string was all trimmed, report that we stripped off characters
// from whichever position the caller was interested in. For empty input, we // from whichever position the caller was interested in. For empty input, we
// stripped no characters, but we still need to clear |output|. // stripped no characters, but we still need to clear |output|.
if (input.empty() || if (input.empty() || first_good_char == Str::npos ||
(first_good_char == Str::npos) || (last_good_char == Str::npos)) { last_good_char == Str::npos) {
bool input_was_empty = input.empty(); // in case output == &input bool input_was_empty = input.empty(); // in case output == &input
output->clear(); output->clear();
return input_was_empty ? TRIM_NONE : positions; return input_was_empty ? TRIM_NONE : positions;
} }
// Trim. // Trim.
*output = output->assign(input.data() + first_good_char,
input.substr(first_good_char, last_good_char - first_good_char + 1); last_good_char - first_good_char + 1);
// Return where we trimmed from. // Return where we trimmed from.
return static_cast<TrimPositions>( return static_cast<TrimPositions>(
((first_good_char == 0) ? TRIM_NONE : TRIM_LEADING) | (first_good_char == 0 ? TRIM_NONE : TRIM_LEADING) |
((last_good_char == last_char) ? TRIM_NONE : TRIM_TRAILING)); (last_good_char == last_char ? TRIM_NONE : TRIM_TRAILING));
} }
bool TrimString(const string16& input, bool TrimString(StringPiece16 input,
StringPiece16 trim_chars, StringPiece16 trim_chars,
string16* output) { string16* output) {
return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE; return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
} }
bool TrimString(const std::string& input, bool TrimString(StringPiece input,
StringPiece trim_chars, StringPiece trim_chars,
std::string* output) { std::string* output) {
return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE; return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
@ -370,7 +369,7 @@ void TruncateUTF8ToByteSize(const std::string& input,
output->clear(); output->clear();
} }
TrimPositions TrimWhitespace(const string16& input, TrimPositions TrimWhitespace(StringPiece16 input,
TrimPositions positions, TrimPositions positions,
string16* output) { string16* output) {
return TrimStringT(input, StringPiece16(kWhitespaceUTF16), positions, output); return TrimStringT(input, StringPiece16(kWhitespaceUTF16), positions, output);
@ -381,7 +380,7 @@ StringPiece16 TrimWhitespace(StringPiece16 input,
return TrimStringPieceT(input, StringPiece16(kWhitespaceUTF16), positions); return TrimStringPieceT(input, StringPiece16(kWhitespaceUTF16), positions);
} }
TrimPositions TrimWhitespaceASCII(const std::string& input, TrimPositions TrimWhitespaceASCII(StringPiece input,
TrimPositions positions, TrimPositions positions,
std::string* output) { std::string* output) {
return TrimStringT(input, StringPiece(kWhitespaceASCII), positions, output); return TrimStringT(input, StringPiece(kWhitespaceASCII), positions, output);
@ -506,20 +505,29 @@ bool IsStringASCII(WStringPiece str) {
} }
#endif #endif
bool IsStringUTF8(StringPiece str) { template <bool (*Validator)(uint32_t)>
const char *src = str.data(); inline static bool DoIsStringUTF8(StringPiece str) {
const char* src = str.data();
int32_t src_len = static_cast<int32_t>(str.length()); int32_t src_len = static_cast<int32_t>(str.length());
int32_t char_index = 0; int32_t char_index = 0;
while (char_index < src_len) { while (char_index < src_len) {
int32_t code_point; int32_t code_point;
CBU8_NEXT(src, char_index, src_len, code_point); CBU8_NEXT(src, char_index, src_len, code_point);
if (!IsValidCharacter(code_point)) if (!Validator(code_point))
return false; return false;
} }
return true; return true;
} }
bool IsStringUTF8(StringPiece str) {
return DoIsStringUTF8<IsValidCharacter>(str);
}
bool IsStringUTF8AllowingNoncharacters(StringPiece str) {
return DoIsStringUTF8<IsValidCodepoint>(str);
}
// Implementation note: Normally this function will be called with a hardcoded // Implementation note: Normally this function will be called with a hardcoded
// constant for the lowercase_ascii parameter. Constructing a StringPiece from // constant for the lowercase_ascii parameter. Constructing a StringPiece from
// a C constant requires running strlen, so the result will be two passes // a C constant requires running strlen, so the result will be two passes
@ -913,7 +921,7 @@ void ReplaceSubstringsAfterOffset(std::string* str,
template <class string_type> template <class string_type>
inline typename string_type::value_type* WriteIntoT(string_type* str, inline typename string_type::value_type* WriteIntoT(string_type* str,
size_t length_with_null) { size_t length_with_null) {
DCHECK_GT(length_with_null, 1u); DCHECK_GE(length_with_null, 1u);
str->reserve(length_with_null); str->reserve(length_with_null);
str->resize(length_with_null - 1); str->resize(length_with_null - 1);
return &((*str)[0]); return &((*str)[0]);
@ -1085,6 +1093,36 @@ string16 ReplaceStringPlaceholders(const string16& format_string,
return result; return result;
} }
#if defined(OS_WIN) && defined(BASE_STRING16_IS_STD_U16STRING)
TrimPositions TrimWhitespace(WStringPiece input,
TrimPositions positions,
std::wstring* output) {
return TrimStringT(input, WStringPiece(kWhitespaceWide), positions, output);
}
WStringPiece TrimWhitespace(WStringPiece input, TrimPositions positions) {
return TrimStringPieceT(input, WStringPiece(kWhitespaceWide), positions);
}
bool TrimString(WStringPiece input,
WStringPiece trim_chars,
std::wstring* output) {
return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
}
WStringPiece TrimString(WStringPiece input,
WStringPiece trim_chars,
TrimPositions positions) {
return TrimStringPieceT(input, trim_chars, positions);
}
wchar_t* WriteInto(std::wstring* str, size_t length_with_null) {
return WriteIntoT(str, length_with_null);
}
#endif
// The following code is compatible with the OpenBSD lcpy interface. See: // The following code is compatible with the OpenBSD lcpy interface. See:
// http://www.gratisoft.us/todd/papers/strlcpy.html // http://www.gratisoft.us/todd/papers/strlcpy.html
// ftp://ftp.openbsd.org/pub/OpenBSD/src/lib/libc/string/{wcs,str}lcpy.c // ftp://ftp.openbsd.org/pub/OpenBSD/src/lib/libc/string/{wcs,str}lcpy.c

View File

@ -17,7 +17,6 @@
#include <vector> #include <vector>
#include "base/base_export.h" #include "base/base_export.h"
#include "base/bit_cast.h"
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "base/stl_util.h" #include "base/stl_util.h"
#include "base/strings/string16.h" #include "base/strings/string16.h"
@ -161,6 +160,7 @@ BASE_EXPORT const string16& EmptyString16();
// by HTML5, and don't include control characters. // by HTML5, and don't include control characters.
BASE_EXPORT extern const wchar_t kWhitespaceWide[]; // Includes Unicode. BASE_EXPORT extern const wchar_t kWhitespaceWide[]; // Includes Unicode.
BASE_EXPORT extern const char16 kWhitespaceUTF16[]; // Includes Unicode. BASE_EXPORT extern const char16 kWhitespaceUTF16[]; // Includes Unicode.
BASE_EXPORT extern const char16 kWhitespaceNoCrLfUTF16[]; // Unicode w/o CR/LF.
BASE_EXPORT extern const char kWhitespaceASCII[]; BASE_EXPORT extern const char kWhitespaceASCII[];
BASE_EXPORT extern const char16 kWhitespaceASCIIAs16[]; // No unicode. BASE_EXPORT extern const char16 kWhitespaceASCIIAs16[]; // No unicode.
@ -184,11 +184,11 @@ BASE_EXPORT bool RemoveChars(const std::string& input,
// NOTE: Safe to use the same variable for both |input| and |output|. // NOTE: Safe to use the same variable for both |input| and |output|.
BASE_EXPORT bool ReplaceChars(const string16& input, BASE_EXPORT bool ReplaceChars(const string16& input,
StringPiece16 replace_chars, StringPiece16 replace_chars,
const string16& replace_with, StringPiece16 replace_with,
string16* output); string16* output);
BASE_EXPORT bool ReplaceChars(const std::string& input, BASE_EXPORT bool ReplaceChars(const std::string& input,
StringPiece replace_chars, StringPiece replace_chars,
const std::string& replace_with, StringPiece replace_with,
std::string* output); std::string* output);
enum TrimPositions { enum TrimPositions {
@ -204,10 +204,10 @@ enum TrimPositions {
// //
// It is safe to use the same variable for both |input| and |output| (this is // It is safe to use the same variable for both |input| and |output| (this is
// the normal usage to trim in-place). // the normal usage to trim in-place).
BASE_EXPORT bool TrimString(const string16& input, BASE_EXPORT bool TrimString(StringPiece16 input,
StringPiece16 trim_chars, StringPiece16 trim_chars,
string16* output); string16* output);
BASE_EXPORT bool TrimString(const std::string& input, BASE_EXPORT bool TrimString(StringPiece input,
StringPiece trim_chars, StringPiece trim_chars,
std::string* output); std::string* output);
@ -229,37 +229,63 @@ BASE_EXPORT void TruncateUTF8ToByteSize(const std::string& input,
#if defined(WCHAR_T_IS_UTF16) #if defined(WCHAR_T_IS_UTF16)
// Utility functions to access the underlying string buffer as a wide char // Utility functions to access the underlying string buffer as a wide char
// pointer. // pointer.
//
// Note: These functions violate strict aliasing when char16 and wchar_t are
// unrelated types. We thus pass -fno-strict-aliasing to the compiler on
// non-Windows platforms [1], and rely on it being off in Clang's CL mode [2].
//
// [1] https://crrev.com/b9a0976622/build/config/compiler/BUILD.gn#244
// [2]
// https://github.com/llvm/llvm-project/blob/1e28a66/clang/lib/Driver/ToolChains/Clang.cpp#L3949
inline wchar_t* as_writable_wcstr(char16* str) { inline wchar_t* as_writable_wcstr(char16* str) {
return bit_cast<wchar_t*>(str); return reinterpret_cast<wchar_t*>(str);
} }
inline wchar_t* as_writable_wcstr(string16& str) { inline wchar_t* as_writable_wcstr(string16& str) {
return bit_cast<wchar_t*>(data(str)); return reinterpret_cast<wchar_t*>(data(str));
} }
inline const wchar_t* as_wcstr(const char16* str) { inline const wchar_t* as_wcstr(const char16* str) {
return bit_cast<const wchar_t*>(str); return reinterpret_cast<const wchar_t*>(str);
} }
inline const wchar_t* as_wcstr(StringPiece16 str) { inline const wchar_t* as_wcstr(StringPiece16 str) {
return bit_cast<const wchar_t*>(str.data()); return reinterpret_cast<const wchar_t*>(str.data());
} }
// Utility functions to access the underlying string buffer as a char16 pointer. // Utility functions to access the underlying string buffer as a char16 pointer.
inline char16* as_writable_u16cstr(wchar_t* str) { inline char16* as_writable_u16cstr(wchar_t* str) {
return bit_cast<char16*>(str); return reinterpret_cast<char16*>(str);
} }
inline char16* as_writable_u16cstr(std::wstring& str) { inline char16* as_writable_u16cstr(std::wstring& str) {
return bit_cast<char16*>(data(str)); return reinterpret_cast<char16*>(data(str));
} }
inline const char16* as_u16cstr(const wchar_t* str) { inline const char16* as_u16cstr(const wchar_t* str) {
return bit_cast<const char16*>(str); return reinterpret_cast<const char16*>(str);
} }
inline const char16* as_u16cstr(WStringPiece str) { inline const char16* as_u16cstr(WStringPiece str) {
return bit_cast<const char16*>(str.data()); return reinterpret_cast<const char16*>(str.data());
}
// Utility functions to convert between base::WStringPiece and
// base::StringPiece16.
inline WStringPiece AsWStringPiece(StringPiece16 str) {
return WStringPiece(as_wcstr(str.data()), str.size());
}
inline StringPiece16 AsStringPiece16(WStringPiece str) {
return StringPiece16(as_u16cstr(str.data()), str.size());
}
inline std::wstring AsWString(StringPiece16 str) {
return std::wstring(as_wcstr(str.data()), str.size());
}
inline string16 AsString16(WStringPiece str) {
return string16(as_u16cstr(str.data()), str.size());
} }
#endif // defined(WCHAR_T_IS_UTF16) #endif // defined(WCHAR_T_IS_UTF16)
@ -270,12 +296,12 @@ inline const char16* as_u16cstr(WStringPiece str) {
// //
// The std::string versions return where whitespace was found. // The std::string versions return where whitespace was found.
// NOTE: Safe to use the same variable for both input and output. // NOTE: Safe to use the same variable for both input and output.
BASE_EXPORT TrimPositions TrimWhitespace(const string16& input, BASE_EXPORT TrimPositions TrimWhitespace(StringPiece16 input,
TrimPositions positions, TrimPositions positions,
string16* output); string16* output);
BASE_EXPORT StringPiece16 TrimWhitespace(StringPiece16 input, BASE_EXPORT StringPiece16 TrimWhitespace(StringPiece16 input,
TrimPositions positions); TrimPositions positions);
BASE_EXPORT TrimPositions TrimWhitespaceASCII(const std::string& input, BASE_EXPORT TrimPositions TrimWhitespaceASCII(StringPiece input,
TrimPositions positions, TrimPositions positions,
std::string* output); std::string* output);
BASE_EXPORT StringPiece TrimWhitespaceASCII(StringPiece input, BASE_EXPORT StringPiece TrimWhitespaceASCII(StringPiece input,
@ -302,21 +328,23 @@ BASE_EXPORT bool ContainsOnlyChars(StringPiece input, StringPiece characters);
BASE_EXPORT bool ContainsOnlyChars(StringPiece16 input, BASE_EXPORT bool ContainsOnlyChars(StringPiece16 input,
StringPiece16 characters); StringPiece16 characters);
// Returns true if the specified string matches the criteria. How can a wide // Returns true if |str| is structurally valid UTF-8 and also doesn't
// string be 8-bit or UTF8? It contains only characters that are < 256 (in the // contain any non-character code point (e.g. U+10FFFE). Prohibiting
// first case) or characters that use only 8-bits and whose 8-bit // non-characters increases the likelihood of detecting non-UTF-8 in
// representation looks like a UTF-8 string (the second case). // real-world text, for callers which do not need to accept
// // non-characters in strings.
// Note that IsStringUTF8 checks not only if the input is structurally
// valid but also if it doesn't contain any non-character codepoint
// (e.g. U+FFFE). It's done on purpose because all the existing callers want
// to have the maximum 'discriminating' power from other encodings. If
// there's a use case for just checking the structural validity, we have to
// add a new function for that.
//
// IsStringASCII assumes the input is likely all ASCII, and does not leave early
// if it is not the case.
BASE_EXPORT bool IsStringUTF8(StringPiece str); BASE_EXPORT bool IsStringUTF8(StringPiece str);
// Returns true if |str| contains valid UTF-8, allowing non-character
// code points.
BASE_EXPORT bool IsStringUTF8AllowingNoncharacters(StringPiece str);
// Returns true if |str| contains only valid ASCII character values.
// Note 1: IsStringASCII executes in time determined solely by the
// length of the string, not by its contents, so it is robust against
// timing attacks for all strings of equal length.
// Note 2: IsStringASCII assumes the input is likely all ASCII, and
// does not leave early if it is not the case.
BASE_EXPORT bool IsStringASCII(StringPiece str); BASE_EXPORT bool IsStringASCII(StringPiece str);
BASE_EXPORT bool IsStringASCII(StringPiece16 str); BASE_EXPORT bool IsStringASCII(StringPiece16 str);
#if defined(WCHAR_T_IS_UTF32) #if defined(WCHAR_T_IS_UTF32)
@ -383,6 +411,10 @@ template <typename Char>
inline bool IsAsciiDigit(Char c) { inline bool IsAsciiDigit(Char c) {
return c >= '0' && c <= '9'; return c >= '0' && c <= '9';
} }
template <typename Char>
inline bool IsAsciiPrintable(Char c) {
return c >= ' ' && c <= '~';
}
template <typename Char> template <typename Char>
inline bool IsHexDigit(Char c) { inline bool IsHexDigit(Char c) {
@ -445,10 +477,6 @@ BASE_EXPORT void ReplaceSubstringsAfterOffset(
// convenient in that is can be used inline in the call, and fast in that it // convenient in that is can be used inline in the call, and fast in that it
// avoids copying the results of the call from a char* into a string. // avoids copying the results of the call from a char* into a string.
// //
// |length_with_null| must be at least 2, since otherwise the underlying string
// would have size 0, and trying to access &((*str)[0]) in that case can result
// in a number of problems.
//
// Internally, this takes linear time because the resize() call 0-fills the // Internally, this takes linear time because the resize() call 0-fills the
// underlying array for potentially all // underlying array for potentially all
// (|length_with_null - 1| * sizeof(string_type::value_type)) bytes. Ideally we // (|length_with_null - 1| * sizeof(string_type::value_type)) bytes. Ideally we
@ -460,9 +488,11 @@ BASE_EXPORT void ReplaceSubstringsAfterOffset(
BASE_EXPORT char* WriteInto(std::string* str, size_t length_with_null); BASE_EXPORT char* WriteInto(std::string* str, size_t length_with_null);
BASE_EXPORT char16* WriteInto(string16* str, size_t length_with_null); BASE_EXPORT char16* WriteInto(string16* str, size_t length_with_null);
// Does the opposite of SplitString()/SplitStringPiece(). Joins a vector or list // Joins a vector or list of strings into a single string, inserting |separator|
// of strings into a single string, inserting |separator| (which may be empty) // (which may be empty) in between all elements.
// in between all elements. //
// Note this is inverse of SplitString()/SplitStringPiece() defined in
// string_split.h.
// //
// If possible, callers should build a vector of StringPieces and use the // If possible, callers should build a vector of StringPieces and use the
// StringPiece variant, so that they do not create unnecessary copies of // StringPiece variant, so that they do not create unnecessary copies of
@ -506,6 +536,25 @@ BASE_EXPORT string16 ReplaceStringPlaceholders(const string16& format_string,
const string16& a, const string16& a,
size_t* offset); size_t* offset);
#if defined(OS_WIN) && defined(BASE_STRING16_IS_STD_U16STRING)
BASE_EXPORT TrimPositions TrimWhitespace(WStringPiece input,
TrimPositions positions,
std::wstring* output);
BASE_EXPORT WStringPiece TrimWhitespace(WStringPiece input,
TrimPositions positions);
BASE_EXPORT bool TrimString(WStringPiece input,
WStringPiece trim_chars,
std::wstring* output);
BASE_EXPORT WStringPiece TrimString(WStringPiece input,
WStringPiece trim_chars,
TrimPositions positions);
BASE_EXPORT wchar_t* WriteInto(std::wstring* str, size_t length_with_null);
#endif
} // namespace base } // namespace base
#if defined(OS_WIN) #if defined(OS_WIN)

View File

@ -6,61 +6,48 @@
namespace base { namespace base {
#define WHITESPACE_UNICODE \ #define WHITESPACE_ASCII_NO_CR_LF \
0x0009, /* CHARACTER TABULATION */ \ 0x09, /* CHARACTER TABULATION */ \
0x000A, /* LINE FEED (LF) */ \ 0x0B, /* LINE TABULATION */ \
0x000B, /* LINE TABULATION */ \ 0x0C, /* FORM FEED (FF) */ \
0x000C, /* FORM FEED (FF) */ \ 0x20 /* SPACE */
0x000D, /* CARRIAGE RETURN (CR) */ \
0x0020, /* SPACE */ \
0x0085, /* NEXT LINE (NEL) */ \
0x00A0, /* NO-BREAK SPACE */ \
0x1680, /* OGHAM SPACE MARK */ \
0x2000, /* EN QUAD */ \
0x2001, /* EM QUAD */ \
0x2002, /* EN SPACE */ \
0x2003, /* EM SPACE */ \
0x2004, /* THREE-PER-EM SPACE */ \
0x2005, /* FOUR-PER-EM SPACE */ \
0x2006, /* SIX-PER-EM SPACE */ \
0x2007, /* FIGURE SPACE */ \
0x2008, /* PUNCTUATION SPACE */ \
0x2009, /* THIN SPACE */ \
0x200A, /* HAIR SPACE */ \
0x2028, /* LINE SEPARATOR */ \
0x2029, /* PARAGRAPH SEPARATOR */ \
0x202F, /* NARROW NO-BREAK SPACE */ \
0x205F, /* MEDIUM MATHEMATICAL SPACE */ \
0x3000, /* IDEOGRAPHIC SPACE */ \
0
const wchar_t kWhitespaceWide[] = { #define WHITESPACE_ASCII \
WHITESPACE_UNICODE WHITESPACE_ASCII_NO_CR_LF, /* Comment to make clang-format linebreak */ \
}; 0x0A, /* LINE FEED (LF) */ \
0x0D /* CARRIAGE RETURN (CR) */
const char16 kWhitespaceUTF16[] = { #define WHITESPACE_UNICODE_NON_ASCII \
WHITESPACE_UNICODE 0x0085, /* NEXT LINE (NEL) */ \
}; 0x00A0, /* NO-BREAK SPACE */ \
0x1680, /* OGHAM SPACE MARK */ \
0x2000, /* EN QUAD */ \
0x2001, /* EM QUAD */ \
0x2002, /* EN SPACE */ \
0x2003, /* EM SPACE */ \
0x2004, /* THREE-PER-EM SPACE */ \
0x2005, /* FOUR-PER-EM SPACE */ \
0x2006, /* SIX-PER-EM SPACE */ \
0x2007, /* FIGURE SPACE */ \
0x2008, /* PUNCTUATION SPACE */ \
0x2009, /* THIN SPACE */ \
0x200A, /* HAIR SPACE */ \
0x2028, /* LINE SEPARATOR */ \
0x2029, /* PARAGRAPH SEPARATOR */ \
0x202F, /* NARROW NO-BREAK SPACE */ \
0x205F, /* MEDIUM MATHEMATICAL SPACE */ \
0x3000 /* IDEOGRAPHIC SPACE */
const char kWhitespaceASCII[] = { #define WHITESPACE_UNICODE_NO_CR_LF \
0x09, // CHARACTER TABULATION WHITESPACE_ASCII_NO_CR_LF, WHITESPACE_UNICODE_NON_ASCII
0x0A, // LINE FEED (LF)
0x0B, // LINE TABULATION
0x0C, // FORM FEED (FF)
0x0D, // CARRIAGE RETURN (CR)
0x20, // SPACE
0
};
const char16 kWhitespaceASCIIAs16[] = { #define WHITESPACE_UNICODE WHITESPACE_ASCII, WHITESPACE_UNICODE_NON_ASCII
0x09, // CHARACTER TABULATION
0x0A, // LINE FEED (LF) const wchar_t kWhitespaceWide[] = {WHITESPACE_UNICODE, 0};
0x0B, // LINE TABULATION const char16 kWhitespaceUTF16[] = {WHITESPACE_UNICODE, 0};
0x0C, // FORM FEED (FF) const char16 kWhitespaceNoCrLfUTF16[] = {WHITESPACE_UNICODE_NO_CR_LF, 0};
0x0D, // CARRIAGE RETURN (CR) const char kWhitespaceASCII[] = {WHITESPACE_ASCII, 0};
0x20, // SPACE const char16 kWhitespaceASCIIAs16[] = {WHITESPACE_ASCII, 0};
0
};
const char kUtf8ByteOrderMark[] = "\xEF\xBB\xBF"; const char kUtf8ByteOrderMark[] = "\xEF\xBB\xBF";

View File

@ -39,18 +39,25 @@ inline int vsnprintfT(wchar_t* buffer,
va_list argptr) { va_list argptr) {
return base::vswprintf(buffer, buf_size, format, argptr); return base::vswprintf(buffer, buf_size, format, argptr);
} }
inline int vsnprintfT(char16_t* buffer,
size_t buf_size,
const char16_t* format,
va_list argptr) {
return base::vswprintf(reinterpret_cast<wchar_t*>(buffer), buf_size,
reinterpret_cast<const wchar_t*>(format), argptr);
}
#endif #endif
// Templatized backend for StringPrintF/StringAppendF. This does not finalize // Templatized backend for StringPrintF/StringAppendF. This does not finalize
// the va_list, the caller is expected to do that. // the va_list, the caller is expected to do that.
template <class StringType> template <class CharT>
static void StringAppendVT(StringType* dst, static void StringAppendVT(std::basic_string<CharT>* dst,
const typename StringType::value_type* format, const CharT* format,
va_list ap) { va_list ap) {
// First try with a small fixed size buffer. // First try with a small fixed size buffer.
// This buffer size should be kept in sync with StringUtilTest.GrowBoundary // This buffer size should be kept in sync with StringUtilTest.GrowBoundary
// and StringUtilTest.StringPrintfBounds. // and StringUtilTest.StringPrintfBounds.
typename StringType::value_type stack_buf[1024]; CharT stack_buf[1024];
va_list ap_copy; va_list ap_copy;
va_copy(ap_copy, ap); va_copy(ap_copy, ap);
@ -93,7 +100,7 @@ static void StringAppendVT(StringType* dst,
return; return;
} }
std::vector<typename StringType::value_type> mem_buf(mem_length); std::vector<CharT> mem_buf(mem_length);
// NOTE: You can only use a va_list once. Since we're in a while loop, we // NOTE: You can only use a va_list once. Since we're in a while loop, we
// need to make a new copy each time so we don't use up the original. // need to make a new copy each time so we don't use up the original.
@ -129,6 +136,15 @@ std::wstring StringPrintf(const wchar_t* format, ...) {
va_end(ap); va_end(ap);
return result; return result;
} }
std::u16string StringPrintf(const char16_t* format, ...) {
va_list ap;
va_start(ap, format);
std::u16string result;
StringAppendV(&result, format, ap);
va_end(ap);
return result;
}
#endif #endif
std::string StringPrintV(const char* format, va_list ap) { std::string StringPrintV(const char* format, va_list ap) {
@ -156,6 +172,17 @@ const std::wstring& SStringPrintf(std::wstring* dst,
va_end(ap); va_end(ap);
return *dst; return *dst;
} }
const std::u16string& SStringPrintf(std::u16string* dst,
const char16_t* format,
...) {
va_list ap;
va_start(ap, format);
dst->clear();
StringAppendV(dst, format, ap);
va_end(ap);
return *dst;
}
#endif #endif
void StringAppendF(std::string* dst, const char* format, ...) { void StringAppendF(std::string* dst, const char* format, ...) {
@ -172,6 +199,13 @@ void StringAppendF(std::wstring* dst, const wchar_t* format, ...) {
StringAppendV(dst, format, ap); StringAppendV(dst, format, ap);
va_end(ap); va_end(ap);
} }
void StringAppendF(std::u16string* dst, const char16_t* format, ...) {
va_list ap;
va_start(ap, format);
StringAppendV(dst, format, ap);
va_end(ap);
}
#endif #endif
void StringAppendV(std::string* dst, const char* format, va_list ap) { void StringAppendV(std::string* dst, const char* format, va_list ap) {
@ -182,6 +216,10 @@ void StringAppendV(std::string* dst, const char* format, va_list ap) {
void StringAppendV(std::wstring* dst, const wchar_t* format, va_list ap) { void StringAppendV(std::wstring* dst, const wchar_t* format, va_list ap) {
StringAppendVT(dst, format, ap); StringAppendVT(dst, format, ap);
} }
void StringAppendV(std::u16string* dst, const char16_t* format, va_list ap) {
StringAppendVT(dst, format, ap);
}
#endif #endif
} // namespace base } // namespace base

View File

@ -19,8 +19,14 @@ namespace base {
BASE_EXPORT std::string StringPrintf(const char* format, ...) BASE_EXPORT std::string StringPrintf(const char* format, ...)
PRINTF_FORMAT(1, 2) WARN_UNUSED_RESULT; PRINTF_FORMAT(1, 2) WARN_UNUSED_RESULT;
#if defined(OS_WIN) #if defined(OS_WIN)
// Note: Unfortunately compile time checking of the format string for UTF-16
// strings is not supported by any compiler, thus these functions should be used
// carefully and sparingly. Also applies to SStringPrintf and StringAppendV
// below.
BASE_EXPORT std::wstring StringPrintf(const wchar_t* format, ...) BASE_EXPORT std::wstring StringPrintf(const wchar_t* format, ...)
WPRINTF_FORMAT(1, 2) WARN_UNUSED_RESULT; WPRINTF_FORMAT(1, 2) WARN_UNUSED_RESULT;
BASE_EXPORT std::u16string StringPrintf(const char16_t* format, ...)
WPRINTF_FORMAT(1, 2) WARN_UNUSED_RESULT;
#endif #endif
// Return a C++ string given vprintf-like input. // Return a C++ string given vprintf-like input.
@ -35,6 +41,9 @@ BASE_EXPORT const std::string& SStringPrintf(std::string* dst,
BASE_EXPORT const std::wstring& SStringPrintf(std::wstring* dst, BASE_EXPORT const std::wstring& SStringPrintf(std::wstring* dst,
const wchar_t* format, const wchar_t* format,
...) WPRINTF_FORMAT(2, 3); ...) WPRINTF_FORMAT(2, 3);
BASE_EXPORT const std::u16string& SStringPrintf(std::u16string* dst,
const char16_t* format,
...) WPRINTF_FORMAT(2, 3);
#endif #endif
// Append result to a supplied string. // Append result to a supplied string.
@ -43,6 +52,8 @@ BASE_EXPORT void StringAppendF(std::string* dst, const char* format, ...)
#if defined(OS_WIN) #if defined(OS_WIN)
BASE_EXPORT void StringAppendF(std::wstring* dst, const wchar_t* format, ...) BASE_EXPORT void StringAppendF(std::wstring* dst, const wchar_t* format, ...)
WPRINTF_FORMAT(2, 3); WPRINTF_FORMAT(2, 3);
BASE_EXPORT void StringAppendF(std::u16string* dst, const char16_t* format, ...)
WPRINTF_FORMAT(2, 3);
#endif #endif
// Lower-level routine that takes a va_list and appends to a specified // Lower-level routine that takes a va_list and appends to a specified
@ -53,6 +64,9 @@ BASE_EXPORT void StringAppendV(std::string* dst, const char* format, va_list ap)
BASE_EXPORT void StringAppendV(std::wstring* dst, BASE_EXPORT void StringAppendV(std::wstring* dst,
const wchar_t* format, const wchar_t* format,
va_list ap) WPRINTF_FORMAT(2, 0); va_list ap) WPRINTF_FORMAT(2, 0);
BASE_EXPORT void StringAppendV(std::u16string* dst,
const char16_t* format,
va_list ap) WPRINTF_FORMAT(2, 0);
#endif #endif
} // namespace base } // namespace base

View File

@ -17,16 +17,19 @@
namespace base { namespace base {
inline bool IsValidCodepoint(uint32_t code_point) { inline bool IsValidCodepoint(uint32_t code_point) {
// Excludes the surrogate code points ([0xD800, 0xDFFF]) and // Excludes code points that are not Unicode scalar values, i.e.
// codepoints larger than 0x10FFFF (the highest codepoint allowed). // surrogate code points ([0xD800, 0xDFFF]). Additionally, excludes
// Non-characters and unassigned codepoints are allowed. // code points larger than 0x10FFFF (the highest codepoint allowed).
// Non-characters and unassigned code points are allowed.
// https://unicode.org/glossary/#unicode_scalar_value
return code_point < 0xD800u || return code_point < 0xD800u ||
(code_point >= 0xE000u && code_point <= 0x10FFFFu); (code_point >= 0xE000u && code_point <= 0x10FFFFu);
} }
inline bool IsValidCharacter(uint32_t code_point) { inline bool IsValidCharacter(uint32_t code_point) {
// Excludes non-characters (U+FDD0..U+FDEF, and all codepoints ending in // Excludes non-characters (U+FDD0..U+FDEF, and all code points
// 0xFFFE or 0xFFFF) from the set of valid code points. // ending in 0xFFFE or 0xFFFF) from the set of valid code points.
// https://unicode.org/faq/private_use.html#nonchar1
return code_point < 0xD800u || (code_point >= 0xE000u && return code_point < 0xD800u || (code_point >= 0xE000u &&
code_point < 0xFDD0u) || (code_point > 0xFDEFu && code_point < 0xFDD0u) || (code_point > 0xFDEFu &&
code_point <= 0x10FFFFu && (code_point & 0xFFFEu) != 0xFFFEu); code_point <= 0x10FFFFu && (code_point & 0xFFFEu) != 0xFFFEu);

View File

@ -9,7 +9,6 @@
#include <type_traits> #include <type_traits>
#include "base/bit_cast.h"
#include "base/strings/string_piece.h" #include "base/strings/string_piece.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "base/strings/utf_string_conversion_utils.h" #include "base/strings/utf_string_conversion_utils.h"

View File

@ -23,31 +23,31 @@ namespace base {
// possible. // possible.
BASE_EXPORT bool WideToUTF8(const wchar_t* src, size_t src_len, BASE_EXPORT bool WideToUTF8(const wchar_t* src, size_t src_len,
std::string* output); std::string* output);
BASE_EXPORT std::string WideToUTF8(WStringPiece wide); BASE_EXPORT std::string WideToUTF8(WStringPiece wide) WARN_UNUSED_RESULT;
BASE_EXPORT bool UTF8ToWide(const char* src, size_t src_len, BASE_EXPORT bool UTF8ToWide(const char* src, size_t src_len,
std::wstring* output); std::wstring* output);
BASE_EXPORT std::wstring UTF8ToWide(StringPiece utf8); BASE_EXPORT std::wstring UTF8ToWide(StringPiece utf8) WARN_UNUSED_RESULT;
BASE_EXPORT bool WideToUTF16(const wchar_t* src, size_t src_len, BASE_EXPORT bool WideToUTF16(const wchar_t* src, size_t src_len,
string16* output); string16* output);
BASE_EXPORT string16 WideToUTF16(WStringPiece wide); BASE_EXPORT string16 WideToUTF16(WStringPiece wide) WARN_UNUSED_RESULT;
BASE_EXPORT bool UTF16ToWide(const char16* src, size_t src_len, BASE_EXPORT bool UTF16ToWide(const char16* src, size_t src_len,
std::wstring* output); std::wstring* output);
BASE_EXPORT std::wstring UTF16ToWide(StringPiece16 utf16); BASE_EXPORT std::wstring UTF16ToWide(StringPiece16 utf16) WARN_UNUSED_RESULT;
BASE_EXPORT bool UTF8ToUTF16(const char* src, size_t src_len, string16* output); BASE_EXPORT bool UTF8ToUTF16(const char* src, size_t src_len, string16* output);
BASE_EXPORT string16 UTF8ToUTF16(StringPiece utf8); BASE_EXPORT string16 UTF8ToUTF16(StringPiece utf8) WARN_UNUSED_RESULT;
BASE_EXPORT bool UTF16ToUTF8(const char16* src, size_t src_len, BASE_EXPORT bool UTF16ToUTF8(const char16* src, size_t src_len,
std::string* output); std::string* output);
BASE_EXPORT std::string UTF16ToUTF8(StringPiece16 utf16); BASE_EXPORT std::string UTF16ToUTF8(StringPiece16 utf16) WARN_UNUSED_RESULT;
// This converts an ASCII string, typically a hardcoded constant, to a UTF16 // This converts an ASCII string, typically a hardcoded constant, to a UTF16
// string. // string.
BASE_EXPORT string16 ASCIIToUTF16(StringPiece ascii); BASE_EXPORT string16 ASCIIToUTF16(StringPiece ascii) WARN_UNUSED_RESULT;
// Converts to 7-bit ASCII by truncating. The result must be known to be ASCII // Converts to 7-bit ASCII by truncating. The result must be known to be ASCII
// beforehand. // beforehand.
BASE_EXPORT std::string UTF16ToASCII(StringPiece16 utf16); BASE_EXPORT std::string UTF16ToASCII(StringPiece16 utf16) WARN_UNUSED_RESULT;
} // namespace base } // namespace base

View File

@ -58,9 +58,6 @@
// thread that has Wait()ed the longest is selected. The default policy // thread that has Wait()ed the longest is selected. The default policy
// may improve performance, as the selected thread may have a greater chance of // may improve performance, as the selected thread may have a greater chance of
// having some of its stack data in various CPU caches. // having some of its stack data in various CPU caches.
//
// For a discussion of the many very subtle implementation details, see the FAQ
// at the end of condition_variable_win.cc.
#ifndef BASE_SYNCHRONIZATION_CONDITION_VARIABLE_H_ #ifndef BASE_SYNCHRONIZATION_CONDITION_VARIABLE_H_
#define BASE_SYNCHRONIZATION_CONDITION_VARIABLE_H_ #define BASE_SYNCHRONIZATION_CONDITION_VARIABLE_H_

View File

@ -67,7 +67,7 @@ void ConditionVariable::Wait() {
Optional<internal::ScopedBlockingCallWithBaseSyncPrimitives> Optional<internal::ScopedBlockingCallWithBaseSyncPrimitives>
scoped_blocking_call; scoped_blocking_call;
if (waiting_is_blocking_) if (waiting_is_blocking_)
scoped_blocking_call.emplace(BlockingType::MAY_BLOCK); scoped_blocking_call.emplace(FROM_HERE, BlockingType::MAY_BLOCK);
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
user_lock_->CheckHeldAndUnmark(); user_lock_->CheckHeldAndUnmark();
@ -83,7 +83,7 @@ void ConditionVariable::TimedWait(const TimeDelta& max_time) {
Optional<internal::ScopedBlockingCallWithBaseSyncPrimitives> Optional<internal::ScopedBlockingCallWithBaseSyncPrimitives>
scoped_blocking_call; scoped_blocking_call;
if (waiting_is_blocking_) if (waiting_is_blocking_)
scoped_blocking_call.emplace(BlockingType::MAY_BLOCK); scoped_blocking_call.emplace(FROM_HERE, BlockingType::MAY_BLOCK);
int64_t usecs = max_time.InMicroseconds(); int64_t usecs = max_time.InMicroseconds();
struct timespec relative_time; struct timespec relative_time;

View File

@ -26,8 +26,8 @@ class LOCKABLE BASE_EXPORT Lock {
~Lock() {} ~Lock() {}
// TODO(lukasza): https://crbug.com/831825: Add EXCLUSIVE_LOCK_FUNCTION // TODO(lukasza): https://crbug.com/831825: Add EXCLUSIVE_LOCK_FUNCTION
// annotation to Acquire method and similar annotations to Release, Try and // annotation to Acquire method and similar annotations to Release and Try
// AssertAcquired methods (here and in the #else branch). // methods (here and in the #else branch).
void Acquire() { lock_.Lock(); } void Acquire() { lock_.Lock(); }
void Release() { lock_.Unlock(); } void Release() { lock_.Unlock(); }
@ -38,7 +38,7 @@ class LOCKABLE BASE_EXPORT Lock {
bool Try() { return lock_.Try(); } bool Try() { return lock_.Try(); }
// Null implementation if not debug. // Null implementation if not debug.
void AssertAcquired() const {} void AssertAcquired() const ASSERT_EXCLUSIVE_LOCK() {}
#else #else
Lock(); Lock();
~Lock(); ~Lock();
@ -63,7 +63,7 @@ class LOCKABLE BASE_EXPORT Lock {
return rv; return rv;
} }
void AssertAcquired() const; void AssertAcquired() const ASSERT_EXCLUSIVE_LOCK();
#endif // DCHECK_IS_ON() #endif // DCHECK_IS_ON()
// Whether Lock mitigates priority inversion when used from different thread // Whether Lock mitigates priority inversion when used from different thread
@ -116,6 +116,18 @@ using AutoLock = internal::BasicAutoLock<Lock>;
// constructor, and re-Acquire() it in the destructor. // constructor, and re-Acquire() it in the destructor.
using AutoUnlock = internal::BasicAutoUnlock<Lock>; using AutoUnlock = internal::BasicAutoUnlock<Lock>;
// Like AutoLock but is a no-op when the provided Lock* is null. Inspired from
// absl::MutexLockMaybe. Use this instead of base::Optional<base::AutoLock> to
// get around -Wthread-safety-analysis warnings for conditional locking.
using AutoLockMaybe = internal::BasicAutoLockMaybe<Lock>;
// Like AutoLock but permits Release() of its mutex before destruction.
// Release() may be called at most once. Inspired from
// absl::ReleasableMutexLock. Use this instead of base::Optional<base::AutoLock>
// to get around -Wthread-safety-analysis warnings for AutoLocks that are
// explicitly released early (prefer proper scoping to this).
using ReleasableAutoLock = internal::BasicReleasableAutoLock<Lock>;
} // namespace base } // namespace base
#endif // BASE_SYNCHRONIZATION_LOCK_H_ #endif // BASE_SYNCHRONIZATION_LOCK_H_

Some files were not shown because too many files have changed in this diff Show More