зеркало из https://github.com/mozilla/gecko-dev.git
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 filesystem_interception.cc - Add mozilla::AddWin32kLockdownPolicy to apply MITIGATION_WIN32K_DISABLE before SUBSYS_WIN32K_LOCKDOWN Depends on D79558 Differential Revision: https://phabricator.services.mozilla.com/D79560
This commit is contained in:
Родитель
be67c3dc79
Коммит
8a6f673311
|
@ -15,4 +15,6 @@
|
|||
|
||||
#include "build/buildflag.h"
|
||||
|
||||
#define BUILDFLAG_INTERNAL_USE_TCMALLOC() (0)
|
||||
|
||||
#endif // BASE_ALLOCATOR_BUILDFLAGS_H_
|
||||
|
|
|
@ -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
|
|
@ -4,18 +4,24 @@
|
|||
* 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 Chromium source file base/debug/stack_trace.h.
|
||||
// To provide a dummy class StackTrace required in base/win/scoped_handle.cc.
|
||||
// This is a dummy version of Chromium source file base/debug/stack_trace.h
|
||||
// to provide a dummy class StackTrace.
|
||||
|
||||
#ifndef BASE_DEBUG_STACK_TRACE_H_
|
||||
#define BASE_DEBUG_STACK_TRACE_H_
|
||||
|
||||
#include <iosfwd>
|
||||
|
||||
namespace base {
|
||||
namespace debug {
|
||||
|
||||
class BASE_EXPORT StackTrace {
|
||||
public:
|
||||
StackTrace() {};
|
||||
|
||||
#if !defined(__UCLIBC__) & !defined(_AIX)
|
||||
void OutputToStream(std::ostream*) const {}
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace debug
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#ifndef BASE_FEATURE_LIST_H_
|
||||
#define BASE_FEATURE_LIST_H_
|
||||
|
||||
#include "base/macros.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
// Specifies whether a given feature is enabled or disabled by default.
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
|
||||
#include "base/files/file_path.h"
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
using StringType = FilePath::StringType;
|
||||
|
@ -18,6 +16,41 @@ namespace {
|
|||
|
||||
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
|
||||
|
||||
FilePath::FilePath() = default;
|
||||
|
@ -37,4 +70,148 @@ FilePath& FilePath::operator=(const 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
|
||||
|
|
|
@ -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_
|
|
@ -24,9 +24,9 @@ class BASE_EXPORT SharedMemoryTracker {
|
|||
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:
|
||||
SharedMemoryTracker() {};
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#ifndef BASE_METRICS_HISTOGRAM_MACROS_H_
|
||||
#define BASE_METRICS_HISTOGRAM_MACROS_H_
|
||||
|
||||
#define UMA_HISTOGRAM_ENUMERATION(name, sample, enum_max) do { } while (0)
|
||||
#define SCOPED_UMA_HISTOGRAM_TIMER(name) do { } while (0)
|
||||
#define UMA_HISTOGRAM_ENUMERATION(name, sample) do { } while (0)
|
||||
|
||||
#endif // BASE_METRICS_HISTOGRAM_MACROS_H_
|
||||
|
|
|
@ -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_
|
|
@ -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_
|
|
@ -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
|
|
@ -35,7 +35,8 @@ namespace internal {
|
|||
|
||||
class BASE_EXPORT ScopedBlockingCallWithBaseSyncPrimitives {
|
||||
public:
|
||||
ScopedBlockingCallWithBaseSyncPrimitives(BlockingType blocking_type) {};
|
||||
ScopedBlockingCallWithBaseSyncPrimitives(const Location& from_here,
|
||||
BlockingType blocking_type) {}
|
||||
~ScopedBlockingCallWithBaseSyncPrimitives() {};
|
||||
};
|
||||
|
||||
|
|
|
@ -330,6 +330,30 @@ DeriveAppContainerSidFromAppContainerName(
|
|||
#define PROC_THREAD_ATTRIBUTE_ALL_APPLICATION_PACKAGES_POLICY \
|
||||
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_
|
||||
|
|
|
@ -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
|
|
@ -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_
|
|
@ -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_
|
|
@ -48,11 +48,11 @@ AtExitManager::~AtExitManager() {
|
|||
// static
|
||||
void AtExitManager::RegisterCallback(AtExitCallbackType func, void* param) {
|
||||
DCHECK(func);
|
||||
RegisterTask(base::Bind(func, param));
|
||||
RegisterTask(base::BindOnce(func, param));
|
||||
}
|
||||
|
||||
// static
|
||||
void AtExitManager::RegisterTask(base::Closure task) {
|
||||
void AtExitManager::RegisterTask(base::OnceClosure task) {
|
||||
if (!g_top_manager) {
|
||||
NOTREACHED() << "Tried to RegisterCallback without an AtExitManager";
|
||||
return;
|
||||
|
@ -75,7 +75,7 @@ void AtExitManager::ProcessCallbacksNow() {
|
|||
// 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
|
||||
// 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_);
|
||||
tasks.swap(g_top_manager->stack_);
|
||||
|
@ -89,8 +89,7 @@ void AtExitManager::ProcessCallbacksNow() {
|
|||
ScopedAllowCrossThreadRefCountAccess allow_cross_thread_ref_count_access;
|
||||
|
||||
while (!tasks.empty()) {
|
||||
base::Closure task = tasks.top();
|
||||
task.Run();
|
||||
std::move(tasks.top()).Run();
|
||||
tasks.pop();
|
||||
}
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ class BASE_EXPORT AtExitManager {
|
|||
static void RegisterCallback(AtExitCallbackType func, void* param);
|
||||
|
||||
// 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
|
||||
// is possible to register new callbacks after calling this function.
|
||||
|
@ -63,7 +63,7 @@ class BASE_EXPORT AtExitManager {
|
|||
private:
|
||||
base::Lock lock_;
|
||||
|
||||
base::stack<base::Closure> stack_ GUARDED_BY(lock_);
|
||||
base::stack<base::OnceClosure> stack_ GUARDED_BY(lock_);
|
||||
|
||||
#if DCHECK_IS_ON()
|
||||
bool processing_callbacks_ GUARDED_BY(lock_) = false;
|
||||
|
|
|
@ -39,15 +39,6 @@
|
|||
#include "base/base_export.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 subtle {
|
||||
|
||||
|
@ -100,8 +91,7 @@ Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
|
|||
// ensure that no later memory access can be reordered ahead of the operation.
|
||||
// "Release" operations ensure that no previous memory access can be reordered
|
||||
// after the operation. "Barrier" operations have both "Acquire" and "Release"
|
||||
// semantics. A MemoryBarrier() has "Barrier" semantics, but does no memory
|
||||
// access.
|
||||
// semantics.
|
||||
Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
|
||||
Atomic32 old_value,
|
||||
Atomic32 new_value);
|
||||
|
@ -109,7 +99,6 @@ Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
|
|||
Atomic32 old_value,
|
||||
Atomic32 new_value);
|
||||
|
||||
void MemoryBarrier();
|
||||
void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value);
|
||||
void Acquire_Store(volatile Atomic32* ptr, Atomic32 value);
|
||||
void Release_Store(volatile Atomic32* ptr, Atomic32 value);
|
||||
|
|
|
@ -52,16 +52,6 @@ typedef volatile std::atomic<Atomic32>* AtomicLocation32;
|
|||
static_assert(sizeof(*(AtomicLocation32) nullptr) == sizeof(Atomic32),
|
||||
"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,
|
||||
Atomic32 old_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) {
|
||||
((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) {
|
||||
|
@ -135,7 +125,7 @@ inline Atomic32 Acquire_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);
|
||||
}
|
||||
|
||||
|
@ -202,7 +192,7 @@ inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
|
|||
|
||||
inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
|
||||
((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) {
|
||||
|
@ -218,7 +208,7 @@ inline Atomic64 Acquire_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);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,18 +11,11 @@
|
|||
|
||||
#include <intrin.h>
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "base/macros.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 subtle {
|
||||
|
||||
|
@ -56,18 +49,6 @@ inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
|
|||
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,
|
||||
Atomic32 old_value,
|
||||
Atomic32 new_value) {
|
||||
|
@ -104,7 +85,7 @@ inline Atomic32 Acquire_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;
|
||||
}
|
||||
|
||||
|
@ -173,7 +154,7 @@ inline Atomic64 Acquire_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;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
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";
|
||||
|
||||
// Disables the crash reporting.
|
||||
|
@ -16,6 +16,9 @@ const char kDisableBreakpad[] = "disable-breakpad";
|
|||
// Comma-separated list of feature names to disable. See also kEnableFeatures.
|
||||
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
|
||||
// processes cannot access to files needed to make this decision, this flag is
|
||||
// generated internally.
|
||||
|
@ -24,15 +27,9 @@ const char kEnableCrashReporter[] = "enable-crash-reporter";
|
|||
// Comma-separated list of feature names to enable. See also kDisableFeatures.
|
||||
const char kEnableFeatures[] = "enable-features";
|
||||
|
||||
// Generates full memory crash dump.
|
||||
const char kFullMemoryCrashReport[] = "full-memory-crash-report";
|
||||
|
||||
// Force low-end device mode when set.
|
||||
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.
|
||||
// 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.
|
||||
|
@ -43,44 +40,19 @@ const char kDisableLowEndDeviceMode[] = "disable-low-end-device-mode";
|
|||
// FieldTrialList::CreateTrialsFromString() in field_trial.h for details.
|
||||
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.
|
||||
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
|
||||
// will only work if chrome has been built with the gn arg enable_profiling =
|
||||
// true. The output will go to the value of kProfilingFile.
|
||||
|
@ -102,7 +74,45 @@ const char kProfilingFile[] = "profiling-file";
|
|||
// specified.
|
||||
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)
|
||||
// Disable high-resolution timer on Windows.
|
||||
const char kDisableHighResTimer[] = "disable-highres-timer";
|
||||
|
||||
// Disables the USB keyboard detection for blocking the OSK on Win8+.
|
||||
const char kDisableUsbKeyboardDetect[] = "disable-usb-keyboard-detect";
|
||||
#endif
|
||||
|
@ -126,17 +136,14 @@ const char kEnableCrashReporterForTesting[] =
|
|||
// Enables the reached code profiler that samples all threads in all processes
|
||||
// to determine which functions are almost never executed.
|
||||
const char kEnableReachedCodeProfiler[] = "enable-reached-code-profiler";
|
||||
#endif
|
||||
|
||||
// Specifies optimization of memory layout of the native library using the
|
||||
// orderfile symbols given in base/android/library_loader/anchor_functions.h,
|
||||
// via madvise and changing the library prefetch behavior.
|
||||
#if defined(OS_LINUX)
|
||||
// Controls whether or not retired instruction counts are surfaced for threads
|
||||
// in trace events on Linux.
|
||||
//
|
||||
// If this switch is not specified, an optimization may be done depending on a
|
||||
// synthetic trial. If specified, its values may be 'on' or 'off'. These
|
||||
// override the synthetic trial.
|
||||
//
|
||||
// This flag is only used on architectures with SUPPORTS_CODE_ORDERING defined.
|
||||
const char kOrderfileMemoryOptimization[] = "orderfile-memory-optimization";
|
||||
// This flag requires the BPF sandbox to be disabled.
|
||||
const char kEnableThreadInstructionCount[] = "enable-thread-instruction-count";
|
||||
#endif
|
||||
|
||||
} // namespace switches
|
||||
|
|
|
@ -20,6 +20,7 @@ extern const char kEnableFeatures[];
|
|||
extern const char kEnableLowEndDeviceMode[];
|
||||
extern const char kForceFieldTrials[];
|
||||
extern const char kFullMemoryCrashReport[];
|
||||
extern const char kLogBestEffortTasks[];
|
||||
extern const char kNoErrorDialogs[];
|
||||
extern const char kProfilingAtStart[];
|
||||
extern const char kProfilingFile[];
|
||||
|
@ -33,6 +34,7 @@ extern const char kVModule[];
|
|||
extern const char kWaitForDebugger[];
|
||||
|
||||
#if defined(OS_WIN)
|
||||
extern const char kDisableHighResTimer[];
|
||||
extern const char kDisableUsbKeyboardDetect[];
|
||||
#endif
|
||||
|
||||
|
@ -49,6 +51,10 @@ extern const char kEnableReachedCodeProfiler[];
|
|||
extern const char kOrderfileMemoryOptimization[];
|
||||
#endif
|
||||
|
||||
#if defined(OS_LINUX)
|
||||
extern const char kEnableThreadInstructionCount[];
|
||||
#endif
|
||||
|
||||
} // namespace switches
|
||||
|
||||
#endif // BASE_BASE_SWITCHES_H_
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "base/bind_internal.h"
|
||||
|
@ -41,7 +42,7 @@
|
|||
// class C : public base::RefCounted<C> { void F(); };
|
||||
// auto instance = base::MakeRefCounted<C>();
|
||||
// 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
|
||||
// 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 =
|
||||
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 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)).");
|
||||
template <typename Invoker>
|
||||
constexpr auto GetInvokeFunc(std::false_type) {
|
||||
return Invoker::Run;
|
||||
}
|
||||
|
||||
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
|
||||
// target function. This check does not affect the behavior of Bind, but its
|
||||
// error message should be more readable.
|
||||
static constexpr bool kIsOnce = IsOnceCallback<CallbackT<void()>>::value;
|
||||
using Helper = internal::BindTypeHelper<Functor, Args...>;
|
||||
using FunctorTraits = typename Helper::FunctorTraits;
|
||||
using BoundArgsList = typename Helper::BoundArgsList;
|
||||
using UnwrappedArgsList =
|
||||
internal::MakeUnwrappedTypeList<true, FunctorTraits::is_method,
|
||||
internal::MakeUnwrappedTypeList<kIsOnce, FunctorTraits::is_method,
|
||||
Args&&...>;
|
||||
using BoundParamsList = typename Helper::BoundParamsList;
|
||||
static_assert(internal::AssertBindArgsValidity<
|
||||
|
@ -209,13 +220,14 @@ BindOnce(Functor&& functor, Args&&... args) {
|
|||
using BindState = internal::MakeBindStateType<Functor, Args...>;
|
||||
using UnboundRunType = MakeUnboundRunType<Functor, Args...>;
|
||||
using Invoker = internal::Invoker<BindState, UnboundRunType>;
|
||||
using CallbackType = OnceCallback<UnboundRunType>;
|
||||
using CallbackType = CallbackT<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::RunOnce;
|
||||
PolymorphicInvoke invoke_func =
|
||||
GetInvokeFunc<Invoker>(std::integral_constant<bool, kIsOnce>());
|
||||
|
||||
using InvokeFuncStorage = internal::BindStateBase::InvokeFuncStorage;
|
||||
return CallbackType(BindState::Create(
|
||||
|
@ -223,6 +235,23 @@ BindOnce(Functor&& functor, 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.
|
||||
template <typename Functor, typename... Args>
|
||||
inline RepeatingCallback<MakeUnboundRunType<Functor, Args...>>
|
||||
|
@ -231,36 +260,8 @@ BindRepeating(Functor&& functor, Args&&... args) {
|
|||
!internal::IsOnceCallback<std::decay_t<Functor>>(),
|
||||
"BindRepeating cannot bind OnceCallback. Use BindOnce with std::move().");
|
||||
|
||||
// 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
|
||||
// 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)...));
|
||||
return internal::BindImpl<RepeatingCallback>(std::forward<Functor>(functor),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// Unannotated Bind.
|
||||
|
@ -275,22 +276,27 @@ Bind(Functor&& functor, Args&&... args) {
|
|||
|
||||
// Special cases for binding to a base::Callback without extra bound arguments.
|
||||
template <typename Signature>
|
||||
OnceCallback<Signature> BindOnce(OnceCallback<Signature> closure) {
|
||||
return closure;
|
||||
OnceCallback<Signature> BindOnce(OnceCallback<Signature> callback) {
|
||||
return callback;
|
||||
}
|
||||
|
||||
template <typename Signature>
|
||||
OnceCallback<Signature> BindOnce(RepeatingCallback<Signature> callback) {
|
||||
return callback;
|
||||
}
|
||||
|
||||
template <typename Signature>
|
||||
RepeatingCallback<Signature> BindRepeating(
|
||||
RepeatingCallback<Signature> closure) {
|
||||
return closure;
|
||||
RepeatingCallback<Signature> callback) {
|
||||
return callback;
|
||||
}
|
||||
|
||||
template <typename Signature>
|
||||
Callback<Signature> Bind(Callback<Signature> closure) {
|
||||
return closure;
|
||||
Callback<Signature> Bind(Callback<Signature> callback) {
|
||||
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.
|
||||
//
|
||||
// EXAMPLE OF Unretained():
|
||||
|
@ -302,9 +308,9 @@ Callback<Signature> Bind(Callback<Signature> closure) {
|
|||
//
|
||||
// // In some function somewhere.
|
||||
// Foo foo;
|
||||
// Closure foo_callback =
|
||||
// Bind(&Foo::func, Unretained(&foo));
|
||||
// foo_callback.Run(); // Prints "Foo:f".
|
||||
// OnceClosure foo_callback =
|
||||
// BindOnce(&Foo::func, Unretained(&foo));
|
||||
// std::move(foo_callback).Run(); // Prints "Foo:f".
|
||||
//
|
||||
// Without the Unretained() wrapper on |&foo|, the above call would fail
|
||||
// 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) {}
|
||||
//
|
||||
// scoped_refptr<RefCountedBytes> bytes = ...;
|
||||
// Closure callback = Bind(&foo, base::RetainedRef(bytes));
|
||||
// callback.Run();
|
||||
// OnceClosure callback = BindOnce(&foo, base::RetainedRef(bytes));
|
||||
// std::move(callback).Run();
|
||||
//
|
||||
// Without RetainedRef, the scoped_refptr would try to implicitly convert to
|
||||
// a raw pointer and fail compilation:
|
||||
//
|
||||
// Closure callback = Bind(&foo, bytes); // ERROR!
|
||||
// OnceClosure callback = BindOnce(&foo, bytes); // ERROR!
|
||||
template <typename T>
|
||||
static inline internal::RetainedRefWrapper<T> RetainedRef(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));
|
||||
}
|
||||
|
||||
// Owned() transfers ownership of an object to the Callback resulting from
|
||||
// bind; the object will be deleted when the Callback is deleted.
|
||||
// Owned() transfers ownership of an object to the callback resulting from
|
||||
// bind; the object will be deleted when the callback is deleted.
|
||||
//
|
||||
// EXAMPLE OF Owned():
|
||||
//
|
||||
// void foo(int* arg) { cout << *arg << endl }
|
||||
//
|
||||
// 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"
|
||||
// *n = 2;
|
||||
// *pn = 2;
|
||||
// foo_callback.Run(); // Prints "2"
|
||||
//
|
||||
// foo_callback.Reset(); // |pn| is deleted. Also will happen when
|
||||
// // |foo_callback| goes out of scope.
|
||||
//
|
||||
// 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>
|
||||
static inline internal::OwnedWrapper<T> Owned(T* o) {
|
||||
return internal::OwnedWrapper<T>(o);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline internal::OwnedWrapper<T> Owned(std::unique_ptr<T>&& ptr) {
|
||||
return internal::OwnedWrapper<T>(std::move(ptr));
|
||||
template <typename T, typename Deleter>
|
||||
static inline internal::OwnedWrapper<T, Deleter> Owned(
|
||||
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)
|
||||
// through a Callback. Logically, this signifies a destructive transfer of
|
||||
// the state of the argument into the target function. Invoking
|
||||
// Callback::Run() twice on a Callback that was created with a Passed()
|
||||
// through a RepeatingCallback. Logically, this signifies a destructive transfer
|
||||
// of the state of the argument into the target function. Invoking
|
||||
// RepeatingCallback::Run() twice on a callback that was created with a Passed()
|
||||
// argument will CHECK() because the first invocation would have already
|
||||
// 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.
|
||||
// // 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
|
||||
// // it on Reset().
|
||||
// cb.Reset();
|
||||
//
|
||||
// // |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|
|
||||
// // no longer owns Foo() and, if reset, would not delete Foo().
|
||||
// cb.Run(); // Foo() is now transferred to |arg| and deleted.
|
||||
// cb.Run(); // This CHECK()s since Foo() already been used once.
|
||||
//
|
||||
// We offer 2 syntaxes for calling Passed(). The first takes an rvalue and
|
||||
// is best suited for use with the return value of a function or other temporary
|
||||
// We offer 2 syntaxes for calling Passed(). The first takes an rvalue and is
|
||||
// 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
|
||||
// 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));
|
||||
}
|
||||
|
||||
// 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,
|
||||
// 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():
|
||||
//
|
||||
// int DoSomething(int arg) { cout << arg << endl; }
|
||||
//
|
||||
// // Assign to a Callback with a void return type.
|
||||
// Callback<void(int)> cb = Bind(IgnoreResult(&DoSomething));
|
||||
// cb->Run(1); // Prints "1".
|
||||
// // Assign to a callback with a void return type.
|
||||
// OnceCallback<void(int)> cb = BindOnce(IgnoreResult(&DoSomething));
|
||||
// std::move(cb).Run(1); // Prints "1".
|
||||
//
|
||||
// // Prints "1" on |ml|.
|
||||
// ml->PostTask(FROM_HERE, BindOnce(IgnoreResult(&DoSomething), 1);
|
||||
// // Prints "2" on |ml|.
|
||||
// ml->PostTask(FROM_HERE, BindOnce(IgnoreResult(&DoSomething), 2);
|
||||
template <typename T>
|
||||
static inline internal::IgnoreResultHelper<T> IgnoreResult(T data) {
|
||||
return internal::IgnoreResultHelper<T>(std::move(data));
|
||||
|
@ -447,8 +454,9 @@ static inline internal::IgnoreResultHelper<T> IgnoreResult(T data) {
|
|||
// EXAMPLE OF RetainBlock():
|
||||
//
|
||||
// // Wrap the block and bind it to a callback.
|
||||
// Callback<void(int)> cb = Bind(RetainBlock(^(int n) { NSLog(@"%d", n); }));
|
||||
// cb.Run(1); // Logs "1".
|
||||
// OnceCallback<void(int)> cb =
|
||||
// BindOnce(RetainBlock(^(int n) { NSLog(@"%d", n); }));
|
||||
// std::move(cb).Run(1); // Logs "1".
|
||||
template <typename R, typename... Args>
|
||||
base::mac::ScopedBlock<R (^)(Args...)> RetainBlock(R (^block)(Args...)) {
|
||||
return base::mac::ScopedBlock<R (^)(Args...)>(block,
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
#include "build/build_config.h"
|
||||
|
||||
// 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 {
|
||||
|
||||
|
|
|
@ -104,15 +104,16 @@ struct IgnoreResultHelper {
|
|||
T functor_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
template <typename T, typename Deleter = std::default_delete<T>>
|
||||
class OwnedWrapper {
|
||||
public:
|
||||
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(); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<T> ptr_;
|
||||
std::unique_ptr<T, Deleter> ptr_;
|
||||
};
|
||||
|
||||
// PassedWrapper is a copyable adapter for a scoper that ignores const.
|
||||
|
@ -354,10 +355,9 @@ template <typename Functor, typename SFINAE>
|
|||
struct FunctorTraits;
|
||||
|
||||
// For empty callable types.
|
||||
// This specialization is intended to allow binding captureless lambdas by
|
||||
// base::Bind(), based on the fact that captureless lambdas are empty while
|
||||
// capturing lambdas are not. This also allows any functors as far as it's an
|
||||
// empty class.
|
||||
// This specialization is intended to allow binding captureless lambdas, based
|
||||
// on the fact that captureless lambdas are empty while capturing lambdas are
|
||||
// not. This also allows any functors as far as it's an empty class.
|
||||
// Example:
|
||||
//
|
||||
// // Captureless lambdas are allowed.
|
||||
|
@ -791,10 +791,10 @@ BanUnconstructedRefCountedReceiver(const Receiver& receiver, Unused&&...) {
|
|||
//
|
||||
// scoped_refptr<Foo> oo = Foo::Create();
|
||||
DCHECK(receiver->HasAtLeastOneRef())
|
||||
<< "base::Bind() refuses to create the first reference to ref-counted "
|
||||
"objects. That is typically happens around PostTask() in their "
|
||||
"constructor, and such objects can be destroyed before `new` returns "
|
||||
"if the task resolves fast enough.";
|
||||
<< "base::Bind{Once,Repeating}() refuses to create the first reference "
|
||||
"to ref-counted objects. That typically happens around PostTask() in "
|
||||
"their constructor, and such objects can be destroyed before `new` "
|
||||
"returns if the task resolves fast enough.";
|
||||
}
|
||||
|
||||
// BindState<>
|
||||
|
@ -917,7 +917,7 @@ using MakeBindStateType =
|
|||
// };
|
||||
//
|
||||
// WeakPtr<Foo> oo = nullptr;
|
||||
// base::Bind(&Foo::bar, oo).Run();
|
||||
// base::BindOnce(&Foo::bar, oo).Run();
|
||||
template <typename T>
|
||||
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(); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct BindUnwrapTraits<internal::OwnedWrapper<T>> {
|
||||
static T* Unwrap(const internal::OwnedWrapper<T>& o) { return o.get(); }
|
||||
template <typename T, typename Deleter>
|
||||
struct BindUnwrapTraits<internal::OwnedWrapper<T, Deleter>> {
|
||||
static T* Unwrap(const internal::OwnedWrapper<T, Deleter>& o) {
|
||||
return o.get();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
|
|
|
@ -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_
|
|
@ -2,8 +2,9 @@
|
|||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
// NOTE: Header files that do not require the full definition of Callback or
|
||||
// Closure should #include "base/callback_forward.h" instead of this file.
|
||||
// NOTE: Header files that do not require the full definition of
|
||||
// base::{Once,Repeating}Callback or base::{Once,Repeating}Closure should
|
||||
// #include "base/callback_forward.h" instead of this file.
|
||||
|
||||
#ifndef BASE_CALLBACK_H_
|
||||
#define BASE_CALLBACK_H_
|
||||
|
@ -42,7 +43,7 @@
|
|||
//
|
||||
// Callbacks also support cancellation. A common use is binding the receiver
|
||||
// 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.
|
||||
//
|
||||
// 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& operator=(RepeatingCallback&&) noexcept = default;
|
||||
|
||||
bool Equals(const RepeatingCallback& other) const {
|
||||
bool operator==(const RepeatingCallback& other) const {
|
||||
return EqualsInternal(other);
|
||||
}
|
||||
|
||||
bool operator!=(const RepeatingCallback& other) const {
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
R Run(Args... args) const & {
|
||||
PolymorphicInvoke f =
|
||||
reinterpret_cast<PolymorphicInvoke>(this->polymorphic_invoke());
|
||||
|
@ -135,7 +140,7 @@ class RepeatingCallback<R(Args...)> : public internal::CallbackBaseCopyable {
|
|||
RepeatingCallback cb = std::move(*this);
|
||||
PolymorphicInvoke f =
|
||||
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)...);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -16,8 +16,9 @@ class RepeatingCallback;
|
|||
template <typename Signature>
|
||||
using Callback = RepeatingCallback<Signature>;
|
||||
|
||||
// Syntactic sugar to make Callback<void()> easier to declare since it
|
||||
// will be used in a lot of APIs with delayed execution.
|
||||
// Syntactic sugar to make OnceClosure<void()> and RepeatingClosure<void()>
|
||||
// easier to declare since they will be used in a lot of APIs with delayed
|
||||
// execution.
|
||||
using OnceClosure = OnceCallback<void()>;
|
||||
using RepeatingClosure = RepeatingCallback<void()>;
|
||||
using Closure = Callback<void()>;
|
||||
|
|
|
@ -19,14 +19,19 @@ struct FakeBindState;
|
|||
|
||||
namespace internal {
|
||||
|
||||
class CallbackBase;
|
||||
class CallbackBaseCopyable;
|
||||
|
||||
class BindStateBase;
|
||||
class FinallyExecutorCommon;
|
||||
class ThenAndCatchExecutorCommon;
|
||||
|
||||
template <typename ReturnType>
|
||||
class PostTaskExecutor;
|
||||
|
||||
template <typename Functor, typename... BoundArgs>
|
||||
struct BindState;
|
||||
|
||||
class CallbackBase;
|
||||
class CallbackBaseCopyable;
|
||||
|
||||
struct BindStateBaseRefCountTraits {
|
||||
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
|
||||
// us to shield the Callback class from the types of the bound argument via
|
||||
// "type erasure."
|
||||
// At the base level, the only task is to add reference counting data. Don't use
|
||||
// RefCountedThreadSafe since it requires the destructor to be a virtual method.
|
||||
// Creating a vtable for every BindState template instantiation results in a lot
|
||||
// of bloat. Its only task is to call the destructor which can be done with a
|
||||
// function pointer.
|
||||
// At the base level, the only task is to add reference counting data. Avoid
|
||||
// using or inheriting any virtual functions. Creating a vtable for every
|
||||
// BindState template instantiation results in a lot of bloat. Its only task is
|
||||
// to call the destructor which can be done with a function pointer.
|
||||
class BASE_EXPORT BindStateBase
|
||||
: public RefCountedThreadSafe<BindStateBase, BindStateBaseRefCountTraits> {
|
||||
public:
|
||||
|
@ -135,6 +139,12 @@ class BASE_EXPORT CallbackBase {
|
|||
void Reset();
|
||||
|
||||
protected:
|
||||
friend class FinallyExecutorCommon;
|
||||
friend class ThenAndCatchExecutorCommon;
|
||||
|
||||
template <typename ReturnType>
|
||||
friend class PostTaskExecutor;
|
||||
|
||||
using InvokeFuncStorage = BindStateBase::InvokeFuncStorage;
|
||||
|
||||
// Returns true if this callback equals |other|. |other| may be null.
|
||||
|
|
|
@ -9,6 +9,10 @@
|
|||
|
||||
#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.
|
||||
//
|
||||
// Warning numbers are enumerated at:
|
||||
|
@ -22,8 +26,8 @@
|
|||
|
||||
// MSVC_PUSH_DISABLE_WARNING pushes |n| onto a stack of warnings to be disabled.
|
||||
// The warning remains disabled until popped by MSVC_POP_WARNING.
|
||||
#define MSVC_PUSH_DISABLE_WARNING(n) __pragma(warning(push)) \
|
||||
__pragma(warning(disable:n))
|
||||
#define MSVC_PUSH_DISABLE_WARNING(n) \
|
||||
__pragma(warning(push)) __pragma(warning(disable : n))
|
||||
|
||||
// Pop effects of innermost MSVC_PUSH_* macro.
|
||||
#define MSVC_POP_WARNING() __pragma(warning(pop))
|
||||
|
@ -137,7 +141,7 @@
|
|||
// For member functions, the implicit this parameter counts as index 1.
|
||||
#if defined(COMPILER_GCC) || defined(__clang__)
|
||||
#define PRINTF_FORMAT(format_param, dots_param) \
|
||||
__attribute__((format(printf, format_param, dots_param)))
|
||||
__attribute__((format(printf, format_param, dots_param)))
|
||||
#else
|
||||
#define PRINTF_FORMAT(format_param, dots_param)
|
||||
#endif
|
||||
|
@ -166,14 +170,14 @@
|
|||
// Mark a memory region fully initialized.
|
||||
// Use this to annotate code that deliberately reads uninitialized data, for
|
||||
// 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.
|
||||
// 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
|
||||
// passing data to another process via shared memory.
|
||||
#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
|
||||
#define MSAN_UNPOISON(p, size)
|
||||
#define MSAN_CHECK_MEM_IS_INITIALIZED(p, size)
|
||||
|
@ -238,4 +242,57 @@
|
|||
#define PRETTY_FUNCTION __func__
|
||||
#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_
|
||||
|
|
|
@ -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_
|
|
@ -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_
|
|
@ -14,6 +14,7 @@
|
|||
#include "base/containers/vector_buffer.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/stl_util.h"
|
||||
#include "base/template_util.h"
|
||||
|
||||
// base::circular_deque is similar to std::deque. Unlike std::deque, the
|
||||
|
@ -521,15 +522,15 @@ class circular_deque {
|
|||
return buffer_[i - right_size];
|
||||
}
|
||||
value_type& at(size_type i) {
|
||||
return const_cast<value_type&>(
|
||||
const_cast<const circular_deque*>(this)->at(i));
|
||||
return const_cast<value_type&>(as_const(*this).at(i));
|
||||
}
|
||||
|
||||
value_type& operator[](size_type i) { return at(i); }
|
||||
const value_type& operator[](size_type i) const {
|
||||
return const_cast<circular_deque*>(this)->at(i);
|
||||
value_type& operator[](size_type i) {
|
||||
return const_cast<value_type&>(as_const(*this)[i]);
|
||||
}
|
||||
|
||||
const value_type& operator[](size_type i) const { return at(i); }
|
||||
|
||||
value_type& front() {
|
||||
DCHECK(!empty());
|
||||
return buffer_[begin_];
|
||||
|
|
|
@ -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_
|
|
@ -13,7 +13,6 @@
|
|||
#include <utility>
|
||||
|
||||
#include "base/stl_util.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
#if defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX))
|
||||
#include "base/files/file_util.h"
|
||||
|
@ -28,6 +27,44 @@
|
|||
|
||||
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()
|
||||
: signature_(0),
|
||||
type_(0),
|
||||
|
@ -48,6 +85,7 @@ CPU::CPU()
|
|||
has_avx2_(false),
|
||||
has_aesni_(false),
|
||||
has_non_stop_time_stamp_counter_(false),
|
||||
is_running_in_vm_(false),
|
||||
cpu_vendor_("unknown") {
|
||||
Initialize();
|
||||
}
|
||||
|
@ -156,7 +194,6 @@ void CPU::Initialize() {
|
|||
memcpy(cpu_string, &cpu_info[1], kVendorNameSize);
|
||||
cpu_string[kVendorNameSize] = '\0';
|
||||
cpu_vendor_ = cpu_string;
|
||||
bool hypervisor = false;
|
||||
|
||||
// Interpret CPU feature information.
|
||||
if (num_ids > 0) {
|
||||
|
@ -167,11 +204,9 @@ void CPU::Initialize() {
|
|||
}
|
||||
signature_ = cpu_info[0];
|
||||
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;
|
||||
ext_model_ = (cpu_info[0] >> 16) & 0xf;
|
||||
ext_family_ = (cpu_info[0] >> 20) & 0xff;
|
||||
std::tie(family_, model_, ext_family_, ext_model_) =
|
||||
internal::ComputeX86FamilyAndModel(cpu_vendor_, signature_);
|
||||
has_mmx_ = (cpu_info[3] & 0x00800000) != 0;
|
||||
has_sse_ = (cpu_info[3] & 0x02000000) != 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
|
||||
// announce themselves. Hypervisors trap CPUID and sometimes return
|
||||
// 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
|
||||
// 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;
|
||||
}
|
||||
|
||||
if (!has_non_stop_time_stamp_counter_ && hypervisor) {
|
||||
if (!has_non_stop_time_stamp_counter_ && is_running_in_vm_) {
|
||||
int cpu_info_hv[4] = {};
|
||||
__cpuid(cpu_info_hv, 0x40000000);
|
||||
if (cpu_info_hv[1] == 0x7263694D && // Micr
|
||||
|
@ -251,8 +286,14 @@ void CPU::Initialize() {
|
|||
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();
|
||||
#elif defined(OS_WIN)
|
||||
// Windows makes high-resolution thread timing information available in
|
||||
// user-space.
|
||||
has_non_stop_time_stamp_counter_ = true;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -6,11 +6,25 @@
|
|||
#define BASE_CPU_H_
|
||||
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
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.
|
||||
class BASE_EXPORT CPU final {
|
||||
public:
|
||||
|
@ -52,6 +66,7 @@ class BASE_EXPORT CPU final {
|
|||
bool has_non_stop_time_stamp_counter() const {
|
||||
return has_non_stop_time_stamp_counter_;
|
||||
}
|
||||
bool is_running_in_vm() const { return is_running_in_vm_; }
|
||||
|
||||
IntelMicroArchitecture GetIntelMicroArchitecture() const;
|
||||
const std::string& cpu_brand() const { return cpu_brand_; }
|
||||
|
@ -79,6 +94,7 @@ class BASE_EXPORT CPU final {
|
|||
bool has_avx2_;
|
||||
bool has_aesni_;
|
||||
bool has_non_stop_time_stamp_counter_;
|
||||
bool is_running_in_vm_;
|
||||
std::string cpu_vendor_;
|
||||
std::string cpu_brand_;
|
||||
};
|
||||
|
|
|
@ -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_
|
|
@ -38,6 +38,12 @@ BASE_EXPORT void BreakDebugger();
|
|||
BASE_EXPORT void SetSuppressDebugUI(bool suppress);
|
||||
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 base
|
||||
|
||||
|
|
|
@ -19,29 +19,23 @@
|
|||
#endif // defined(OS_WIN)
|
||||
|
||||
// TODO(peria): Enable profiling on Windows.
|
||||
#if BUILDFLAG(ENABLE_PROFILING) && !defined(NO_TCMALLOC) && !defined(OS_WIN)
|
||||
|
||||
#if BUILDFLAG(USE_NEW_TCMALLOC)
|
||||
#if BUILDFLAG(ENABLE_PROFILING) && BUILDFLAG(USE_TCMALLOC) && !defined(OS_WIN)
|
||||
#include "third_party/tcmalloc/chromium/src/gperftools/profiler.h"
|
||||
#else
|
||||
#include "third_party/tcmalloc/gperftools-2.0/chromium/src/gperftools/profiler.h"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
namespace debug {
|
||||
|
||||
// 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;
|
||||
|
||||
void StartProfiling(const std::string& name) {
|
||||
++profile_count;
|
||||
std::string full_name(name);
|
||||
std::string pid = IntToString(GetCurrentProcId());
|
||||
std::string count = IntToString(profile_count);
|
||||
std::string pid = NumberToString(GetCurrentProcId());
|
||||
std::string count = NumberToString(profile_count);
|
||||
ReplaceSubstringsAfterOffset(&full_name, 0, "{pid}", pid);
|
||||
ReplaceSubstringsAfterOffset(&full_name, 0, "{count}", count);
|
||||
ProfilerStart(full_name.c_str());
|
||||
|
@ -158,7 +152,7 @@ FunctionType FindFunctionInImports(const char* function_name) {
|
|||
base::win::PEImage image(CURRENT_MODULE());
|
||||
|
||||
FunctionSearchContext ctx = { function_name, NULL };
|
||||
image.EnumImportChunks(FindResolutionFunctionInImports, &ctx);
|
||||
image.EnumImportChunks(FindResolutionFunctionInImports, &ctx, nullptr);
|
||||
|
||||
return reinterpret_cast<FunctionType>(ctx.function);
|
||||
}
|
||||
|
|
|
@ -4,10 +4,6 @@
|
|||
|
||||
#include "base/environment.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "base/strings/string_piece.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 env_vars {
|
||||
|
@ -141,97 +120,4 @@ bool Environment::HasVar(StringPiece variable_name) {
|
|||
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
|
||||
|
|
|
@ -38,52 +38,23 @@ class BASE_EXPORT Environment {
|
|||
// Syntactic sugar for GetVar(variable_name, nullptr);
|
||||
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,
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
#if defined(OS_WIN)
|
||||
|
||||
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);
|
||||
|
||||
using NativeEnvironmentString = std::wstring;
|
||||
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||
|
||||
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);
|
||||
|
||||
using NativeEnvironmentString = std::string;
|
||||
#endif
|
||||
using EnvironmentMap =
|
||||
std::map<NativeEnvironmentString, NativeEnvironmentString>;
|
||||
|
||||
} // namespace base
|
||||
|
||||
|
|
|
@ -104,6 +104,7 @@
|
|||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <functional>
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
@ -152,7 +153,7 @@ class BASE_EXPORT FilePath {
|
|||
#if defined(OS_WIN)
|
||||
// On Windows, for Unicode-aware applications, native pathnames are wchar_t
|
||||
// arrays encoded in UTF-16.
|
||||
typedef base::string16 StringType;
|
||||
typedef std::wstring StringType;
|
||||
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||
// 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
|
||||
|
|
|
@ -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
|
|
@ -2,7 +2,11 @@
|
|||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// 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
|
||||
// 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 {
|
||||
|
||||
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);
|
||||
}
|
||||
namespace {
|
||||
|
||||
uint32_t Hash(const std::string& str) {
|
||||
return PersistentHash(str.data(), str.size());
|
||||
}
|
||||
|
||||
uint32_t Hash(const string16& str) {
|
||||
return PersistentHash(str.data(), str.size() * sizeof(char16));
|
||||
}
|
||||
|
||||
uint32_t PersistentHash(const void* data, size_t length) {
|
||||
// This hash function must not change, since it is designed to be persistable
|
||||
// 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());
|
||||
size_t FastHashImpl(base::span<const uint8_t> data) {
|
||||
// We use the updated CityHash within our namespace (not the deprecated
|
||||
// version from third_party/smhasher).
|
||||
#if defined(ARCH_CPU_64_BITS)
|
||||
return base::internal::cityhash_v111::CityHash64(
|
||||
reinterpret_cast<const char*>(data.data()), data.size());
|
||||
#else
|
||||
return base::internal::cityhash_v111::CityHash32(
|
||||
reinterpret_cast<const char*>(data.data()), data.size());
|
||||
#endif
|
||||
}
|
||||
|
||||
// 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
|
||||
//
|
||||
// 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 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:
|
||||
// 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) {
|
||||
size_t HashInts64Impl(uint64_t value1, uint64_t value2) {
|
||||
uint32_t short_random1 = 842304669U;
|
||||
uint32_t short_random2 = 619063811U;
|
||||
uint32_t short_random3 = 937041849U;
|
||||
|
@ -101,4 +89,79 @@ size_t HashInts64(uint64_t value1, uint64_t value2) {
|
|||
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
|
|
@ -2,8 +2,8 @@
|
|||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_HASH_H_
|
||||
#define BASE_HASH_H_
|
||||
#ifndef BASE_HASH_HASH_H_
|
||||
#define BASE_HASH_HASH_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
@ -13,26 +13,42 @@
|
|||
#include <utility>
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/containers/span.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/strings/string16.h"
|
||||
#include "base/strings/string_piece.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
// Computes a hash of a memory buffer. This hash function is subject to change
|
||||
// in the future, so use only for temporary in-memory structures. If you need
|
||||
// to persist a change on disk or between computers, use PersistentHash().
|
||||
//
|
||||
// WARNING: This hash function should not be used for any cryptographic purpose.
|
||||
// WARNING: This hash functions should not be used for any cryptographic
|
||||
// purpose.
|
||||
|
||||
// Deprecated: Computes a hash of a memory buffer, use FastHash() instead.
|
||||
// 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 std::string& 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
|
||||
// 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
|
||||
// new version will have to be added in addition.
|
||||
//
|
||||
// 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 std::string& str);
|
||||
|
||||
|
@ -67,4 +83,4 @@ struct IntPairHash<std::pair<Type1, Type2>> {
|
|||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_HASH_H_
|
||||
#endif // BASE_HASH_HASH_H_
|
|
@ -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_
|
|
@ -43,7 +43,7 @@ Location::Location(const char* function_name,
|
|||
std::string Location::ToString() const {
|
||||
if (has_source_info()) {
|
||||
return std::string(function_name_) + "@" + file_name_ + ":" +
|
||||
IntToString(line_number_);
|
||||
NumberToString(line_number_);
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
||||
#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() {
|
||||
return RETURN_ADDRESS();
|
||||
|
|
|
@ -8,14 +8,29 @@
|
|||
#include <stddef.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/debug/debugging_buildflags.h"
|
||||
#include "base/hash.h"
|
||||
#include "base/hash/hash.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
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
|
||||
// significantly brought to life.
|
||||
class BASE_EXPORT Location {
|
||||
|
@ -73,6 +88,16 @@ class BASE_EXPORT Location {
|
|||
const char* file_name,
|
||||
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:
|
||||
const char* function_name_ = nullptr;
|
||||
const char* file_name_ = nullptr;
|
||||
|
@ -108,7 +133,7 @@ template <>
|
|||
struct hash<::base::Location> {
|
||||
std::size_t operator()(const ::base::Location& loc) const {
|
||||
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)));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <stddef.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
@ -17,13 +18,18 @@
|
|||
#include "base/base_export.h"
|
||||
#include "base/callback_forward.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/scoped_clear_last_error.h"
|
||||
#include "base/strings/string_piece_forward.h"
|
||||
#include "base/template_util.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
#if defined(OS_CHROMEOS)
|
||||
#include <cstdio>
|
||||
#endif
|
||||
|
||||
//
|
||||
// Optional message capabilities
|
||||
// -----------------------------
|
||||
|
@ -162,27 +168,35 @@ namespace logging {
|
|||
|
||||
// TODO(avi): do we want to do a unification of character types here?
|
||||
#if defined(OS_WIN)
|
||||
typedef base::char16 PathChar;
|
||||
typedef wchar_t PathChar;
|
||||
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||
typedef char PathChar;
|
||||
#endif
|
||||
|
||||
// Where to record logging output? A flat file and/or system debug log
|
||||
// via OutputDebugString.
|
||||
enum LoggingDestination {
|
||||
// A bitmask of potential logging destinations.
|
||||
using LoggingDestination = uint32_t;
|
||||
// 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_TO_FILE = 1 << 0,
|
||||
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
|
||||
// it may not even be possible to locate the executable on disk, use
|
||||
// stderr.
|
||||
#if defined(OS_WIN)
|
||||
LOG_DEFAULT = LOG_TO_FILE,
|
||||
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||
// On Windows, use a file next to the exe.
|
||||
// On POSIX platforms, where it may not even be possible to locate the
|
||||
// executable on disk, use stderr.
|
||||
// On Fuchsia, use the Fuchsia logging service.
|
||||
#if defined(OS_FUCHSIA) || defined(OS_NACL)
|
||||
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
|
||||
};
|
||||
|
||||
|
@ -200,21 +214,22 @@ enum LogLockingState { LOCK_LOG_FILE, DONT_LOCK_LOG_FILE };
|
|||
enum OldFileDeletionState { DELETE_OLD_LOG_FILE, APPEND_TO_OLD_LOG_FILE };
|
||||
|
||||
struct BASE_EXPORT LoggingSettings {
|
||||
// The defaults values are:
|
||||
//
|
||||
// logging_dest: LOG_DEFAULT
|
||||
// log_file: NULL
|
||||
// lock_log: LOCK_LOG_FILE
|
||||
// delete_old: APPEND_TO_OLD_LOG_FILE
|
||||
LoggingSettings();
|
||||
// Equivalent to logging destination enum, but allows for multiple
|
||||
// destinations.
|
||||
uint32_t logging_dest = LOG_DEFAULT;
|
||||
|
||||
LoggingDestination logging_dest;
|
||||
|
||||
// The three settings below have an effect only when LOG_TO_FILE is
|
||||
// The four settings below have an effect only when LOG_TO_FILE is
|
||||
// set in |logging_dest|.
|
||||
const PathChar* log_file;
|
||||
LogLockingState lock_log;
|
||||
OldFileDeletionState delete_old;
|
||||
const PathChar* log_file_path = nullptr;
|
||||
LogLockingState lock_log = LOCK_LOG_FILE;
|
||||
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
|
||||
|
@ -300,10 +315,10 @@ BASE_EXPORT void SetShowErrorDialogs(bool enable_dialogs);
|
|||
// however clients can use this function to override with their own handling
|
||||
// (e.g. a silent one for Unit Tests)
|
||||
using LogAssertHandlerFunction =
|
||||
base::Callback<void(const char* file,
|
||||
int line,
|
||||
const base::StringPiece message,
|
||||
const base::StringPiece stack_trace)>;
|
||||
base::RepeatingCallback<void(const char* file,
|
||||
int line,
|
||||
const base::StringPiece message,
|
||||
const base::StringPiece stack_trace)>;
|
||||
|
||||
class BASE_EXPORT ScopedLogAssertHandler {
|
||||
public:
|
||||
|
@ -515,9 +530,9 @@ BASE_EXPORT extern std::ostream* g_swallow_stream;
|
|||
class CheckOpResult {
|
||||
public:
|
||||
// |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.
|
||||
operator bool() const { return !message_; }
|
||||
constexpr operator bool() const { return !message_; }
|
||||
// Returns the message.
|
||||
std::string* message() { return message_; }
|
||||
|
||||
|
@ -525,110 +540,6 @@ class CheckOpResult {
|
|||
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*
|
||||
// controlled by NDEBUG, so the check will be executed regardless of
|
||||
// compilation mode.
|
||||
|
@ -659,26 +570,6 @@ class CheckOpResult {
|
|||
|
||||
#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.
|
||||
#define CHECK(condition) \
|
||||
LAZY_STREAM(::logging::LogMessage(__FILE__, __LINE__, #condition).stream(), \
|
||||
|
@ -688,8 +579,6 @@ class CheckOpResult {
|
|||
LAZY_STREAM(PLOG_STREAM(FATAL), !ANALYZER_ASSUME_TRUE(condition)) \
|
||||
<< "Check failed: " #condition ". "
|
||||
|
||||
#endif // _PREFAST_
|
||||
|
||||
// Helper macro for binary operators.
|
||||
// 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
|
||||
|
@ -718,6 +607,16 @@ MakeCheckOpValueString(std::ostream* os, const T& 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
|
||||
// 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
|
||||
|
@ -786,20 +685,21 @@ std::string* MakeCheckOpString<std::string, std::string>(
|
|||
// The checked condition is wrapped with ANALYZER_ASSUME_TRUE, which under
|
||||
// static analysis builds, blocks analysis of the current path if the
|
||||
// condition is false.
|
||||
#define DEFINE_CHECK_OP_IMPL(name, op) \
|
||||
template <class t1, class t2> \
|
||||
inline std::string* Check##name##Impl(const t1& v1, const t2& v2, \
|
||||
const char* names) { \
|
||||
if (ANALYZER_ASSUME_TRUE(v1 op v2)) \
|
||||
return NULL; \
|
||||
else \
|
||||
return ::logging::MakeCheckOpString(v1, v2, names); \
|
||||
} \
|
||||
inline std::string* Check##name##Impl(int v1, int v2, const char* names) { \
|
||||
if (ANALYZER_ASSUME_TRUE(v1 op v2)) \
|
||||
return NULL; \
|
||||
else \
|
||||
return ::logging::MakeCheckOpString(v1, v2, names); \
|
||||
#define DEFINE_CHECK_OP_IMPL(name, op) \
|
||||
template <class t1, class t2> \
|
||||
constexpr std::string* Check##name##Impl(const t1& v1, const t2& v2, \
|
||||
const char* names) { \
|
||||
if (ANALYZER_ASSUME_TRUE(v1 op v2)) \
|
||||
return nullptr; \
|
||||
else \
|
||||
return ::logging::MakeCheckOpString(v1, v2, names); \
|
||||
} \
|
||||
constexpr std::string* Check##name##Impl(int v1, int v2, \
|
||||
const char* names) { \
|
||||
if (ANALYZER_ASSUME_TRUE(v1 op v2)) \
|
||||
return nullptr; \
|
||||
else \
|
||||
return ::logging::MakeCheckOpString(v1, v2, names); \
|
||||
}
|
||||
DEFINE_CHECK_OP_IMPL(EQ, ==)
|
||||
DEFINE_CHECK_OP_IMPL(NE, !=)
|
||||
|
@ -817,9 +717,9 @@ DEFINE_CHECK_OP_IMPL(GT, > )
|
|||
#define CHECK_GT(val1, val2) CHECK_OP(GT, > , val1, val2)
|
||||
|
||||
#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
|
||||
#define DCHECK_IS_ON() 0
|
||||
#define DCHECK_IS_ON() false
|
||||
#else
|
||||
#define DCHECK_IS_ON() 1
|
||||
#define DCHECK_IS_ON() true
|
||||
#endif
|
||||
|
||||
// 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
|
||||
// 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()
|
||||
|
||||
#define DCHECK(condition) \
|
||||
|
@ -916,8 +801,6 @@ const LogSeverity LOG_DCHECK = LOG_FATAL;
|
|||
|
||||
#endif // DCHECK_IS_ON()
|
||||
|
||||
#endif // defined(_PREFAST_) && defined(OS_WIN)
|
||||
|
||||
// Helper macro for binary operators.
|
||||
// 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
|
||||
|
@ -982,7 +865,7 @@ const LogSeverity LOG_DCHECK = LOG_FATAL;
|
|||
#define DCHECK_GE(val1, val2) DCHECK_OP(GE, >=, 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
|
||||
// call overhead down to a minimum.
|
||||
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.
|
||||
const char* file_;
|
||||
const int line_;
|
||||
const char* file_basename_;
|
||||
|
||||
// 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
|
||||
|
@ -1117,6 +1001,14 @@ class BASE_EXPORT ErrnoLogMessage {
|
|||
// after this call.
|
||||
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.
|
||||
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();
|
||||
|
||||
// Returns the default log file path.
|
||||
BASE_EXPORT base::string16 GetLogFileFullPath();
|
||||
BASE_EXPORT std::wstring GetLogFileFullPath();
|
||||
#endif
|
||||
|
||||
} // namespace logging
|
||||
|
@ -1173,18 +1065,13 @@ inline std::ostream& operator<<(std::ostream& out, const std::wstring& wstr) {
|
|||
#define NOTIMPLEMENTED_MSG "NOT IMPLEMENTED"
|
||||
#endif
|
||||
|
||||
#if defined(OS_ANDROID) && defined(OFFICIAL_BUILD)
|
||||
#define NOTIMPLEMENTED() EAT_STREAM_PARAMETERS
|
||||
#define NOTIMPLEMENTED_LOG_ONCE() EAT_STREAM_PARAMETERS
|
||||
#else
|
||||
#define NOTIMPLEMENTED() LOG(ERROR) << NOTIMPLEMENTED_MSG
|
||||
#define NOTIMPLEMENTED_LOG_ONCE() \
|
||||
do { \
|
||||
static bool logged_once = false; \
|
||||
LOG_IF(ERROR, !logged_once) << NOTIMPLEMENTED_MSG; \
|
||||
logged_once = true; \
|
||||
} while (0); \
|
||||
#define NOTIMPLEMENTED() DLOG(ERROR) << NOTIMPLEMENTED_MSG
|
||||
#define NOTIMPLEMENTED_LOG_ONCE() \
|
||||
do { \
|
||||
static bool logged_once = false; \
|
||||
DLOG_IF(ERROR, !logged_once) << NOTIMPLEMENTED_MSG; \
|
||||
logged_once = true; \
|
||||
} while (0); \
|
||||
EAT_STREAM_PARAMETERS
|
||||
#endif
|
||||
|
||||
#endif // BASE_LOGGING_H_
|
||||
|
|
|
@ -10,6 +10,10 @@
|
|||
#ifndef 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.
|
||||
#define DISALLOW_COPY(TypeName) \
|
||||
TypeName(const TypeName&) = delete
|
||||
|
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -4,6 +4,9 @@
|
|||
|
||||
#include "base/memory/ref_counted.h"
|
||||
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
#include "base/threading/thread_collision_warner.h"
|
||||
|
||||
namespace base {
|
||||
|
@ -32,15 +35,35 @@ RefCountedThreadSafeBase::~RefCountedThreadSafeBase() {
|
|||
}
|
||||
#endif
|
||||
|
||||
// This is a security check. In 32-bit-archs, an attacker would run out of
|
||||
// address space after allocating at most 2^32 scoped_refptrs. This replicates
|
||||
// that boundary for 64-bit-archs.
|
||||
// For security and correctness, we check the arithmetic on ref counts.
|
||||
//
|
||||
// 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)
|
||||
void RefCountedBase::AddRefImpl() const {
|
||||
// Check if |ref_count_| overflow only on 64 bit archs since the number of
|
||||
// objects may exceed 2^32.
|
||||
// To avoid the binary size bloat, use non-inline function here.
|
||||
CHECK(++ref_count_ > 0);
|
||||
// An attacker could induce use-after-free bugs, and potentially exploit them,
|
||||
// by creating so many references to a ref-counted object that the reference
|
||||
// count overflows. On 32-bit architectures, there is not enough address space
|
||||
// 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
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ class BASE_EXPORT RefCountedBase {
|
|||
|
||||
// Returns true if the object should self-delete.
|
||||
bool Release() const {
|
||||
--ref_count_;
|
||||
ReleaseImpl();
|
||||
|
||||
// TODO(maruel): Add back once it doesn't assert 500 times/sec.
|
||||
// Current thread books the critical section "AddRelease"
|
||||
|
@ -126,8 +126,10 @@ class BASE_EXPORT RefCountedBase {
|
|||
|
||||
#if defined(ARCH_CPU_64_BITS)
|
||||
void AddRefImpl() const;
|
||||
void ReleaseImpl() const;
|
||||
#else
|
||||
void AddRefImpl() const { ++ref_count_; }
|
||||
void ReleaseImpl() const { --ref_count_; }
|
||||
#endif
|
||||
|
||||
#if DCHECK_IS_ON()
|
||||
|
@ -135,6 +137,8 @@ class BASE_EXPORT RefCountedBase {
|
|||
#endif
|
||||
|
||||
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()
|
||||
mutable bool needs_adopt_ref_ = false;
|
||||
|
@ -444,6 +448,16 @@ class RefCountedData
|
|||
~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
|
||||
|
||||
#endif // BASE_MEMORY_REF_COUNTED_H_
|
||||
|
|
|
@ -25,10 +25,17 @@ class RefCounted;
|
|||
template <class, typename>
|
||||
class RefCountedThreadSafe;
|
||||
class SequencedTaskRunner;
|
||||
class WrappedPromise;
|
||||
|
||||
template <typename T>
|
||||
scoped_refptr<T> AdoptRef(T* t);
|
||||
|
||||
namespace internal {
|
||||
|
||||
class BasePromise;
|
||||
|
||||
} // namespace internal
|
||||
|
||||
namespace subtle {
|
||||
|
||||
enum AdoptRefTag { kAdoptRefTag };
|
||||
|
@ -171,8 +178,16 @@ class scoped_refptr {
|
|||
|
||||
constexpr scoped_refptr() = default;
|
||||
|
||||
// Constructs from raw pointer. constexpr if |p| is null.
|
||||
constexpr scoped_refptr(T* p) : ptr_(p) {
|
||||
// Allow implicit construction from nullptr.
|
||||
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_)
|
||||
AddRef(ptr_);
|
||||
}
|
||||
|
@ -221,6 +236,11 @@ class scoped_refptr {
|
|||
return ptr_;
|
||||
}
|
||||
|
||||
scoped_refptr& operator=(std::nullptr_t) {
|
||||
reset();
|
||||
return *this;
|
||||
}
|
||||
|
||||
scoped_refptr& operator=(T* p) { return *this = scoped_refptr(p); }
|
||||
|
||||
// Unified assignment operator.
|
||||
|
@ -260,6 +280,11 @@ class scoped_refptr {
|
|||
friend scoped_refptr<U> base::AdoptRef(U*);
|
||||
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
|
||||
// caller is responsible for managing the lifetime of the reference.
|
||||
T* release();
|
||||
|
|
|
@ -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_
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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
|
|
@ -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_
|
|
@ -16,14 +16,13 @@
|
|||
//
|
||||
// class Controller {
|
||||
// public:
|
||||
// Controller() : weak_factory_(this) {}
|
||||
// void SpawnWorker() { Worker::StartNew(weak_factory_.GetWeakPtr()); }
|
||||
// void WorkComplete(const Result& result) { ... }
|
||||
// private:
|
||||
// // Member variables should appear before the WeakPtrFactory, to ensure
|
||||
// // that any WeakPtrs to Controller are invalidated before its members
|
||||
// // variable's destructors are executed, rendering them invalid.
|
||||
// WeakPtrFactory<Controller> weak_factory_;
|
||||
// WeakPtrFactory<Controller> weak_factory_{this};
|
||||
// };
|
||||
//
|
||||
// class Worker {
|
||||
|
@ -117,9 +116,9 @@ class BASE_EXPORT WeakReference {
|
|||
explicit WeakReference(const scoped_refptr<Flag>& flag);
|
||||
~WeakReference();
|
||||
|
||||
WeakReference(WeakReference&& other);
|
||||
WeakReference(WeakReference&& other) noexcept;
|
||||
WeakReference(const WeakReference& other);
|
||||
WeakReference& operator=(WeakReference&& other) = default;
|
||||
WeakReference& operator=(WeakReference&& other) noexcept = default;
|
||||
WeakReference& operator=(const WeakReference& other) = default;
|
||||
|
||||
bool IsValid() const;
|
||||
|
@ -141,7 +140,7 @@ class BASE_EXPORT WeakReferenceOwner {
|
|||
void Invalidate();
|
||||
|
||||
private:
|
||||
mutable scoped_refptr<WeakReference::Flag> flag_;
|
||||
scoped_refptr<WeakReference::Flag> flag_;
|
||||
};
|
||||
|
||||
// This class simplifies the implementation of WeakPtr's type conversion
|
||||
|
@ -154,9 +153,9 @@ class BASE_EXPORT WeakPtrBase {
|
|||
~WeakPtrBase();
|
||||
|
||||
WeakPtrBase(const WeakPtrBase& other) = default;
|
||||
WeakPtrBase(WeakPtrBase&& other) = default;
|
||||
WeakPtrBase(WeakPtrBase&& other) noexcept = default;
|
||||
WeakPtrBase& operator=(const WeakPtrBase& other) = default;
|
||||
WeakPtrBase& operator=(WeakPtrBase&& other) = default;
|
||||
WeakPtrBase& operator=(WeakPtrBase&& other) noexcept = default;
|
||||
|
||||
void reset() {
|
||||
ref_ = internal::WeakReference();
|
||||
|
@ -237,7 +236,7 @@ class WeakPtr : public internal::WeakPtrBase {
|
|||
ptr_ = reinterpret_cast<uintptr_t>(t);
|
||||
}
|
||||
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
|
||||
// inheritance. This also enforces the "U is a T" rule.
|
||||
T* t = reinterpret_cast<U*>(other.ptr_);
|
||||
|
|
|
@ -192,12 +192,14 @@ constexpr ClampedNumeric<typename UnderlyingType<T>::type> MakeClampedNum(
|
|||
return value;
|
||||
}
|
||||
|
||||
#if !BASE_NUMERICS_DISABLE_OSTREAM_OPERATORS
|
||||
// Overload the ostream output operator to make logging work nicely.
|
||||
template <typename T>
|
||||
std::ostream& operator<<(std::ostream& os, const ClampedNumeric<T>& value) {
|
||||
os << static_cast<T>(value);
|
||||
return os;
|
||||
}
|
||||
#endif
|
||||
|
||||
// These implement the variadic wrapper for the math operations.
|
||||
template <template <typename, typename, typename> class M,
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
#include <stddef.h>
|
||||
|
||||
#include <limits>
|
||||
#include <ostream>
|
||||
#include <type_traits>
|
||||
|
||||
#include "base/numerics/safe_conversions_impl.h"
|
||||
|
@ -20,6 +19,10 @@
|
|||
#define BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS (0)
|
||||
#endif
|
||||
|
||||
#if !BASE_NUMERICS_DISABLE_OSTREAM_OPERATORS
|
||||
#include <ostream>
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
namespace internal {
|
||||
|
||||
|
@ -308,12 +311,14 @@ constexpr StrictNumeric<typename UnderlyingType<T>::type> MakeStrictNum(
|
|||
return value;
|
||||
}
|
||||
|
||||
#if !BASE_NUMERICS_DISABLE_OSTREAM_OPERATORS
|
||||
// Overload the ostream output operator to make logging work nicely.
|
||||
template <typename T>
|
||||
std::ostream& operator<<(std::ostream& os, const StrictNumeric<T>& value) {
|
||||
os << static_cast<T>(value);
|
||||
return os;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define BASE_NUMERIC_COMPARISON_OPERATORS(CLASS, NAME, OP) \
|
||||
template <typename L, typename R, \
|
||||
|
|
|
@ -80,8 +80,9 @@ template <typename T>
|
|||
constexpr typename std::make_unsigned<T>::type SafeUnsignedAbs(T value) {
|
||||
static_assert(std::is_integral<T>::value, "Type must be integral");
|
||||
using UnsignedT = typename std::make_unsigned<T>::type;
|
||||
return IsValueNegative(value) ? 0 - static_cast<UnsignedT>(value)
|
||||
: static_cast<UnsignedT>(value);
|
||||
return IsValueNegative(value)
|
||||
? static_cast<UnsignedT>(0u - static_cast<UnsignedT>(value))
|
||||
: static_cast<UnsignedT>(value);
|
||||
}
|
||||
|
||||
// This allows us to switch paths on known compile-time constants.
|
||||
|
|
|
@ -17,12 +17,15 @@
|
|||
|
||||
#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.
|
||||
#if !defined(__native_client__) && \
|
||||
((defined(__clang__) && \
|
||||
((__clang_major__ > 3) || \
|
||||
(__clang_major__ == 3 && __clang_minor__ >= 4))) || \
|
||||
(defined(__GNUC__) && __GNUC__ >= 5))
|
||||
#elif !defined(__native_client__) && \
|
||||
((defined(__clang__) && \
|
||||
((__clang_major__ > 3) || \
|
||||
(__clang_major__ == 3 && __clang_minor__ >= 4))) || \
|
||||
(defined(__GNUC__) && __GNUC__ >= 5))
|
||||
#include "base/numerics/safe_math_clang_gcc_impl.h"
|
||||
#define BASE_HAS_OPTIMIZED_SAFE_MATH (1)
|
||||
#else
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
#ifndef BASE_OPTIONAL_H_
|
||||
#define BASE_OPTIONAL_H_
|
||||
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/template_util.h"
|
||||
#include "base/thread_annotations.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
|
@ -270,9 +270,7 @@ class OptionalBase {
|
|||
storage_.Init(std::forward<U>(value));
|
||||
}
|
||||
|
||||
// TODO(lukasza): Figure out how to remove the NO_THREAD_SAFETY_ANALYSIS
|
||||
// annotation below. See https://crbug.com/881875#c1 for details.
|
||||
void FreeIfNeeded() NO_THREAD_SAFETY_ANALYSIS {
|
||||
void FreeIfNeeded() {
|
||||
if (!storage_.is_populated_)
|
||||
return;
|
||||
storage_.value_.~T();
|
||||
|
@ -429,6 +427,28 @@ class OPTIONAL_DECLSPEC_EMPTY_BASES Optional
|
|||
std::is_copy_assignable<T>::value>,
|
||||
public internal::MoveAssignable<std::is_move_constructible<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:
|
||||
#undef OPTIONAL_DECLSPEC_EMPTY_BASES
|
||||
using value_type = T;
|
||||
|
@ -570,32 +590,32 @@ class OPTIONAL_DECLSPEC_EMPTY_BASES Optional
|
|||
}
|
||||
|
||||
constexpr const T* operator->() const {
|
||||
DCHECK(storage_.is_populated_);
|
||||
CHECK(storage_.is_populated_);
|
||||
return &storage_.value_;
|
||||
}
|
||||
|
||||
constexpr T* operator->() {
|
||||
DCHECK(storage_.is_populated_);
|
||||
CHECK(storage_.is_populated_);
|
||||
return &storage_.value_;
|
||||
}
|
||||
|
||||
constexpr const T& operator*() const & {
|
||||
DCHECK(storage_.is_populated_);
|
||||
CHECK(storage_.is_populated_);
|
||||
return storage_.value_;
|
||||
}
|
||||
|
||||
constexpr T& operator*() & {
|
||||
DCHECK(storage_.is_populated_);
|
||||
CHECK(storage_.is_populated_);
|
||||
return storage_.value_;
|
||||
}
|
||||
|
||||
constexpr const T&& operator*() const && {
|
||||
DCHECK(storage_.is_populated_);
|
||||
CHECK(storage_.is_populated_);
|
||||
return std::move(storage_.value_);
|
||||
}
|
||||
|
||||
constexpr T&& operator*() && {
|
||||
DCHECK(storage_.is_populated_);
|
||||
CHECK(storage_.is_populated_);
|
||||
return std::move(storage_.value_);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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_
|
|
@ -29,6 +29,7 @@ const DWORD kNormalTerminationExitCode = 0;
|
|||
const DWORD kDebuggerInactiveExitCode = 0xC0000354;
|
||||
const DWORD kKeyboardInterruptExitCode = 0xC000013A;
|
||||
const DWORD kDebuggerTerminatedExitCode = 0x40010004;
|
||||
const DWORD kStatusInvalidImageHashExitCode = 0xC0000428;
|
||||
|
||||
// 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
|
||||
|
@ -46,6 +47,7 @@ const DWORD kProcessKilledExitCode = 1;
|
|||
// exit code arguments to KillProcess*(), use platform/application
|
||||
// specific values instead.
|
||||
enum TerminationStatus {
|
||||
// clang-format off
|
||||
TERMINATION_STATUS_NORMAL_TERMINATION, // zero exit status
|
||||
TERMINATION_STATUS_ABNORMAL_TERMINATION, // non-zero exit status
|
||||
TERMINATION_STATUS_PROCESS_WAS_KILLED, // e.g. SIGKILL or task manager kill
|
||||
|
@ -64,7 +66,12 @@ enum TerminationStatus {
|
|||
#endif
|
||||
TERMINATION_STATUS_LAUNCH_FAILED, // child process never launched
|
||||
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
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
// Attempts to kill all the processes on the current machine that were launched
|
||||
|
|
|
@ -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_
|
|
@ -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_
|
|
@ -53,6 +53,46 @@ const ProcessId kNullProcessId = 0;
|
|||
#define CrPRIdPid "d"
|
||||
#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.
|
||||
// Note that on some platforms, this is not guaranteed to be unique across
|
||||
// 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
|
||||
// currently running processes within the chrome session, but IDs of terminated
|
||||
// processes may be reused. This returns an opaque value that is different from
|
||||
// a process's PID.
|
||||
BASE_EXPORT uint32_t GetUniqueIdForProcess();
|
||||
// processes may be reused.
|
||||
BASE_EXPORT UniqueProcId GetUniqueIdForProcess();
|
||||
|
||||
#if defined(OS_LINUX)
|
||||
// When a process is started in a different PID namespace from the browser
|
||||
|
|
|
@ -21,8 +21,14 @@ ProcessHandle GetCurrentProcessHandle() {
|
|||
}
|
||||
|
||||
ProcessId GetProcId(ProcessHandle process) {
|
||||
if (process == base::kNullProcessHandle)
|
||||
return 0;
|
||||
// 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) {
|
||||
|
|
|
@ -8,9 +8,12 @@
|
|||
#include "base/compiler_specific.h"
|
||||
#include "base/logging.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
|
||||
// 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
|
||||
// unused member doesn't result in an extra byte (four when padded) per
|
||||
|
@ -42,20 +45,45 @@
|
|||
// void MyMethod() {
|
||||
// DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
|
||||
// ... (do stuff) ...
|
||||
// MyOtherMethod();
|
||||
// }
|
||||
//
|
||||
// void MyOtherMethod()
|
||||
// VALID_CONTEXT_REQUIRED(my_sequence_checker_) {
|
||||
// foo_ = 42;
|
||||
// }
|
||||
//
|
||||
// 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_);
|
||||
// }
|
||||
|
||||
#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()
|
||||
#define SEQUENCE_CHECKER(name) base::SequenceChecker name
|
||||
#define DCHECK_CALLED_ON_VALID_SEQUENCE(name) \
|
||||
DCHECK((name).CalledOnValidSequence())
|
||||
#define DCHECK_CALLED_ON_VALID_SEQUENCE(name, ...) \
|
||||
base::ScopedValidateSequenceChecker SEQUENCE_CHECKER_INTERNAL_UID( \
|
||||
scoped_validate_sequence_checker_)(name, ##__VA_ARGS__);
|
||||
#define DETACH_FROM_SEQUENCE(name) (name).DetachFromSequence()
|
||||
#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 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)
|
||||
#endif // DCHECK_IS_ON()
|
||||
|
||||
|
@ -65,9 +93,18 @@ namespace base {
|
|||
//
|
||||
// Note: You should almost always use the SequenceChecker class (through the
|
||||
// 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:
|
||||
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; }
|
||||
void DetachFromSequence() {}
|
||||
|
||||
|
@ -83,6 +120,24 @@ class SequenceChecker : public SequenceCheckerDoNothing {
|
|||
};
|
||||
#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
|
||||
|
||||
#endif // BASE_SEQUENCE_CHECKER_H_
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "base/compiler_specific.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/synchronization/lock.h"
|
||||
#include "base/thread_annotations.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
|
@ -20,11 +21,22 @@ namespace base {
|
|||
//
|
||||
// Note: You should almost always use the SequenceChecker class to get the right
|
||||
// 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:
|
||||
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
|
||||
// the constructor.
|
||||
bool CalledOnValidSequence() const WARN_UNUSED_RESULT;
|
||||
|
@ -36,9 +48,12 @@ class BASE_EXPORT SequenceCheckerImpl {
|
|||
private:
|
||||
class Core;
|
||||
|
||||
// Guards all variables below.
|
||||
// Calls straight to ThreadLocalStorage::HasBeenDestroyed(). Exposed purely
|
||||
// for 'friend' to work.
|
||||
static bool HasThreadLocalStorageBeenDestroyed();
|
||||
|
||||
mutable Lock lock_;
|
||||
mutable std::unique_ptr<Core> core_;
|
||||
mutable std::unique_ptr<Core> core_ GUARDED_BY(lock_);
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(SequenceCheckerImpl);
|
||||
};
|
||||
|
|
|
@ -152,6 +152,19 @@ class BASE_EXPORT SequencedTaskRunner : public TaskRunner {
|
|||
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:
|
||||
~SequencedTaskRunner() override = default;
|
||||
|
||||
|
|
|
@ -17,12 +17,15 @@
|
|||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/optional.h"
|
||||
#include "base/template_util.h"
|
||||
|
||||
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
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
// 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.
|
||||
// Works for std::priority_queue, std::queue, and std::stack.
|
||||
template <class A>
|
||||
|
@ -134,39 +198,250 @@ STLCount(const Container& container, const T& val) {
|
|||
return std::count(container.begin(), container.end(), val);
|
||||
}
|
||||
|
||||
// Test to see if a set or map contains a particular key.
|
||||
// Returns true if the key is in the collection.
|
||||
template <typename Collection, typename Key>
|
||||
bool ContainsKey(const Collection& collection, const Key& key) {
|
||||
return collection.find(key) != collection.end();
|
||||
// General purpose implementation to check if |container| contains |value|.
|
||||
template <typename Container,
|
||||
typename Value,
|
||||
std::enable_if_t<
|
||||
!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 {
|
||||
|
||||
template <typename Collection>
|
||||
class HasKeyType {
|
||||
template <typename C>
|
||||
static std::true_type test(typename C::key_type*);
|
||||
template <typename C>
|
||||
static std::false_type test(...);
|
||||
template <typename Map, typename Key, typename Value>
|
||||
std::pair<typename Map::iterator, bool> InsertOrAssignImpl(Map& map,
|
||||
Key&& key,
|
||||
Value&& value) {
|
||||
auto lower = map.lower_bound(key);
|
||||
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:
|
||||
static constexpr bool value = decltype(test<Collection>(nullptr))::value;
|
||||
};
|
||||
// key did not yet exist, insert it.
|
||||
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
|
||||
|
||||
// Test to see if a collection like a vector contains a particular value.
|
||||
// Returns true if the value is in the collection.
|
||||
// Don't use this on collections such as sets or maps. This is enforced by
|
||||
// disabling this method if the collection defines a key_type.
|
||||
template <typename Collection,
|
||||
typename Value,
|
||||
typename std::enable_if<!internal::HasKeyType<Collection>::value,
|
||||
int>::type = 0>
|
||||
bool ContainsValue(const Collection& collection, const Value& value) {
|
||||
return std::find(std::begin(collection), std::end(collection), value) !=
|
||||
std::end(collection);
|
||||
// Implementation of C++17's std::map::insert_or_assign as a free function.
|
||||
template <typename Map, typename Value>
|
||||
std::pair<typename Map::iterator, bool>
|
||||
InsertOrAssign(Map& map, const typename Map::key_type& key, Value&& value) {
|
||||
return internal::InsertOrAssignImpl(map, key, std::forward<Value>(value));
|
||||
}
|
||||
|
||||
template <typename Map, typename Value>
|
||||
std::pair<typename Map::iterator, bool>
|
||||
InsertOrAssign(Map& map, typename Map::key_type&& key, Value&& value) {
|
||||
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.
|
||||
|
|
|
@ -13,10 +13,11 @@
|
|||
#include <type_traits>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/no_destructor.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/third_party/dmg_fp/dmg_fp.h"
|
||||
#include "base/third_party/double_conversion/double-conversion/double-conversion.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
|
@ -360,21 +361,29 @@ string16 NumberToString16(unsigned long long 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) {
|
||||
// According to g_fmt.cc, it is sufficient to declare a buffer of size 32.
|
||||
char buffer[32];
|
||||
dmg_fp::g_fmt(buffer, value);
|
||||
return std::string(buffer);
|
||||
double_conversion::StringBuilder builder(buffer, sizeof(buffer));
|
||||
GetDoubleToStringConverter()->ToShortest(value, &builder);
|
||||
return std::string(buffer, builder.position());
|
||||
}
|
||||
|
||||
base::string16 NumberToString16(double value) {
|
||||
// According to g_fmt.cc, it is sufficient to declare a buffer of size 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
|
||||
// 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) {
|
||||
|
@ -417,35 +426,37 @@ bool StringToSizeT(StringPiece16 input, size_t* output) {
|
|||
return String16ToIntImpl(input, output);
|
||||
}
|
||||
|
||||
bool StringToDouble(const std::string& input, double* output) {
|
||||
// Thread-safe? It is on at least Mac, Linux, and Windows.
|
||||
internal::ScopedClearLastError clear_errno;
|
||||
template <typename STRING, typename CHAR>
|
||||
bool StringToDoubleImpl(STRING input, const CHAR* data, double* output) {
|
||||
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;
|
||||
*output = dmg_fp::strtod(input.c_str(), &endptr);
|
||||
int processed_characters_count;
|
||||
*output = converter->StringToDouble(data, input.size(),
|
||||
&processed_characters_count);
|
||||
|
||||
// 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 endptr does not point to the end of the string, there are either
|
||||
// characters remaining in the string after a parsed number, or the string
|
||||
// does not begin with a parseable number. endptr is compared to the
|
||||
// expected end given the string's stated length to correctly catch cases
|
||||
// where the string contains embedded NUL characters.
|
||||
// - If the value saturated to HUGE_VAL.
|
||||
// - If the entire string was not processed, there are either characters
|
||||
// remaining in the string after a parsed number, or the string does not
|
||||
// begin with a parseable number.
|
||||
// - If the first character is a space, there was leading whitespace
|
||||
return errno == 0 &&
|
||||
!input.empty() &&
|
||||
input.c_str() + input.length() == endptr &&
|
||||
!isspace(input[0]);
|
||||
return !input.empty() && *output != HUGE_VAL && *output != -HUGE_VAL &&
|
||||
static_cast<size_t>(processed_characters_count) == input.size() &&
|
||||
!IsUnicodeWhitespace(input[0]);
|
||||
}
|
||||
|
||||
// Note: if you need to add String16ToDouble, first ask yourself if it's
|
||||
// really necessary. If it is, probably the best implementation here is to
|
||||
// convert to 8-bit and then use the 8-bit version.
|
||||
bool StringToDouble(StringPiece input, double* output) {
|
||||
return StringToDoubleImpl(input, input.data(), output);
|
||||
}
|
||||
|
||||
// Note: if you need to add an iterator range version of StringToDouble, first
|
||||
// ask yourself if it's really necessary. If it is, probably the best
|
||||
// implementation here is to instantiate a string and use the string version.
|
||||
bool StringToDouble(StringPiece16 input, double* output) {
|
||||
return StringToDoubleImpl(
|
||||
input, reinterpret_cast<const uint16_t*>(input.data()), output);
|
||||
}
|
||||
|
||||
std::string HexEncode(const void* bytes, size_t size) {
|
||||
static const char kHexChars[] = "0123456789ABCDEF";
|
||||
|
@ -461,6 +472,10 @@ std::string HexEncode(const void* bytes, size_t size) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
std::string HexEncode(base::span<const uint8_t> bytes) {
|
||||
return HexEncode(bytes.data(), bytes.size());
|
||||
}
|
||||
|
||||
bool HexStringToInt(StringPiece input, int* output) {
|
||||
return IteratorRangeToNumber<HexIteratorRangeToIntTraits>::Invoke(
|
||||
input.begin(), input.end(), output);
|
||||
|
@ -481,7 +496,8 @@ bool HexStringToUInt64(StringPiece input, uint64_t* 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);
|
||||
size_t count = input.size();
|
||||
if (count == 0 || (count % 2) != 0)
|
||||
|
@ -498,4 +514,32 @@ bool HexStringToBytes(StringPiece input, std::vector<uint8_t>* output) {
|
|||
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
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <vector>
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/containers/span.h"
|
||||
#include "base/strings/string16.h"
|
||||
#include "base/strings/string_piece.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 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 ------------------------------------------------
|
||||
|
||||
// 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.
|
||||
// WARNING: Will write to |output| even when returning false.
|
||||
// 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 ----------------------------------------------------------------
|
||||
|
||||
|
@ -131,6 +110,7 @@ BASE_EXPORT bool StringToDouble(const std::string& input, double* output);
|
|||
// max size for |size| should be is
|
||||
// std::numeric_limits<size_t>::max() / 2
|
||||
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.
|
||||
// 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,
|
||||
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
|
||||
|
||||
#endif // BASE_STRINGS_STRING_NUMBER_CONVERSIONS_H_
|
||||
|
|
|
@ -52,22 +52,6 @@ std::ostream& operator<<(std::ostream& o, const StringPiece16& piece) {
|
|||
|
||||
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>
|
||||
void AppendToStringT(const BasicStringPiece<STR>& self, STR* target) {
|
||||
if (!self.empty())
|
||||
|
@ -219,8 +203,11 @@ size_t find_first_of(const StringPiece& self,
|
|||
size_t find_first_of(const StringPiece16& self,
|
||||
const StringPiece16& s,
|
||||
size_t pos) {
|
||||
// Use the faster std::find() if searching for a single character.
|
||||
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())
|
||||
return StringPiece16::npos;
|
||||
return found - self.begin();
|
||||
|
@ -435,16 +422,5 @@ StringPiece16 substr(const StringPiece16& self,
|
|||
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 base
|
||||
|
|
|
@ -47,9 +47,6 @@ namespace base {
|
|||
// template internal to the .cc file.
|
||||
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 StringPiece16& self, string16* target);
|
||||
|
||||
|
@ -141,21 +138,12 @@ BASE_EXPORT StringPiece16 substr(const StringPiece16& self,
|
|||
size_t pos,
|
||||
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
|
||||
|
||||
// BasicStringPiece ------------------------------------------------------------
|
||||
|
||||
// Defines the types, methods, operators, and data members common to both
|
||||
// StringPiece and StringPiece16. Do not refer to this class directly, but
|
||||
// rather to BasicStringPiece, StringPiece, or StringPiece16.
|
||||
// StringPiece and StringPiece16.
|
||||
//
|
||||
// This is templatized by string class type rather than character type, so
|
||||
// BasicStringPiece<std::string> or BasicStringPiece<base::string16>.
|
||||
|
@ -190,11 +178,7 @@ template <typename STRING_TYPE> class BasicStringPiece {
|
|||
: ptr_(offset), length_(len) {}
|
||||
BasicStringPiece(const typename STRING_TYPE::const_iterator& begin,
|
||||
const typename STRING_TYPE::const_iterator& end) {
|
||||
#if DCHECK_IS_ON()
|
||||
// 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
|
||||
DCHECK(begin <= end) << "StringPiece iterators swapped or invalid.";
|
||||
length_ = static_cast<size_t>(std::distance(begin, end));
|
||||
|
||||
// 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_; }
|
||||
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 {
|
||||
CHECK(i < length_);
|
||||
return ptr_[i];
|
||||
|
@ -280,12 +251,6 @@ template <typename STRING_TYPE> class BasicStringPiece {
|
|||
size_type max_size() 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 {
|
||||
internal::AppendToString(*this, target);
|
||||
}
|
||||
|
|
|
@ -14,27 +14,15 @@ namespace base {
|
|||
|
||||
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.
|
||||
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>() {
|
||||
return kWhitespaceUTF16;
|
||||
}
|
||||
|
@ -42,37 +30,12 @@ template<> StringPiece WhitespaceForType<std::string>() {
|
|||
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
|
||||
// the corresponding string or StringPiece output, and can take single- or
|
||||
// multiple-character delimiters.
|
||||
//
|
||||
// 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>
|
||||
// the corresponding string or StringPiece output.
|
||||
template <typename OutputStringType, typename Str>
|
||||
static std::vector<OutputStringType> SplitStringT(
|
||||
BasicStringPiece<Str> str,
|
||||
DelimiterType delimiter,
|
||||
BasicStringPiece<Str> delimiter,
|
||||
WhitespaceHandling whitespace,
|
||||
SplitResult result_type) {
|
||||
std::vector<OutputStringType> result;
|
||||
|
@ -81,7 +44,7 @@ static std::vector<OutputStringType> SplitStringT(
|
|||
|
||||
size_t start = 0;
|
||||
while (start != Str::npos) {
|
||||
size_t end = FindFirstOf(str, delimiter, start);
|
||||
size_t end = str.find_first_of(delimiter, start);
|
||||
|
||||
BasicStringPiece<Str> piece;
|
||||
if (end == Str::npos) {
|
||||
|
@ -96,7 +59,7 @@ static std::vector<OutputStringType> SplitStringT(
|
|||
piece = TrimString(piece, WhitespaceForType<Str>(), TRIM_ALL);
|
||||
|
||||
if (result_type == SPLIT_WANT_ALL || !piece.empty())
|
||||
result.push_back(PieceToOutputType<Str, OutputStringType>(piece));
|
||||
result.emplace_back(piece);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -115,7 +78,7 @@ bool AppendStringKeyValue(StringPiece input,
|
|||
DVLOG(1) << "cannot find delimiter in: " << input;
|
||||
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.
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
template <typename Str, typename OutputStringType>
|
||||
void SplitStringUsingSubstrT(BasicStringPiece<Str> input,
|
||||
BasicStringPiece<Str> delimiter,
|
||||
WhitespaceHandling whitespace,
|
||||
SplitResult result_type,
|
||||
std::vector<OutputStringType>* result) {
|
||||
template <typename OutputStringType, typename Str>
|
||||
std::vector<OutputStringType> SplitStringUsingSubstrT(
|
||||
BasicStringPiece<Str> input,
|
||||
BasicStringPiece<Str> delimiter,
|
||||
WhitespaceHandling whitespace,
|
||||
SplitResult result_type) {
|
||||
using Piece = BasicStringPiece<Str>;
|
||||
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;
|
||||
begin_index = end_index + delimiter.size()) {
|
||||
end_index = input.find(delimiter, begin_index);
|
||||
|
@ -151,8 +115,10 @@ void SplitStringUsingSubstrT(BasicStringPiece<Str> input,
|
|||
term = TrimString(term, WhitespaceForType<Str>(), TRIM_ALL);
|
||||
|
||||
if (result_type == SPLIT_WANT_ALL || !term.empty())
|
||||
result->push_back(PieceToOutputType<Str, OutputStringType>(term));
|
||||
result.emplace_back(term);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -161,48 +127,29 @@ std::vector<std::string> SplitString(StringPiece input,
|
|||
StringPiece separators,
|
||||
WhitespaceHandling whitespace,
|
||||
SplitResult result_type) {
|
||||
if (separators.size() == 1) {
|
||||
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);
|
||||
return SplitStringT<std::string>(input, separators, whitespace, result_type);
|
||||
}
|
||||
|
||||
std::vector<string16> SplitString(StringPiece16 input,
|
||||
StringPiece16 separators,
|
||||
WhitespaceHandling whitespace,
|
||||
SplitResult result_type) {
|
||||
if (separators.size() == 1) {
|
||||
return SplitStringT<string16, string16, char16>(
|
||||
input, separators[0], whitespace, result_type);
|
||||
}
|
||||
return SplitStringT<string16, string16, StringPiece16>(
|
||||
input, separators, whitespace, result_type);
|
||||
return SplitStringT<string16>(input, separators, whitespace, result_type);
|
||||
}
|
||||
|
||||
std::vector<StringPiece> SplitStringPiece(StringPiece input,
|
||||
StringPiece separators,
|
||||
WhitespaceHandling whitespace,
|
||||
SplitResult result_type) {
|
||||
if (separators.size() == 1) {
|
||||
return SplitStringT<std::string, StringPiece, char>(
|
||||
input, separators[0], whitespace, result_type);
|
||||
}
|
||||
return SplitStringT<std::string, StringPiece, StringPiece>(
|
||||
input, separators, whitespace, result_type);
|
||||
return SplitStringT<StringPiece>(input, separators, whitespace, result_type);
|
||||
}
|
||||
|
||||
std::vector<StringPiece16> SplitStringPiece(StringPiece16 input,
|
||||
StringPiece16 separators,
|
||||
WhitespaceHandling whitespace,
|
||||
SplitResult result_type) {
|
||||
if (separators.size() == 1) {
|
||||
return SplitStringT<string16, StringPiece16, char16>(
|
||||
input, separators[0], whitespace, result_type);
|
||||
}
|
||||
return SplitStringT<string16, StringPiece16, StringPiece16>(
|
||||
input, separators, whitespace, result_type);
|
||||
return SplitStringT<StringPiece16>(input, separators, whitespace,
|
||||
result_type);
|
||||
}
|
||||
|
||||
bool SplitStringIntoKeyValuePairs(StringPiece input,
|
||||
|
@ -240,18 +187,16 @@ std::vector<string16> SplitStringUsingSubstr(StringPiece16 input,
|
|||
StringPiece16 delimiter,
|
||||
WhitespaceHandling whitespace,
|
||||
SplitResult result_type) {
|
||||
std::vector<string16> result;
|
||||
SplitStringUsingSubstrT(input, delimiter, whitespace, result_type, &result);
|
||||
return result;
|
||||
return SplitStringUsingSubstrT<string16>(input, delimiter, whitespace,
|
||||
result_type);
|
||||
}
|
||||
|
||||
std::vector<std::string> SplitStringUsingSubstr(StringPiece input,
|
||||
StringPiece delimiter,
|
||||
WhitespaceHandling whitespace,
|
||||
SplitResult result_type) {
|
||||
std::vector<std::string> result;
|
||||
SplitStringUsingSubstrT(input, delimiter, whitespace, result_type, &result);
|
||||
return result;
|
||||
return SplitStringUsingSubstrT<std::string>(input, delimiter, whitespace,
|
||||
result_type);
|
||||
}
|
||||
|
||||
std::vector<StringPiece16> SplitStringPieceUsingSubstr(
|
||||
|
@ -260,8 +205,8 @@ std::vector<StringPiece16> SplitStringPieceUsingSubstr(
|
|||
WhitespaceHandling whitespace,
|
||||
SplitResult result_type) {
|
||||
std::vector<StringPiece16> result;
|
||||
SplitStringUsingSubstrT(input, delimiter, whitespace, result_type, &result);
|
||||
return result;
|
||||
return SplitStringUsingSubstrT<StringPiece16>(input, delimiter, whitespace,
|
||||
result_type);
|
||||
}
|
||||
|
||||
std::vector<StringPiece> SplitStringPieceUsingSubstr(
|
||||
|
@ -269,9 +214,41 @@ std::vector<StringPiece> SplitStringPieceUsingSubstr(
|
|||
StringPiece delimiter,
|
||||
WhitespaceHandling whitespace,
|
||||
SplitResult result_type) {
|
||||
std::vector<StringPiece> result;
|
||||
SplitStringUsingSubstrT(input, delimiter, whitespace, result_type, &result);
|
||||
return result;
|
||||
return SplitStringUsingSubstrT<StringPiece>(input, delimiter, whitespace,
|
||||
result_type);
|
||||
}
|
||||
|
||||
#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
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "base/base_export.h"
|
||||
#include "base/strings/string16.h"
|
||||
#include "base/strings/string_piece.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
|
@ -39,26 +40,31 @@ enum SplitResult {
|
|||
// Split the given string on ANY of the given separators, returning copies of
|
||||
// the result.
|
||||
//
|
||||
// Note this is inverse of JoinString() defined in string_util.h.
|
||||
//
|
||||
// To split on either commas or semicolons, keeping all whitespace:
|
||||
//
|
||||
// std::vector<std::string> tokens = base::SplitString(
|
||||
// input, ",;", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
|
||||
BASE_EXPORT std::vector<std::string> SplitString(
|
||||
StringPiece input,
|
||||
StringPiece separators,
|
||||
WhitespaceHandling whitespace,
|
||||
SplitResult result_type);
|
||||
BASE_EXPORT std::vector<string16> SplitString(
|
||||
StringPiece16 input,
|
||||
StringPiece16 separators,
|
||||
WhitespaceHandling whitespace,
|
||||
SplitResult result_type);
|
||||
// input, ", WARN_UNUSED_RESULT;", base::KEEP_WHITESPACE,
|
||||
// base::SPLIT_WANT_ALL) WARN_UNUSED_RESULT;
|
||||
BASE_EXPORT std::vector<std::string> SplitString(StringPiece input,
|
||||
StringPiece separators,
|
||||
WhitespaceHandling whitespace,
|
||||
SplitResult result_type)
|
||||
WARN_UNUSED_RESULT;
|
||||
BASE_EXPORT std::vector<string16> SplitString(StringPiece16 input,
|
||||
StringPiece16 separators,
|
||||
WhitespaceHandling whitespace,
|
||||
SplitResult result_type)
|
||||
WARN_UNUSED_RESULT;
|
||||
|
||||
// Like SplitString above except it returns a vector of StringPieces which
|
||||
// reference the original buffer without copying. Although you have to be
|
||||
// careful to keep the original string unmodified, this provides an efficient
|
||||
// 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:
|
||||
//
|
||||
// for (const auto& cur :
|
||||
|
@ -70,12 +76,12 @@ BASE_EXPORT std::vector<StringPiece> SplitStringPiece(
|
|||
StringPiece input,
|
||||
StringPiece separators,
|
||||
WhitespaceHandling whitespace,
|
||||
SplitResult result_type);
|
||||
SplitResult result_type) WARN_UNUSED_RESULT;
|
||||
BASE_EXPORT std::vector<StringPiece16> SplitStringPiece(
|
||||
StringPiece16 input,
|
||||
StringPiece16 separators,
|
||||
WhitespaceHandling whitespace,
|
||||
SplitResult result_type);
|
||||
SplitResult result_type) WARN_UNUSED_RESULT;
|
||||
|
||||
using StringPairs = std::vector<std::pair<std::string, std::string>>;
|
||||
|
||||
|
@ -102,12 +108,12 @@ BASE_EXPORT std::vector<string16> SplitStringUsingSubstr(
|
|||
StringPiece16 input,
|
||||
StringPiece16 delimiter,
|
||||
WhitespaceHandling whitespace,
|
||||
SplitResult result_type);
|
||||
SplitResult result_type) WARN_UNUSED_RESULT;
|
||||
BASE_EXPORT std::vector<std::string> SplitStringUsingSubstr(
|
||||
StringPiece input,
|
||||
StringPiece delimiter,
|
||||
WhitespaceHandling whitespace,
|
||||
SplitResult result_type);
|
||||
SplitResult result_type) WARN_UNUSED_RESULT;
|
||||
|
||||
// Like SplitStringUsingSubstr above except it returns a vector of StringPieces
|
||||
// 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 delimiter,
|
||||
WhitespaceHandling whitespace,
|
||||
SplitResult result_type);
|
||||
SplitResult result_type) WARN_UNUSED_RESULT;
|
||||
BASE_EXPORT std::vector<StringPiece> SplitStringPieceUsingSubstr(
|
||||
StringPiece input,
|
||||
StringPiece delimiter,
|
||||
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
|
||||
|
||||
|
|
|
@ -237,17 +237,16 @@ bool ReplaceCharsT(const StringType& input,
|
|||
|
||||
bool ReplaceChars(const string16& input,
|
||||
StringPiece16 replace_chars,
|
||||
const string16& replace_with,
|
||||
StringPiece16 replace_with,
|
||||
string16* output) {
|
||||
return ReplaceCharsT(input, replace_chars, StringPiece16(replace_with),
|
||||
output);
|
||||
return ReplaceCharsT(input, replace_chars, replace_with, output);
|
||||
}
|
||||
|
||||
bool ReplaceChars(const std::string& input,
|
||||
StringPiece replace_chars,
|
||||
const std::string& replace_with,
|
||||
StringPiece replace_with,
|
||||
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,
|
||||
|
@ -262,8 +261,8 @@ bool RemoveChars(const std::string& input,
|
|||
return ReplaceCharsT(input, remove_chars, StringPiece(), output);
|
||||
}
|
||||
|
||||
template<typename Str>
|
||||
TrimPositions TrimStringT(const Str& input,
|
||||
template <typename Str>
|
||||
TrimPositions TrimStringT(BasicStringPiece<Str> input,
|
||||
BasicStringPiece<Str> trim_chars,
|
||||
TrimPositions positions,
|
||||
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
|
||||
// StringPiece version of trim_chars (normally the trim_chars will be a
|
||||
// constant so avoid making a copy).
|
||||
BasicStringPiece<Str> input_piece(input);
|
||||
const size_t last_char = input.length() - 1;
|
||||
const size_t first_good_char = (positions & TRIM_LEADING) ?
|
||||
input_piece.find_first_not_of(trim_chars) : 0;
|
||||
const size_t last_good_char = (positions & TRIM_TRAILING) ?
|
||||
input_piece.find_last_not_of(trim_chars) : last_char;
|
||||
const size_t first_good_char =
|
||||
(positions & TRIM_LEADING) ? input.find_first_not_of(trim_chars) : 0;
|
||||
const size_t last_good_char = (positions & TRIM_TRAILING)
|
||||
? input.find_last_not_of(trim_chars)
|
||||
: last_char;
|
||||
|
||||
// When the string was all trimmed, report that we stripped off characters
|
||||
// from whichever position the caller was interested in. For empty input, we
|
||||
// stripped no characters, but we still need to clear |output|.
|
||||
if (input.empty() ||
|
||||
(first_good_char == Str::npos) || (last_good_char == Str::npos)) {
|
||||
if (input.empty() || first_good_char == Str::npos ||
|
||||
last_good_char == Str::npos) {
|
||||
bool input_was_empty = input.empty(); // in case output == &input
|
||||
output->clear();
|
||||
return input_was_empty ? TRIM_NONE : positions;
|
||||
}
|
||||
|
||||
// Trim.
|
||||
*output =
|
||||
input.substr(first_good_char, last_good_char - first_good_char + 1);
|
||||
output->assign(input.data() + first_good_char,
|
||||
last_good_char - first_good_char + 1);
|
||||
|
||||
// Return where we trimmed from.
|
||||
return static_cast<TrimPositions>(
|
||||
((first_good_char == 0) ? TRIM_NONE : TRIM_LEADING) |
|
||||
((last_good_char == last_char) ? TRIM_NONE : TRIM_TRAILING));
|
||||
(first_good_char == 0 ? TRIM_NONE : TRIM_LEADING) |
|
||||
(last_good_char == last_char ? TRIM_NONE : TRIM_TRAILING));
|
||||
}
|
||||
|
||||
bool TrimString(const string16& input,
|
||||
bool TrimString(StringPiece16 input,
|
||||
StringPiece16 trim_chars,
|
||||
string16* output) {
|
||||
return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
|
||||
}
|
||||
|
||||
bool TrimString(const std::string& input,
|
||||
bool TrimString(StringPiece input,
|
||||
StringPiece trim_chars,
|
||||
std::string* output) {
|
||||
return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
|
||||
|
@ -370,7 +369,7 @@ void TruncateUTF8ToByteSize(const std::string& input,
|
|||
output->clear();
|
||||
}
|
||||
|
||||
TrimPositions TrimWhitespace(const string16& input,
|
||||
TrimPositions TrimWhitespace(StringPiece16 input,
|
||||
TrimPositions positions,
|
||||
string16* output) {
|
||||
return TrimStringT(input, StringPiece16(kWhitespaceUTF16), positions, output);
|
||||
|
@ -381,7 +380,7 @@ StringPiece16 TrimWhitespace(StringPiece16 input,
|
|||
return TrimStringPieceT(input, StringPiece16(kWhitespaceUTF16), positions);
|
||||
}
|
||||
|
||||
TrimPositions TrimWhitespaceASCII(const std::string& input,
|
||||
TrimPositions TrimWhitespaceASCII(StringPiece input,
|
||||
TrimPositions positions,
|
||||
std::string* output) {
|
||||
return TrimStringT(input, StringPiece(kWhitespaceASCII), positions, output);
|
||||
|
@ -506,20 +505,29 @@ bool IsStringASCII(WStringPiece str) {
|
|||
}
|
||||
#endif
|
||||
|
||||
bool IsStringUTF8(StringPiece str) {
|
||||
const char *src = str.data();
|
||||
template <bool (*Validator)(uint32_t)>
|
||||
inline static bool DoIsStringUTF8(StringPiece str) {
|
||||
const char* src = str.data();
|
||||
int32_t src_len = static_cast<int32_t>(str.length());
|
||||
int32_t char_index = 0;
|
||||
|
||||
while (char_index < src_len) {
|
||||
int32_t code_point;
|
||||
CBU8_NEXT(src, char_index, src_len, code_point);
|
||||
if (!IsValidCharacter(code_point))
|
||||
if (!Validator(code_point))
|
||||
return false;
|
||||
}
|
||||
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
|
||||
// constant for the lowercase_ascii parameter. Constructing a StringPiece from
|
||||
// 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>
|
||||
inline typename string_type::value_type* WriteIntoT(string_type* str,
|
||||
size_t length_with_null) {
|
||||
DCHECK_GT(length_with_null, 1u);
|
||||
DCHECK_GE(length_with_null, 1u);
|
||||
str->reserve(length_with_null);
|
||||
str->resize(length_with_null - 1);
|
||||
return &((*str)[0]);
|
||||
|
@ -1085,6 +1093,36 @@ string16 ReplaceStringPlaceholders(const string16& format_string,
|
|||
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:
|
||||
// http://www.gratisoft.us/todd/papers/strlcpy.html
|
||||
// ftp://ftp.openbsd.org/pub/OpenBSD/src/lib/libc/string/{wcs,str}lcpy.c
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#include <vector>
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/bit_cast.h"
|
||||
#include "base/compiler_specific.h"
|
||||
#include "base/stl_util.h"
|
||||
#include "base/strings/string16.h"
|
||||
|
@ -161,6 +160,7 @@ BASE_EXPORT const string16& EmptyString16();
|
|||
// by HTML5, and don't include control characters.
|
||||
BASE_EXPORT extern const wchar_t kWhitespaceWide[]; // 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 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|.
|
||||
BASE_EXPORT bool ReplaceChars(const string16& input,
|
||||
StringPiece16 replace_chars,
|
||||
const string16& replace_with,
|
||||
StringPiece16 replace_with,
|
||||
string16* output);
|
||||
BASE_EXPORT bool ReplaceChars(const std::string& input,
|
||||
StringPiece replace_chars,
|
||||
const std::string& replace_with,
|
||||
StringPiece replace_with,
|
||||
std::string* output);
|
||||
|
||||
enum TrimPositions {
|
||||
|
@ -204,10 +204,10 @@ enum TrimPositions {
|
|||
//
|
||||
// It is safe to use the same variable for both |input| and |output| (this is
|
||||
// the normal usage to trim in-place).
|
||||
BASE_EXPORT bool TrimString(const string16& input,
|
||||
BASE_EXPORT bool TrimString(StringPiece16 input,
|
||||
StringPiece16 trim_chars,
|
||||
string16* output);
|
||||
BASE_EXPORT bool TrimString(const std::string& input,
|
||||
BASE_EXPORT bool TrimString(StringPiece input,
|
||||
StringPiece trim_chars,
|
||||
std::string* output);
|
||||
|
||||
|
@ -229,37 +229,63 @@ BASE_EXPORT void TruncateUTF8ToByteSize(const std::string& input,
|
|||
#if defined(WCHAR_T_IS_UTF16)
|
||||
// Utility functions to access the underlying string buffer as a wide char
|
||||
// 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) {
|
||||
return bit_cast<wchar_t*>(str);
|
||||
return reinterpret_cast<wchar_t*>(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) {
|
||||
return bit_cast<const wchar_t*>(str);
|
||||
return reinterpret_cast<const wchar_t*>(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.
|
||||
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) {
|
||||
return bit_cast<char16*>(data(str));
|
||||
return reinterpret_cast<char16*>(data(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) {
|
||||
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)
|
||||
|
||||
|
@ -270,12 +296,12 @@ inline const char16* as_u16cstr(WStringPiece str) {
|
|||
//
|
||||
// The std::string versions return where whitespace was found.
|
||||
// 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,
|
||||
string16* output);
|
||||
BASE_EXPORT StringPiece16 TrimWhitespace(StringPiece16 input,
|
||||
TrimPositions positions);
|
||||
BASE_EXPORT TrimPositions TrimWhitespaceASCII(const std::string& input,
|
||||
BASE_EXPORT TrimPositions TrimWhitespaceASCII(StringPiece input,
|
||||
TrimPositions positions,
|
||||
std::string* output);
|
||||
BASE_EXPORT StringPiece TrimWhitespaceASCII(StringPiece input,
|
||||
|
@ -302,21 +328,23 @@ BASE_EXPORT bool ContainsOnlyChars(StringPiece input, StringPiece characters);
|
|||
BASE_EXPORT bool ContainsOnlyChars(StringPiece16 input,
|
||||
StringPiece16 characters);
|
||||
|
||||
// Returns true if the specified string matches the criteria. How can a wide
|
||||
// string be 8-bit or UTF8? It contains only characters that are < 256 (in the
|
||||
// first case) or characters that use only 8-bits and whose 8-bit
|
||||
// representation looks like a UTF-8 string (the second case).
|
||||
//
|
||||
// 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.
|
||||
// Returns true if |str| is structurally valid UTF-8 and also doesn't
|
||||
// contain any non-character code point (e.g. U+10FFFE). Prohibiting
|
||||
// non-characters increases the likelihood of detecting non-UTF-8 in
|
||||
// real-world text, for callers which do not need to accept
|
||||
// non-characters in strings.
|
||||
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(StringPiece16 str);
|
||||
#if defined(WCHAR_T_IS_UTF32)
|
||||
|
@ -383,6 +411,10 @@ template <typename Char>
|
|||
inline bool IsAsciiDigit(Char c) {
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
template <typename Char>
|
||||
inline bool IsAsciiPrintable(Char c) {
|
||||
return c >= ' ' && c <= '~';
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
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
|
||||
// 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
|
||||
// underlying array for potentially all
|
||||
// (|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 char16* WriteInto(string16* str, size_t length_with_null);
|
||||
|
||||
// Does the opposite of SplitString()/SplitStringPiece(). Joins a vector or list
|
||||
// of strings into a single string, inserting |separator| (which may be empty)
|
||||
// in between all elements.
|
||||
// Joins a vector or list of strings into a single string, inserting |separator|
|
||||
// (which may be empty) 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
|
||||
// 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,
|
||||
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
|
||||
|
||||
#if defined(OS_WIN)
|
||||
|
|
|
@ -6,61 +6,48 @@
|
|||
|
||||
namespace base {
|
||||
|
||||
#define WHITESPACE_UNICODE \
|
||||
0x0009, /* CHARACTER TABULATION */ \
|
||||
0x000A, /* LINE FEED (LF) */ \
|
||||
0x000B, /* LINE TABULATION */ \
|
||||
0x000C, /* FORM FEED (FF) */ \
|
||||
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
|
||||
#define WHITESPACE_ASCII_NO_CR_LF \
|
||||
0x09, /* CHARACTER TABULATION */ \
|
||||
0x0B, /* LINE TABULATION */ \
|
||||
0x0C, /* FORM FEED (FF) */ \
|
||||
0x20 /* SPACE */
|
||||
|
||||
const wchar_t kWhitespaceWide[] = {
|
||||
WHITESPACE_UNICODE
|
||||
};
|
||||
#define WHITESPACE_ASCII \
|
||||
WHITESPACE_ASCII_NO_CR_LF, /* Comment to make clang-format linebreak */ \
|
||||
0x0A, /* LINE FEED (LF) */ \
|
||||
0x0D /* CARRIAGE RETURN (CR) */
|
||||
|
||||
const char16 kWhitespaceUTF16[] = {
|
||||
WHITESPACE_UNICODE
|
||||
};
|
||||
#define WHITESPACE_UNICODE_NON_ASCII \
|
||||
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[] = {
|
||||
0x09, // CHARACTER TABULATION
|
||||
0x0A, // LINE FEED (LF)
|
||||
0x0B, // LINE TABULATION
|
||||
0x0C, // FORM FEED (FF)
|
||||
0x0D, // CARRIAGE RETURN (CR)
|
||||
0x20, // SPACE
|
||||
0
|
||||
};
|
||||
#define WHITESPACE_UNICODE_NO_CR_LF \
|
||||
WHITESPACE_ASCII_NO_CR_LF, WHITESPACE_UNICODE_NON_ASCII
|
||||
|
||||
const char16 kWhitespaceASCIIAs16[] = {
|
||||
0x09, // CHARACTER TABULATION
|
||||
0x0A, // LINE FEED (LF)
|
||||
0x0B, // LINE TABULATION
|
||||
0x0C, // FORM FEED (FF)
|
||||
0x0D, // CARRIAGE RETURN (CR)
|
||||
0x20, // SPACE
|
||||
0
|
||||
};
|
||||
#define WHITESPACE_UNICODE WHITESPACE_ASCII, WHITESPACE_UNICODE_NON_ASCII
|
||||
|
||||
const wchar_t kWhitespaceWide[] = {WHITESPACE_UNICODE, 0};
|
||||
const char16 kWhitespaceUTF16[] = {WHITESPACE_UNICODE, 0};
|
||||
const char16 kWhitespaceNoCrLfUTF16[] = {WHITESPACE_UNICODE_NO_CR_LF, 0};
|
||||
const char kWhitespaceASCII[] = {WHITESPACE_ASCII, 0};
|
||||
const char16 kWhitespaceASCIIAs16[] = {WHITESPACE_ASCII, 0};
|
||||
|
||||
const char kUtf8ByteOrderMark[] = "\xEF\xBB\xBF";
|
||||
|
||||
|
|
|
@ -39,18 +39,25 @@ inline int vsnprintfT(wchar_t* buffer,
|
|||
va_list 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
|
||||
|
||||
// Templatized backend for StringPrintF/StringAppendF. This does not finalize
|
||||
// the va_list, the caller is expected to do that.
|
||||
template <class StringType>
|
||||
static void StringAppendVT(StringType* dst,
|
||||
const typename StringType::value_type* format,
|
||||
template <class CharT>
|
||||
static void StringAppendVT(std::basic_string<CharT>* dst,
|
||||
const CharT* format,
|
||||
va_list ap) {
|
||||
// First try with a small fixed size buffer.
|
||||
// This buffer size should be kept in sync with StringUtilTest.GrowBoundary
|
||||
// and StringUtilTest.StringPrintfBounds.
|
||||
typename StringType::value_type stack_buf[1024];
|
||||
CharT stack_buf[1024];
|
||||
|
||||
va_list ap_copy;
|
||||
va_copy(ap_copy, ap);
|
||||
|
@ -93,7 +100,7 @@ static void StringAppendVT(StringType* dst,
|
|||
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
|
||||
// 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);
|
||||
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
|
||||
|
||||
std::string StringPrintV(const char* format, va_list ap) {
|
||||
|
@ -156,6 +172,17 @@ const std::wstring& SStringPrintf(std::wstring* dst,
|
|||
va_end(ap);
|
||||
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
|
||||
|
||||
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);
|
||||
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
|
||||
|
||||
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) {
|
||||
StringAppendVT(dst, format, ap);
|
||||
}
|
||||
|
||||
void StringAppendV(std::u16string* dst, const char16_t* format, va_list ap) {
|
||||
StringAppendVT(dst, format, ap);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace base
|
||||
|
|
|
@ -19,8 +19,14 @@ namespace base {
|
|||
BASE_EXPORT std::string StringPrintf(const char* format, ...)
|
||||
PRINTF_FORMAT(1, 2) WARN_UNUSED_RESULT;
|
||||
#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, ...)
|
||||
WPRINTF_FORMAT(1, 2) WARN_UNUSED_RESULT;
|
||||
BASE_EXPORT std::u16string StringPrintf(const char16_t* format, ...)
|
||||
WPRINTF_FORMAT(1, 2) WARN_UNUSED_RESULT;
|
||||
#endif
|
||||
|
||||
// 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,
|
||||
const wchar_t* format,
|
||||
...) WPRINTF_FORMAT(2, 3);
|
||||
BASE_EXPORT const std::u16string& SStringPrintf(std::u16string* dst,
|
||||
const char16_t* format,
|
||||
...) WPRINTF_FORMAT(2, 3);
|
||||
#endif
|
||||
|
||||
// Append result to a supplied string.
|
||||
|
@ -43,6 +52,8 @@ BASE_EXPORT void StringAppendF(std::string* dst, const char* format, ...)
|
|||
#if defined(OS_WIN)
|
||||
BASE_EXPORT void StringAppendF(std::wstring* dst, const wchar_t* format, ...)
|
||||
WPRINTF_FORMAT(2, 3);
|
||||
BASE_EXPORT void StringAppendF(std::u16string* dst, const char16_t* format, ...)
|
||||
WPRINTF_FORMAT(2, 3);
|
||||
#endif
|
||||
|
||||
// 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,
|
||||
const wchar_t* format,
|
||||
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
|
||||
|
||||
} // namespace base
|
||||
|
|
|
@ -17,16 +17,19 @@
|
|||
namespace base {
|
||||
|
||||
inline bool IsValidCodepoint(uint32_t code_point) {
|
||||
// Excludes the surrogate code points ([0xD800, 0xDFFF]) and
|
||||
// codepoints larger than 0x10FFFF (the highest codepoint allowed).
|
||||
// Non-characters and unassigned codepoints are allowed.
|
||||
// Excludes code points that are not Unicode scalar values, i.e.
|
||||
// surrogate code points ([0xD800, 0xDFFF]). Additionally, excludes
|
||||
// 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 ||
|
||||
(code_point >= 0xE000u && code_point <= 0x10FFFFu);
|
||||
}
|
||||
|
||||
inline bool IsValidCharacter(uint32_t code_point) {
|
||||
// Excludes non-characters (U+FDD0..U+FDEF, and all codepoints ending in
|
||||
// 0xFFFE or 0xFFFF) from the set of valid code points.
|
||||
// Excludes non-characters (U+FDD0..U+FDEF, and all 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 &&
|
||||
code_point < 0xFDD0u) || (code_point > 0xFDEFu &&
|
||||
code_point <= 0x10FFFFu && (code_point & 0xFFFEu) != 0xFFFEu);
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
#include <type_traits>
|
||||
|
||||
#include "base/bit_cast.h"
|
||||
#include "base/strings/string_piece.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/strings/utf_string_conversion_utils.h"
|
||||
|
|
|
@ -23,31 +23,31 @@ namespace base {
|
|||
// possible.
|
||||
BASE_EXPORT bool WideToUTF8(const wchar_t* src, size_t src_len,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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 string16 UTF8ToUTF16(StringPiece utf8);
|
||||
BASE_EXPORT string16 UTF8ToUTF16(StringPiece utf8) WARN_UNUSED_RESULT;
|
||||
BASE_EXPORT bool UTF16ToUTF8(const char16* src, size_t src_len,
|
||||
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
|
||||
// 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
|
||||
// beforehand.
|
||||
BASE_EXPORT std::string UTF16ToASCII(StringPiece16 utf16);
|
||||
BASE_EXPORT std::string UTF16ToASCII(StringPiece16 utf16) WARN_UNUSED_RESULT;
|
||||
|
||||
} // namespace base
|
||||
|
||||
|
|
|
@ -58,9 +58,6 @@
|
|||
// 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
|
||||
// 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_
|
||||
#define BASE_SYNCHRONIZATION_CONDITION_VARIABLE_H_
|
||||
|
|
|
@ -67,7 +67,7 @@ void ConditionVariable::Wait() {
|
|||
Optional<internal::ScopedBlockingCallWithBaseSyncPrimitives>
|
||||
scoped_blocking_call;
|
||||
if (waiting_is_blocking_)
|
||||
scoped_blocking_call.emplace(BlockingType::MAY_BLOCK);
|
||||
scoped_blocking_call.emplace(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
|
||||
#if DCHECK_IS_ON()
|
||||
user_lock_->CheckHeldAndUnmark();
|
||||
|
@ -83,7 +83,7 @@ void ConditionVariable::TimedWait(const TimeDelta& max_time) {
|
|||
Optional<internal::ScopedBlockingCallWithBaseSyncPrimitives>
|
||||
scoped_blocking_call;
|
||||
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();
|
||||
struct timespec relative_time;
|
||||
|
|
|
@ -26,8 +26,8 @@ class LOCKABLE BASE_EXPORT Lock {
|
|||
~Lock() {}
|
||||
|
||||
// TODO(lukasza): https://crbug.com/831825: Add EXCLUSIVE_LOCK_FUNCTION
|
||||
// annotation to Acquire method and similar annotations to Release, Try and
|
||||
// AssertAcquired methods (here and in the #else branch).
|
||||
// annotation to Acquire method and similar annotations to Release and Try
|
||||
// methods (here and in the #else branch).
|
||||
void Acquire() { lock_.Lock(); }
|
||||
void Release() { lock_.Unlock(); }
|
||||
|
||||
|
@ -38,7 +38,7 @@ class LOCKABLE BASE_EXPORT Lock {
|
|||
bool Try() { return lock_.Try(); }
|
||||
|
||||
// Null implementation if not debug.
|
||||
void AssertAcquired() const {}
|
||||
void AssertAcquired() const ASSERT_EXCLUSIVE_LOCK() {}
|
||||
#else
|
||||
Lock();
|
||||
~Lock();
|
||||
|
@ -63,7 +63,7 @@ class LOCKABLE BASE_EXPORT Lock {
|
|||
return rv;
|
||||
}
|
||||
|
||||
void AssertAcquired() const;
|
||||
void AssertAcquired() const ASSERT_EXCLUSIVE_LOCK();
|
||||
#endif // DCHECK_IS_ON()
|
||||
|
||||
// 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.
|
||||
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
|
||||
|
||||
#endif // BASE_SYNCHRONIZATION_LOCK_H_
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче