mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-19 00:05:36 +00:00
Bug 1639030 - Part 2: Roll-up of chromium sandbox update and patches to get a running browser. r=bobowen
This commit does: - Sync files under security/sandbox/chromium/ with Chromium 81.0.4044.138 - Update files under security/sandbox/chromium-shim/ - Apply patches under security/sandbox/chromium-shim/patches/with_update/ - Apply a workaround for Clang's bug to compile *_interception.cc - Add mozilla::AddWin32kLockdownPolicy to apply MITIGATION_WIN32K_DISABLE before SUBSYS_WIN32K_LOCKDOWN Differential Revision: https://phabricator.services.mozilla.com/D79560
This commit is contained in:
parent
8e7730db34
commit
0cbcab5ebb
@ -15,4 +15,6 @@
|
|||||||
|
|
||||||
#include "build/buildflag.h"
|
#include "build/buildflag.h"
|
||||||
|
|
||||||
|
#define BUILDFLAG_INTERNAL_USE_TCMALLOC() (0)
|
||||||
|
|
||||||
#endif // BASE_ALLOCATOR_BUILDFLAGS_H_
|
#endif // BASE_ALLOCATOR_BUILDFLAGS_H_
|
||||||
|
22
security/sandbox/chromium-shim/base/debug/crash_logging.cpp
Normal file
22
security/sandbox/chromium-shim/base/debug/crash_logging.cpp
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=2 et sw=2 tw=80: */
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
// This is a dummy version of base/debug/crash_logging.cc
|
||||||
|
|
||||||
|
#include "base/debug/crash_logging.h"
|
||||||
|
|
||||||
|
namespace base {
|
||||||
|
namespace debug {
|
||||||
|
|
||||||
|
CrashKeyString* AllocateCrashKeyString(const char name[],
|
||||||
|
CrashKeySize value_length) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetCrashKeyString(CrashKeyString* crash_key, base::StringPiece value) {}
|
||||||
|
|
||||||
|
} // namespace debug
|
||||||
|
} // namespace base
|
@ -4,18 +4,24 @@
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
// This is a dummy version of Chromium source file base/debug/stack_trace.h.
|
// This is a dummy version of Chromium source file base/debug/stack_trace.h
|
||||||
// To provide a dummy class StackTrace required in base/win/scoped_handle.cc.
|
// to provide a dummy class StackTrace.
|
||||||
|
|
||||||
#ifndef BASE_DEBUG_STACK_TRACE_H_
|
#ifndef BASE_DEBUG_STACK_TRACE_H_
|
||||||
#define BASE_DEBUG_STACK_TRACE_H_
|
#define BASE_DEBUG_STACK_TRACE_H_
|
||||||
|
|
||||||
|
#include <iosfwd>
|
||||||
|
|
||||||
namespace base {
|
namespace base {
|
||||||
namespace debug {
|
namespace debug {
|
||||||
|
|
||||||
class BASE_EXPORT StackTrace {
|
class BASE_EXPORT StackTrace {
|
||||||
public:
|
public:
|
||||||
StackTrace() {};
|
StackTrace() {};
|
||||||
|
|
||||||
|
#if !defined(__UCLIBC__) & !defined(_AIX)
|
||||||
|
void OutputToStream(std::ostream*) const {}
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace debug
|
} // namespace debug
|
||||||
|
@ -10,6 +10,8 @@
|
|||||||
#ifndef BASE_FEATURE_LIST_H_
|
#ifndef BASE_FEATURE_LIST_H_
|
||||||
#define BASE_FEATURE_LIST_H_
|
#define BASE_FEATURE_LIST_H_
|
||||||
|
|
||||||
|
#include "base/macros.h"
|
||||||
|
|
||||||
namespace base {
|
namespace base {
|
||||||
|
|
||||||
// Specifies whether a given feature is enabled or disabled by default.
|
// Specifies whether a given feature is enabled or disabled by default.
|
||||||
|
@ -7,8 +7,6 @@
|
|||||||
|
|
||||||
#include "base/files/file_path.h"
|
#include "base/files/file_path.h"
|
||||||
|
|
||||||
#include "mozilla/Assertions.h"
|
|
||||||
|
|
||||||
namespace base {
|
namespace base {
|
||||||
|
|
||||||
using StringType = FilePath::StringType;
|
using StringType = FilePath::StringType;
|
||||||
@ -18,6 +16,41 @@ namespace {
|
|||||||
|
|
||||||
const FilePath::CharType kStringTerminator = FILE_PATH_LITERAL('\0');
|
const FilePath::CharType kStringTerminator = FILE_PATH_LITERAL('\0');
|
||||||
|
|
||||||
|
// If this FilePath contains a drive letter specification, returns the
|
||||||
|
// position of the last character of the drive letter specification,
|
||||||
|
// otherwise returns npos. This can only be true on Windows, when a pathname
|
||||||
|
// begins with a letter followed by a colon. On other platforms, this always
|
||||||
|
// returns npos.
|
||||||
|
StringPieceType::size_type FindDriveLetter(StringPieceType path) {
|
||||||
|
#if defined(FILE_PATH_USES_DRIVE_LETTERS)
|
||||||
|
// This is dependent on an ASCII-based character set, but that's a
|
||||||
|
// reasonable assumption. iswalpha can be too inclusive here.
|
||||||
|
if (path.length() >= 2 && path[1] == L':' &&
|
||||||
|
((path[0] >= L'A' && path[0] <= L'Z') ||
|
||||||
|
(path[0] >= L'a' && path[0] <= L'z'))) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#endif // FILE_PATH_USES_DRIVE_LETTERS
|
||||||
|
return StringType::npos;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsPathAbsolute(StringPieceType path) {
|
||||||
|
#if defined(FILE_PATH_USES_DRIVE_LETTERS)
|
||||||
|
StringType::size_type letter = FindDriveLetter(path);
|
||||||
|
if (letter != StringType::npos) {
|
||||||
|
// Look for a separator right after the drive specification.
|
||||||
|
return path.length() > letter + 1 &&
|
||||||
|
FilePath::IsSeparator(path[letter + 1]);
|
||||||
|
}
|
||||||
|
// Look for a pair of leading separators.
|
||||||
|
return path.length() > 1 &&
|
||||||
|
FilePath::IsSeparator(path[0]) && FilePath::IsSeparator(path[1]);
|
||||||
|
#else // FILE_PATH_USES_DRIVE_LETTERS
|
||||||
|
// Look for a separator in the first position.
|
||||||
|
return path.length() > 0 && FilePath::IsSeparator(path[0]);
|
||||||
|
#endif // FILE_PATH_USES_DRIVE_LETTERS
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
FilePath::FilePath() = default;
|
FilePath::FilePath() = default;
|
||||||
@ -37,4 +70,148 @@ FilePath& FilePath::operator=(const FilePath& that) = default;
|
|||||||
|
|
||||||
FilePath& FilePath::operator=(FilePath&& that) = default;
|
FilePath& FilePath::operator=(FilePath&& that) = default;
|
||||||
|
|
||||||
|
// static
|
||||||
|
bool FilePath::IsSeparator(CharType character) {
|
||||||
|
for (size_t i = 0; i < kSeparatorsLength - 1; ++i) {
|
||||||
|
if (character == kSeparators[i]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// libgen's dirname and basename aren't guaranteed to be thread-safe and aren't
|
||||||
|
// guaranteed to not modify their input strings, and in fact are implemented
|
||||||
|
// differently in this regard on different platforms. Don't use them, but
|
||||||
|
// adhere to their behavior.
|
||||||
|
FilePath FilePath::DirName() const {
|
||||||
|
FilePath new_path(path_);
|
||||||
|
new_path.StripTrailingSeparatorsInternal();
|
||||||
|
|
||||||
|
// The drive letter, if any, always needs to remain in the output. If there
|
||||||
|
// is no drive letter, as will always be the case on platforms which do not
|
||||||
|
// support drive letters, letter will be npos, or -1, so the comparisons and
|
||||||
|
// resizes below using letter will still be valid.
|
||||||
|
StringType::size_type letter = FindDriveLetter(new_path.path_);
|
||||||
|
|
||||||
|
StringType::size_type last_separator =
|
||||||
|
new_path.path_.find_last_of(kSeparators, StringType::npos,
|
||||||
|
kSeparatorsLength - 1);
|
||||||
|
if (last_separator == StringType::npos) {
|
||||||
|
// path_ is in the current directory.
|
||||||
|
new_path.path_.resize(letter + 1);
|
||||||
|
} else if (last_separator == letter + 1) {
|
||||||
|
// path_ is in the root directory.
|
||||||
|
new_path.path_.resize(letter + 2);
|
||||||
|
} else if (last_separator == letter + 2 &&
|
||||||
|
IsSeparator(new_path.path_[letter + 1])) {
|
||||||
|
// path_ is in "//" (possibly with a drive letter); leave the double
|
||||||
|
// separator intact indicating alternate root.
|
||||||
|
new_path.path_.resize(letter + 3);
|
||||||
|
} else if (last_separator != 0) {
|
||||||
|
// path_ is somewhere else, trim the basename.
|
||||||
|
new_path.path_.resize(last_separator);
|
||||||
|
}
|
||||||
|
|
||||||
|
new_path.StripTrailingSeparatorsInternal();
|
||||||
|
if (!new_path.path_.length())
|
||||||
|
new_path.path_ = kCurrentDirectory;
|
||||||
|
|
||||||
|
return new_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
FilePath FilePath::BaseName() const {
|
||||||
|
FilePath new_path(path_);
|
||||||
|
new_path.StripTrailingSeparatorsInternal();
|
||||||
|
|
||||||
|
// The drive letter, if any, is always stripped.
|
||||||
|
StringType::size_type letter = FindDriveLetter(new_path.path_);
|
||||||
|
if (letter != StringType::npos) {
|
||||||
|
new_path.path_.erase(0, letter + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep everything after the final separator, but if the pathname is only
|
||||||
|
// one character and it's a separator, leave it alone.
|
||||||
|
StringType::size_type last_separator =
|
||||||
|
new_path.path_.find_last_of(kSeparators, StringType::npos,
|
||||||
|
kSeparatorsLength - 1);
|
||||||
|
if (last_separator != StringType::npos &&
|
||||||
|
last_separator < new_path.path_.length() - 1) {
|
||||||
|
new_path.path_.erase(0, last_separator + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
FilePath FilePath::Append(StringPieceType component) const {
|
||||||
|
StringPieceType appended = component;
|
||||||
|
StringType without_nuls;
|
||||||
|
|
||||||
|
StringType::size_type nul_pos = component.find(kStringTerminator);
|
||||||
|
if (nul_pos != StringPieceType::npos) {
|
||||||
|
without_nuls = StringType(component.substr(0, nul_pos));
|
||||||
|
appended = StringPieceType(without_nuls);
|
||||||
|
}
|
||||||
|
|
||||||
|
DCHECK(!IsPathAbsolute(appended));
|
||||||
|
|
||||||
|
if (path_.compare(kCurrentDirectory) == 0 && !appended.empty()) {
|
||||||
|
// Append normally doesn't do any normalization, but as a special case,
|
||||||
|
// when appending to kCurrentDirectory, just return a new path for the
|
||||||
|
// component argument. Appending component to kCurrentDirectory would
|
||||||
|
// serve no purpose other than needlessly lengthening the path, and
|
||||||
|
// it's likely in practice to wind up with FilePath objects containing
|
||||||
|
// only kCurrentDirectory when calling DirName on a single relative path
|
||||||
|
// component.
|
||||||
|
return FilePath(appended);
|
||||||
|
}
|
||||||
|
|
||||||
|
FilePath new_path(path_);
|
||||||
|
new_path.StripTrailingSeparatorsInternal();
|
||||||
|
|
||||||
|
// Don't append a separator if the path is empty (indicating the current
|
||||||
|
// directory) or if the path component is empty (indicating nothing to
|
||||||
|
// append).
|
||||||
|
if (!appended.empty() && !new_path.path_.empty()) {
|
||||||
|
// Don't append a separator if the path still ends with a trailing
|
||||||
|
// separator after stripping (indicating the root directory).
|
||||||
|
if (!IsSeparator(new_path.path_.back())) {
|
||||||
|
// Don't append a separator if the path is just a drive letter.
|
||||||
|
if (FindDriveLetter(new_path.path_) + 1 != new_path.path_.length()) {
|
||||||
|
new_path.path_.append(1, kSeparators[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new_path.path_.append(appended.data(), appended.size());
|
||||||
|
return new_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
FilePath FilePath::Append(const FilePath& component) const {
|
||||||
|
return Append(component.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
void FilePath::StripTrailingSeparatorsInternal() {
|
||||||
|
// If there is no drive letter, start will be 1, which will prevent stripping
|
||||||
|
// the leading separator if there is only one separator. If there is a drive
|
||||||
|
// letter, start will be set appropriately to prevent stripping the first
|
||||||
|
// separator following the drive letter, if a separator immediately follows
|
||||||
|
// the drive letter.
|
||||||
|
StringType::size_type start = FindDriveLetter(path_) + 2;
|
||||||
|
|
||||||
|
StringType::size_type last_stripped = StringType::npos;
|
||||||
|
for (StringType::size_type pos = path_.length();
|
||||||
|
pos > start && IsSeparator(path_[pos - 1]);
|
||||||
|
--pos) {
|
||||||
|
// If the string only has two separators and they're at the beginning,
|
||||||
|
// don't strip them, unless the string began with more than two separators.
|
||||||
|
if (pos != start + 1 || last_stripped == start + 2 ||
|
||||||
|
!IsSeparator(path_[start - 1])) {
|
||||||
|
path_.resize(pos - 1);
|
||||||
|
last_stripped = pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace base
|
} // namespace base
|
||||||
|
20
security/sandbox/chromium-shim/base/logging_buildflags.h
Normal file
20
security/sandbox/chromium-shim/base/logging_buildflags.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
// This is a copy of a file that is generated by the chromium build, with
|
||||||
|
// only the build flags we require.
|
||||||
|
|
||||||
|
// Generated by build/write_buildflag_header.py
|
||||||
|
// From "//base:logging_buildflags"
|
||||||
|
|
||||||
|
#ifndef BASE_LOGGING_BUILDFLAGS_H_
|
||||||
|
#define BASE_LOGGING_BUILDFLAGS_H_
|
||||||
|
|
||||||
|
#include "build/buildflag.h"
|
||||||
|
|
||||||
|
#define BUILDFLAG_INTERNAL_ENABLE_LOG_ERROR_NOT_REACHED() (0)
|
||||||
|
|
||||||
|
#endif // BASE_LOGGING_BUILDFLAGS_H_
|
@ -24,9 +24,9 @@ class BASE_EXPORT SharedMemoryTracker {
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IncrementMemoryUsage(const SharedMemory& shared_memory) {};
|
void IncrementMemoryUsage(const SharedMemoryMapping& mapping) {};
|
||||||
|
|
||||||
void DecrementMemoryUsage(const SharedMemory& shared_memory) {};
|
void DecrementMemoryUsage(const SharedMemoryMapping& mapping) {};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SharedMemoryTracker() {};
|
SharedMemoryTracker() {};
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
#ifndef BASE_METRICS_HISTOGRAM_MACROS_H_
|
#ifndef BASE_METRICS_HISTOGRAM_MACROS_H_
|
||||||
#define BASE_METRICS_HISTOGRAM_MACROS_H_
|
#define BASE_METRICS_HISTOGRAM_MACROS_H_
|
||||||
|
|
||||||
#define UMA_HISTOGRAM_ENUMERATION(name, sample, enum_max) do { } while (0)
|
#define UMA_HISTOGRAM_ENUMERATION(name, sample) do { } while (0)
|
||||||
#define SCOPED_UMA_HISTOGRAM_TIMER(name) do { } while (0)
|
|
||||||
|
|
||||||
#endif // BASE_METRICS_HISTOGRAM_MACROS_H_
|
#endif // BASE_METRICS_HISTOGRAM_MACROS_H_
|
||||||
|
12
security/sandbox/chromium-shim/base/observer_list.h
Normal file
12
security/sandbox/chromium-shim/base/observer_list.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=2 et sw=2 tw=80: */
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
// This is a cut down version of //base/observer_list.h
|
||||||
|
|
||||||
|
#ifndef BASE_OBSERVER_LIST_H_
|
||||||
|
#define BASE_OBSERVER_LIST_H_
|
||||||
|
|
||||||
|
#endif // BASE_OBSERVER_LIST_H_
|
25
security/sandbox/chromium-shim/base/process/launch.h
Normal file
25
security/sandbox/chromium-shim/base/process/launch.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
// This is a reduced version of Chromium's //base/process/launch.h
|
||||||
|
// to satisfy compiler.
|
||||||
|
|
||||||
|
#ifndef BASE_PROCESS_LAUNCH_H_
|
||||||
|
#define BASE_PROCESS_LAUNCH_H_
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "base/environment.h"
|
||||||
|
|
||||||
|
namespace base {
|
||||||
|
|
||||||
|
#if defined(OS_WIN)
|
||||||
|
typedef std::vector<HANDLE> HandlesToInheritVector;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace base
|
||||||
|
|
||||||
|
#endif // BASE_PROCESS_LAUNCH_H_
|
17
security/sandbox/chromium-shim/base/process/memory_win.cpp
Normal file
17
security/sandbox/chromium-shim/base/process/memory_win.cpp
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=2 et sw=2 tw=80: */
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#include "base/process/memory.h"
|
||||||
|
|
||||||
|
#include "mozilla/Assertions.h"
|
||||||
|
|
||||||
|
namespace base {
|
||||||
|
|
||||||
|
void TerminateBecauseOutOfMemory(size_t size) {
|
||||||
|
MOZ_CRASH("Hit base::TerminateBecauseOutOfMemory");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace base
|
@ -35,7 +35,8 @@ namespace internal {
|
|||||||
|
|
||||||
class BASE_EXPORT ScopedBlockingCallWithBaseSyncPrimitives {
|
class BASE_EXPORT ScopedBlockingCallWithBaseSyncPrimitives {
|
||||||
public:
|
public:
|
||||||
ScopedBlockingCallWithBaseSyncPrimitives(BlockingType blocking_type) {};
|
ScopedBlockingCallWithBaseSyncPrimitives(const Location& from_here,
|
||||||
|
BlockingType blocking_type) {}
|
||||||
~ScopedBlockingCallWithBaseSyncPrimitives() {};
|
~ScopedBlockingCallWithBaseSyncPrimitives() {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -330,6 +330,30 @@ DeriveAppContainerSidFromAppContainerName(
|
|||||||
#define PROC_THREAD_ATTRIBUTE_ALL_APPLICATION_PACKAGES_POLICY \
|
#define PROC_THREAD_ATTRIBUTE_ALL_APPLICATION_PACKAGES_POLICY \
|
||||||
ProcThreadAttributeValue (ProcThreadAttributeAllApplicationPackagesPolicy, FALSE, TRUE, FALSE)
|
ProcThreadAttributeValue (ProcThreadAttributeAllApplicationPackagesPolicy, FALSE, TRUE, FALSE)
|
||||||
|
|
||||||
#endif // (_WIN32_WINNT >= 0x0A00)
|
//
|
||||||
|
// Define functions declared only when _WIN32_WINNT >= 0x0A00
|
||||||
|
//
|
||||||
|
|
||||||
|
WINBASEAPI
|
||||||
|
BOOL
|
||||||
|
WINAPI
|
||||||
|
IsWow64Process2(
|
||||||
|
_In_ HANDLE hProcess,
|
||||||
|
_Out_ USHORT* pProcessMachine,
|
||||||
|
_Out_opt_ USHORT* pNativeMachine
|
||||||
|
);
|
||||||
|
|
||||||
|
#endif // (_WIN32_WINNT < 0x0A00)
|
||||||
|
|
||||||
|
#if defined(__MINGW32__)
|
||||||
|
|
||||||
|
// winnt.h
|
||||||
|
#define THREAD_DYNAMIC_CODE_ALLOW 1 // Opt-out of dynamic code generation.
|
||||||
|
|
||||||
|
// Mingw uses an old version THREAD_INFORMATION_CLASS defined in winbase.h
|
||||||
|
// where ThreadDynamicCodePolicy does not exist.
|
||||||
|
#define ThreadDynamicCodePolicy static_cast<THREAD_INFORMATION_CLASS>(2)
|
||||||
|
|
||||||
|
#endif // defined(__MINGW32__)
|
||||||
|
|
||||||
#endif // _SECURITY_SANDBOX_BASE_SHIM_SDKDECLS_H_
|
#endif // _SECURITY_SANDBOX_BASE_SHIM_SDKDECLS_H_
|
||||||
|
42
security/sandbox/chromium-shim/base/win/win_util.cpp
Normal file
42
security/sandbox/chromium-shim/base/win/win_util.cpp
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=2 et sw=2 tw=80: */
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
// This is a partial implementation of Chromium's source file
|
||||||
|
// base/win/win_util.cc
|
||||||
|
|
||||||
|
#include "base/win/win_util.h"
|
||||||
|
|
||||||
|
#include "base/logging.h"
|
||||||
|
#include "base/strings/string_util.h"
|
||||||
|
|
||||||
|
namespace base {
|
||||||
|
namespace win {
|
||||||
|
|
||||||
|
std::wstring GetWindowObjectName(HANDLE handle) {
|
||||||
|
// Get the size of the name.
|
||||||
|
std::wstring object_name;
|
||||||
|
|
||||||
|
DWORD size = 0;
|
||||||
|
::GetUserObjectInformation(handle, UOI_NAME, nullptr, 0, &size);
|
||||||
|
if (!size) {
|
||||||
|
DPCHECK(false);
|
||||||
|
return object_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_ASSERT(size % sizeof(wchar_t) == 0u);
|
||||||
|
|
||||||
|
// Query the name of the object.
|
||||||
|
if (!::GetUserObjectInformation(
|
||||||
|
handle, UOI_NAME, WriteInto(&object_name, size / sizeof(wchar_t)),
|
||||||
|
size, &size)) {
|
||||||
|
DPCHECK(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return object_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace win
|
||||||
|
} // namespace base
|
26
security/sandbox/chromium-shim/base/win/win_util.h
Normal file
26
security/sandbox/chromium-shim/base/win/win_util.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=2 et sw=2 tw=80: */
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
// This is a partial implementation of Chromium's source file
|
||||||
|
// base/win/win_util.h
|
||||||
|
|
||||||
|
#ifndef BASE_WIN_WIN_UTIL_H_
|
||||||
|
#define BASE_WIN_WIN_UTIL_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "base/base_export.h"
|
||||||
|
|
||||||
|
namespace base {
|
||||||
|
namespace win {
|
||||||
|
|
||||||
|
// Returns the name of a desktop or a window station.
|
||||||
|
BASE_EXPORT std::wstring GetWindowObjectName(HANDLE handle);
|
||||||
|
|
||||||
|
} // namespace win
|
||||||
|
} // namespace base
|
||||||
|
|
||||||
|
#endif // BASE_WIN_WIN_UTIL_H_
|
@ -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
|
// static
|
||||||
void AtExitManager::RegisterCallback(AtExitCallbackType func, void* param) {
|
void AtExitManager::RegisterCallback(AtExitCallbackType func, void* param) {
|
||||||
DCHECK(func);
|
DCHECK(func);
|
||||||
RegisterTask(base::Bind(func, param));
|
RegisterTask(base::BindOnce(func, param));
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
void AtExitManager::RegisterTask(base::Closure task) {
|
void AtExitManager::RegisterTask(base::OnceClosure task) {
|
||||||
if (!g_top_manager) {
|
if (!g_top_manager) {
|
||||||
NOTREACHED() << "Tried to RegisterCallback without an AtExitManager";
|
NOTREACHED() << "Tried to RegisterCallback without an AtExitManager";
|
||||||
return;
|
return;
|
||||||
@ -75,7 +75,7 @@ void AtExitManager::ProcessCallbacksNow() {
|
|||||||
// Callbacks may try to add new callbacks, so run them without holding
|
// Callbacks may try to add new callbacks, so run them without holding
|
||||||
// |lock_|. This is an error and caught by the DCHECK in RegisterTask(), but
|
// |lock_|. This is an error and caught by the DCHECK in RegisterTask(), but
|
||||||
// handle it gracefully in release builds so we don't deadlock.
|
// handle it gracefully in release builds so we don't deadlock.
|
||||||
base::stack<base::Closure> tasks;
|
base::stack<base::OnceClosure> tasks;
|
||||||
{
|
{
|
||||||
AutoLock lock(g_top_manager->lock_);
|
AutoLock lock(g_top_manager->lock_);
|
||||||
tasks.swap(g_top_manager->stack_);
|
tasks.swap(g_top_manager->stack_);
|
||||||
@ -89,8 +89,7 @@ void AtExitManager::ProcessCallbacksNow() {
|
|||||||
ScopedAllowCrossThreadRefCountAccess allow_cross_thread_ref_count_access;
|
ScopedAllowCrossThreadRefCountAccess allow_cross_thread_ref_count_access;
|
||||||
|
|
||||||
while (!tasks.empty()) {
|
while (!tasks.empty()) {
|
||||||
base::Closure task = tasks.top();
|
std::move(tasks.top()).Run();
|
||||||
task.Run();
|
|
||||||
tasks.pop();
|
tasks.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ class BASE_EXPORT AtExitManager {
|
|||||||
static void RegisterCallback(AtExitCallbackType func, void* param);
|
static void RegisterCallback(AtExitCallbackType func, void* param);
|
||||||
|
|
||||||
// Registers the specified task to be called at exit.
|
// Registers the specified task to be called at exit.
|
||||||
static void RegisterTask(base::Closure task);
|
static void RegisterTask(base::OnceClosure task);
|
||||||
|
|
||||||
// Calls the functions registered with RegisterCallback in LIFO order. It
|
// Calls the functions registered with RegisterCallback in LIFO order. It
|
||||||
// is possible to register new callbacks after calling this function.
|
// is possible to register new callbacks after calling this function.
|
||||||
@ -63,7 +63,7 @@ class BASE_EXPORT AtExitManager {
|
|||||||
private:
|
private:
|
||||||
base::Lock lock_;
|
base::Lock lock_;
|
||||||
|
|
||||||
base::stack<base::Closure> stack_ GUARDED_BY(lock_);
|
base::stack<base::OnceClosure> stack_ GUARDED_BY(lock_);
|
||||||
|
|
||||||
#if DCHECK_IS_ON()
|
#if DCHECK_IS_ON()
|
||||||
bool processing_callbacks_ GUARDED_BY(lock_) = false;
|
bool processing_callbacks_ GUARDED_BY(lock_) = false;
|
||||||
|
@ -39,15 +39,6 @@
|
|||||||
#include "base/base_export.h"
|
#include "base/base_export.h"
|
||||||
#include "build/build_config.h"
|
#include "build/build_config.h"
|
||||||
|
|
||||||
#if defined(OS_WIN) && (defined(ARCH_CPU_64_BITS) || defined(__MINGW32__))
|
|
||||||
// windows.h #defines this (only on x64). This causes problems because the
|
|
||||||
// public API also uses MemoryBarrier at the public name for this fence. So, on
|
|
||||||
// X64, undef it, and call its documented
|
|
||||||
// (http://msdn.microsoft.com/en-us/library/windows/desktop/ms684208.aspx)
|
|
||||||
// implementation directly.
|
|
||||||
#undef MemoryBarrier
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace base {
|
namespace base {
|
||||||
namespace subtle {
|
namespace subtle {
|
||||||
|
|
||||||
@ -100,8 +91,7 @@ Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
|
|||||||
// ensure that no later memory access can be reordered ahead of the operation.
|
// ensure that no later memory access can be reordered ahead of the operation.
|
||||||
// "Release" operations ensure that no previous memory access can be reordered
|
// "Release" operations ensure that no previous memory access can be reordered
|
||||||
// after the operation. "Barrier" operations have both "Acquire" and "Release"
|
// after the operation. "Barrier" operations have both "Acquire" and "Release"
|
||||||
// semantics. A MemoryBarrier() has "Barrier" semantics, but does no memory
|
// semantics.
|
||||||
// access.
|
|
||||||
Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
|
Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
|
||||||
Atomic32 old_value,
|
Atomic32 old_value,
|
||||||
Atomic32 new_value);
|
Atomic32 new_value);
|
||||||
@ -109,7 +99,6 @@ Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
|
|||||||
Atomic32 old_value,
|
Atomic32 old_value,
|
||||||
Atomic32 new_value);
|
Atomic32 new_value);
|
||||||
|
|
||||||
void MemoryBarrier();
|
|
||||||
void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value);
|
void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value);
|
||||||
void Acquire_Store(volatile Atomic32* ptr, Atomic32 value);
|
void Acquire_Store(volatile Atomic32* ptr, Atomic32 value);
|
||||||
void Release_Store(volatile Atomic32* ptr, Atomic32 value);
|
void Release_Store(volatile Atomic32* ptr, Atomic32 value);
|
||||||
|
@ -52,16 +52,6 @@ typedef volatile std::atomic<Atomic32>* AtomicLocation32;
|
|||||||
static_assert(sizeof(*(AtomicLocation32) nullptr) == sizeof(Atomic32),
|
static_assert(sizeof(*(AtomicLocation32) nullptr) == sizeof(Atomic32),
|
||||||
"incompatible 32-bit atomic layout");
|
"incompatible 32-bit atomic layout");
|
||||||
|
|
||||||
inline void MemoryBarrier() {
|
|
||||||
#if defined(__GLIBCXX__)
|
|
||||||
// Work around libstdc++ bug 51038 where atomic_thread_fence was declared but
|
|
||||||
// not defined, leading to the linker complaining about undefined references.
|
|
||||||
__atomic_thread_fence(std::memory_order_seq_cst);
|
|
||||||
#else
|
|
||||||
std::atomic_thread_fence(std::memory_order_seq_cst);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
|
inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
|
||||||
Atomic32 old_value,
|
Atomic32 old_value,
|
||||||
Atomic32 new_value) {
|
Atomic32 new_value) {
|
||||||
@ -119,7 +109,7 @@ inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
|
|||||||
|
|
||||||
inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
|
inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
|
||||||
((AtomicLocation32)ptr)->store(value, std::memory_order_relaxed);
|
((AtomicLocation32)ptr)->store(value, std::memory_order_relaxed);
|
||||||
MemoryBarrier();
|
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
|
inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
|
||||||
@ -135,7 +125,7 @@ inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
|
inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
|
||||||
MemoryBarrier();
|
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||||
return ((AtomicLocation32)ptr)->load(std::memory_order_relaxed);
|
return ((AtomicLocation32)ptr)->load(std::memory_order_relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,7 +192,7 @@ inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
|
|||||||
|
|
||||||
inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
|
inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
|
||||||
((AtomicLocation64)ptr)->store(value, std::memory_order_relaxed);
|
((AtomicLocation64)ptr)->store(value, std::memory_order_relaxed);
|
||||||
MemoryBarrier();
|
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
|
inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
|
||||||
@ -218,7 +208,7 @@ inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
|
inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
|
||||||
MemoryBarrier();
|
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||||
return ((AtomicLocation64)ptr)->load(std::memory_order_relaxed);
|
return ((AtomicLocation64)ptr)->load(std::memory_order_relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,18 +11,11 @@
|
|||||||
|
|
||||||
#include <intrin.h>
|
#include <intrin.h>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
#include "base/macros.h"
|
#include "base/macros.h"
|
||||||
#include "build/build_config.h"
|
#include "build/build_config.h"
|
||||||
|
|
||||||
#if defined(ARCH_CPU_64_BITS) || defined(__MINGW32__)
|
|
||||||
// windows.h #defines this (only on x64). This causes problems because the
|
|
||||||
// public API also uses MemoryBarrier at the public name for this fence. So, on
|
|
||||||
// X64, undef it, and call its documented
|
|
||||||
// (http://msdn.microsoft.com/en-us/library/windows/desktop/ms684208.aspx)
|
|
||||||
// implementation directly.
|
|
||||||
#undef MemoryBarrier
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace base {
|
namespace base {
|
||||||
namespace subtle {
|
namespace subtle {
|
||||||
|
|
||||||
@ -56,18 +49,6 @@ inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
|
|||||||
return Barrier_AtomicIncrement(ptr, increment);
|
return Barrier_AtomicIncrement(ptr, increment);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void MemoryBarrier() {
|
|
||||||
#if defined(ARCH_CPU_64_BITS)
|
|
||||||
// See #undef and note at the top of this file.
|
|
||||||
__faststorefence();
|
|
||||||
#else
|
|
||||||
// We use the implementation of MemoryBarrier from WinNT.h
|
|
||||||
LONG barrier;
|
|
||||||
|
|
||||||
_InterlockedOr(&barrier, 0);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
|
inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
|
||||||
Atomic32 old_value,
|
Atomic32 old_value,
|
||||||
Atomic32 new_value) {
|
Atomic32 new_value) {
|
||||||
@ -104,7 +85,7 @@ inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
|
inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
|
||||||
MemoryBarrier();
|
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||||
return *ptr;
|
return *ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,7 +154,7 @@ inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
|
inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
|
||||||
MemoryBarrier();
|
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||||
return *ptr;
|
return *ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
namespace switches {
|
namespace switches {
|
||||||
|
|
||||||
// Delays execution of base::TaskPriority::BEST_EFFORT tasks until shutdown.
|
// Delays execution of TaskPriority::BEST_EFFORT tasks until shutdown.
|
||||||
const char kDisableBestEffortTasks[] = "disable-best-effort-tasks";
|
const char kDisableBestEffortTasks[] = "disable-best-effort-tasks";
|
||||||
|
|
||||||
// Disables the crash reporting.
|
// Disables the crash reporting.
|
||||||
@ -16,6 +16,9 @@ const char kDisableBreakpad[] = "disable-breakpad";
|
|||||||
// Comma-separated list of feature names to disable. See also kEnableFeatures.
|
// Comma-separated list of feature names to disable. See also kEnableFeatures.
|
||||||
const char kDisableFeatures[] = "disable-features";
|
const char kDisableFeatures[] = "disable-features";
|
||||||
|
|
||||||
|
// Force disabling of low-end device mode when set.
|
||||||
|
const char kDisableLowEndDeviceMode[] = "disable-low-end-device-mode";
|
||||||
|
|
||||||
// Indicates that crash reporting should be enabled. On platforms where helper
|
// Indicates that crash reporting should be enabled. On platforms where helper
|
||||||
// processes cannot access to files needed to make this decision, this flag is
|
// processes cannot access to files needed to make this decision, this flag is
|
||||||
// generated internally.
|
// generated internally.
|
||||||
@ -24,15 +27,9 @@ const char kEnableCrashReporter[] = "enable-crash-reporter";
|
|||||||
// Comma-separated list of feature names to enable. See also kDisableFeatures.
|
// Comma-separated list of feature names to enable. See also kDisableFeatures.
|
||||||
const char kEnableFeatures[] = "enable-features";
|
const char kEnableFeatures[] = "enable-features";
|
||||||
|
|
||||||
// Generates full memory crash dump.
|
|
||||||
const char kFullMemoryCrashReport[] = "full-memory-crash-report";
|
|
||||||
|
|
||||||
// Force low-end device mode when set.
|
// Force low-end device mode when set.
|
||||||
const char kEnableLowEndDeviceMode[] = "enable-low-end-device-mode";
|
const char kEnableLowEndDeviceMode[] = "enable-low-end-device-mode";
|
||||||
|
|
||||||
// Force disabling of low-end device mode when set.
|
|
||||||
const char kDisableLowEndDeviceMode[] = "disable-low-end-device-mode";
|
|
||||||
|
|
||||||
// This option can be used to force field trials when testing changes locally.
|
// This option can be used to force field trials when testing changes locally.
|
||||||
// The argument is a list of name and value pairs, separated by slashes. If a
|
// The argument is a list of name and value pairs, separated by slashes. If a
|
||||||
// trial name is prefixed with an asterisk, that trial will start activated.
|
// trial name is prefixed with an asterisk, that trial will start activated.
|
||||||
@ -43,44 +40,19 @@ const char kDisableLowEndDeviceMode[] = "disable-low-end-device-mode";
|
|||||||
// FieldTrialList::CreateTrialsFromString() in field_trial.h for details.
|
// FieldTrialList::CreateTrialsFromString() in field_trial.h for details.
|
||||||
const char kForceFieldTrials[] = "force-fieldtrials";
|
const char kForceFieldTrials[] = "force-fieldtrials";
|
||||||
|
|
||||||
|
// Generates full memory crash dump.
|
||||||
|
const char kFullMemoryCrashReport[] = "full-memory-crash-report";
|
||||||
|
|
||||||
|
// Logs information about all tasks posted with TaskPriority::BEST_EFFORT. Use
|
||||||
|
// this to diagnose issues that are thought to be caused by
|
||||||
|
// TaskPriority::BEST_EFFORT execution fences. Note: Tasks posted to a
|
||||||
|
// non-BEST_EFFORT UpdateableSequencedTaskRunner whose priority is later lowered
|
||||||
|
// to BEST_EFFORT are not logged.
|
||||||
|
const char kLogBestEffortTasks[] = "log-best-effort-tasks";
|
||||||
|
|
||||||
// Suppresses all error dialogs when present.
|
// Suppresses all error dialogs when present.
|
||||||
const char kNoErrorDialogs[] = "noerrdialogs";
|
const char kNoErrorDialogs[] = "noerrdialogs";
|
||||||
|
|
||||||
// When running certain tests that spawn child processes, this switch indicates
|
|
||||||
// to the test framework that the current process is a child process.
|
|
||||||
const char kTestChildProcess[] = "test-child-process";
|
|
||||||
|
|
||||||
// When running certain tests that spawn child processes, this switch indicates
|
|
||||||
// to the test framework that the current process should not initialize ICU to
|
|
||||||
// avoid creating any scoped handles too early in startup.
|
|
||||||
const char kTestDoNotInitializeIcu[] = "test-do-not-initialize-icu";
|
|
||||||
|
|
||||||
// Gives the default maximal active V-logging level; 0 is the default.
|
|
||||||
// Normally positive values are used for V-logging levels.
|
|
||||||
const char kV[] = "v";
|
|
||||||
|
|
||||||
// Gives the per-module maximal V-logging levels to override the value
|
|
||||||
// given by --v. E.g. "my_module=2,foo*=3" would change the logging
|
|
||||||
// level for all code in source files "my_module.*" and "foo*.*"
|
|
||||||
// ("-inl" suffixes are also disregarded for this matching).
|
|
||||||
//
|
|
||||||
// Any pattern containing a forward or backward slash will be tested
|
|
||||||
// against the whole pathname and not just the module. E.g.,
|
|
||||||
// "*/foo/bar/*=2" would change the logging level for all code in
|
|
||||||
// source files under a "foo/bar" directory.
|
|
||||||
const char kVModule[] = "vmodule";
|
|
||||||
|
|
||||||
// Will wait for 60 seconds for a debugger to come to attach to the process.
|
|
||||||
const char kWaitForDebugger[] = "wait-for-debugger";
|
|
||||||
|
|
||||||
// Sends trace events from these categories to a file.
|
|
||||||
// --trace-to-file on its own sends to default categories.
|
|
||||||
const char kTraceToFile[] = "trace-to-file";
|
|
||||||
|
|
||||||
// Specifies the file name for --trace-to-file. If unspecified, it will
|
|
||||||
// go to a default file name.
|
|
||||||
const char kTraceToFileName[] = "trace-to-file-name";
|
|
||||||
|
|
||||||
// Starts the sampling based profiler for the browser process at startup. This
|
// Starts the sampling based profiler for the browser process at startup. This
|
||||||
// will only work if chrome has been built with the gn arg enable_profiling =
|
// will only work if chrome has been built with the gn arg enable_profiling =
|
||||||
// true. The output will go to the value of kProfilingFile.
|
// true. The output will go to the value of kProfilingFile.
|
||||||
@ -102,7 +74,45 @@ const char kProfilingFile[] = "profiling-file";
|
|||||||
// specified.
|
// specified.
|
||||||
const char kProfilingFlush[] = "profiling-flush";
|
const char kProfilingFlush[] = "profiling-flush";
|
||||||
|
|
||||||
|
// When running certain tests that spawn child processes, this switch indicates
|
||||||
|
// to the test framework that the current process is a child process.
|
||||||
|
const char kTestChildProcess[] = "test-child-process";
|
||||||
|
|
||||||
|
// When running certain tests that spawn child processes, this switch indicates
|
||||||
|
// to the test framework that the current process should not initialize ICU to
|
||||||
|
// avoid creating any scoped handles too early in startup.
|
||||||
|
const char kTestDoNotInitializeIcu[] = "test-do-not-initialize-icu";
|
||||||
|
|
||||||
|
// Sends trace events from these categories to a file.
|
||||||
|
// --trace-to-file on its own sends to default categories.
|
||||||
|
const char kTraceToFile[] = "trace-to-file";
|
||||||
|
|
||||||
|
// Specifies the file name for --trace-to-file. If unspecified, it will
|
||||||
|
// go to a default file name.
|
||||||
|
const char kTraceToFileName[] = "trace-to-file-name";
|
||||||
|
|
||||||
|
// Gives the default maximal active V-logging level; 0 is the default.
|
||||||
|
// Normally positive values are used for V-logging levels.
|
||||||
|
const char kV[] = "v";
|
||||||
|
|
||||||
|
// Gives the per-module maximal V-logging levels to override the value
|
||||||
|
// given by --v. E.g. "my_module=2,foo*=3" would change the logging
|
||||||
|
// level for all code in source files "my_module.*" and "foo*.*"
|
||||||
|
// ("-inl" suffixes are also disregarded for this matching).
|
||||||
|
//
|
||||||
|
// Any pattern containing a forward or backward slash will be tested
|
||||||
|
// against the whole pathname and not just the module. E.g.,
|
||||||
|
// "*/foo/bar/*=2" would change the logging level for all code in
|
||||||
|
// source files under a "foo/bar" directory.
|
||||||
|
const char kVModule[] = "vmodule";
|
||||||
|
|
||||||
|
// Will wait for 60 seconds for a debugger to come to attach to the process.
|
||||||
|
const char kWaitForDebugger[] = "wait-for-debugger";
|
||||||
|
|
||||||
#if defined(OS_WIN)
|
#if defined(OS_WIN)
|
||||||
|
// Disable high-resolution timer on Windows.
|
||||||
|
const char kDisableHighResTimer[] = "disable-highres-timer";
|
||||||
|
|
||||||
// Disables the USB keyboard detection for blocking the OSK on Win8+.
|
// Disables the USB keyboard detection for blocking the OSK on Win8+.
|
||||||
const char kDisableUsbKeyboardDetect[] = "disable-usb-keyboard-detect";
|
const char kDisableUsbKeyboardDetect[] = "disable-usb-keyboard-detect";
|
||||||
#endif
|
#endif
|
||||||
@ -126,17 +136,14 @@ const char kEnableCrashReporterForTesting[] =
|
|||||||
// Enables the reached code profiler that samples all threads in all processes
|
// Enables the reached code profiler that samples all threads in all processes
|
||||||
// to determine which functions are almost never executed.
|
// to determine which functions are almost never executed.
|
||||||
const char kEnableReachedCodeProfiler[] = "enable-reached-code-profiler";
|
const char kEnableReachedCodeProfiler[] = "enable-reached-code-profiler";
|
||||||
|
#endif
|
||||||
|
|
||||||
// Specifies optimization of memory layout of the native library using the
|
#if defined(OS_LINUX)
|
||||||
// orderfile symbols given in base/android/library_loader/anchor_functions.h,
|
// Controls whether or not retired instruction counts are surfaced for threads
|
||||||
// via madvise and changing the library prefetch behavior.
|
// in trace events on Linux.
|
||||||
//
|
//
|
||||||
// If this switch is not specified, an optimization may be done depending on a
|
// This flag requires the BPF sandbox to be disabled.
|
||||||
// synthetic trial. If specified, its values may be 'on' or 'off'. These
|
const char kEnableThreadInstructionCount[] = "enable-thread-instruction-count";
|
||||||
// override the synthetic trial.
|
|
||||||
//
|
|
||||||
// This flag is only used on architectures with SUPPORTS_CODE_ORDERING defined.
|
|
||||||
const char kOrderfileMemoryOptimization[] = "orderfile-memory-optimization";
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} // namespace switches
|
} // namespace switches
|
||||||
|
@ -20,6 +20,7 @@ extern const char kEnableFeatures[];
|
|||||||
extern const char kEnableLowEndDeviceMode[];
|
extern const char kEnableLowEndDeviceMode[];
|
||||||
extern const char kForceFieldTrials[];
|
extern const char kForceFieldTrials[];
|
||||||
extern const char kFullMemoryCrashReport[];
|
extern const char kFullMemoryCrashReport[];
|
||||||
|
extern const char kLogBestEffortTasks[];
|
||||||
extern const char kNoErrorDialogs[];
|
extern const char kNoErrorDialogs[];
|
||||||
extern const char kProfilingAtStart[];
|
extern const char kProfilingAtStart[];
|
||||||
extern const char kProfilingFile[];
|
extern const char kProfilingFile[];
|
||||||
@ -33,6 +34,7 @@ extern const char kVModule[];
|
|||||||
extern const char kWaitForDebugger[];
|
extern const char kWaitForDebugger[];
|
||||||
|
|
||||||
#if defined(OS_WIN)
|
#if defined(OS_WIN)
|
||||||
|
extern const char kDisableHighResTimer[];
|
||||||
extern const char kDisableUsbKeyboardDetect[];
|
extern const char kDisableUsbKeyboardDetect[];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -49,6 +51,10 @@ extern const char kEnableReachedCodeProfiler[];
|
|||||||
extern const char kOrderfileMemoryOptimization[];
|
extern const char kOrderfileMemoryOptimization[];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(OS_LINUX)
|
||||||
|
extern const char kEnableThreadInstructionCount[];
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace switches
|
} // namespace switches
|
||||||
|
|
||||||
#endif // BASE_BASE_SWITCHES_H_
|
#endif // BASE_BASE_SWITCHES_H_
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "base/bind_internal.h"
|
#include "base/bind_internal.h"
|
||||||
@ -41,7 +42,7 @@
|
|||||||
// class C : public base::RefCounted<C> { void F(); };
|
// class C : public base::RefCounted<C> { void F(); };
|
||||||
// auto instance = base::MakeRefCounted<C>();
|
// auto instance = base::MakeRefCounted<C>();
|
||||||
// auto cb = base::BindOnce(&C::F, instance);
|
// auto cb = base::BindOnce(&C::F, instance);
|
||||||
// cb.Run(); // Identical to instance->F()
|
// std::move(cb).Run(); // Identical to instance->F()
|
||||||
//
|
//
|
||||||
// base::Bind is currently a type alias for base::BindRepeating(). In the
|
// base::Bind is currently a type alias for base::BindRepeating(). In the
|
||||||
// future, we expect to flip this to default to base::BindOnce().
|
// future, we expect to flip this to default to base::BindOnce().
|
||||||
@ -179,26 +180,36 @@ template <bool is_once, bool is_method, typename... Args>
|
|||||||
using MakeUnwrappedTypeList =
|
using MakeUnwrappedTypeList =
|
||||||
typename MakeUnwrappedTypeListImpl<is_once, is_method, Args...>::Type;
|
typename MakeUnwrappedTypeListImpl<is_once, is_method, Args...>::Type;
|
||||||
|
|
||||||
} // namespace internal
|
// Used below in BindImpl to determine whether to use Invoker::Run or
|
||||||
|
// Invoker::RunOnce.
|
||||||
|
// Note: Simply using `kIsOnce ? &Invoker::RunOnce : &Invoker::Run` does not
|
||||||
|
// work, since the compiler needs to check whether both expressions are
|
||||||
|
// well-formed. Using `Invoker::Run` with a OnceCallback triggers a
|
||||||
|
// static_assert, which is why the ternary expression does not compile.
|
||||||
|
// TODO(crbug.com/752720): Remove this indirection once we have `if constexpr`.
|
||||||
|
template <typename Invoker>
|
||||||
|
constexpr auto GetInvokeFunc(std::true_type) {
|
||||||
|
return Invoker::RunOnce;
|
||||||
|
}
|
||||||
|
|
||||||
// Bind as OnceCallback.
|
template <typename Invoker>
|
||||||
template <typename Functor, typename... Args>
|
constexpr auto GetInvokeFunc(std::false_type) {
|
||||||
inline OnceCallback<MakeUnboundRunType<Functor, Args...>>
|
return Invoker::Run;
|
||||||
BindOnce(Functor&& functor, Args&&... args) {
|
}
|
||||||
static_assert(!internal::IsOnceCallback<std::decay_t<Functor>>() ||
|
|
||||||
(std::is_rvalue_reference<Functor&&>() &&
|
|
||||||
!std::is_const<std::remove_reference_t<Functor>>()),
|
|
||||||
"BindOnce requires non-const rvalue for OnceCallback binding."
|
|
||||||
" I.e.: base::BindOnce(std::move(callback)).");
|
|
||||||
|
|
||||||
|
template <template <typename> class CallbackT,
|
||||||
|
typename Functor,
|
||||||
|
typename... Args>
|
||||||
|
decltype(auto) BindImpl(Functor&& functor, Args&&... args) {
|
||||||
// This block checks if each |args| matches to the corresponding params of the
|
// This block checks if each |args| matches to the corresponding params of the
|
||||||
// target function. This check does not affect the behavior of Bind, but its
|
// target function. This check does not affect the behavior of Bind, but its
|
||||||
// error message should be more readable.
|
// error message should be more readable.
|
||||||
|
static constexpr bool kIsOnce = IsOnceCallback<CallbackT<void()>>::value;
|
||||||
using Helper = internal::BindTypeHelper<Functor, Args...>;
|
using Helper = internal::BindTypeHelper<Functor, Args...>;
|
||||||
using FunctorTraits = typename Helper::FunctorTraits;
|
using FunctorTraits = typename Helper::FunctorTraits;
|
||||||
using BoundArgsList = typename Helper::BoundArgsList;
|
using BoundArgsList = typename Helper::BoundArgsList;
|
||||||
using UnwrappedArgsList =
|
using UnwrappedArgsList =
|
||||||
internal::MakeUnwrappedTypeList<true, FunctorTraits::is_method,
|
internal::MakeUnwrappedTypeList<kIsOnce, FunctorTraits::is_method,
|
||||||
Args&&...>;
|
Args&&...>;
|
||||||
using BoundParamsList = typename Helper::BoundParamsList;
|
using BoundParamsList = typename Helper::BoundParamsList;
|
||||||
static_assert(internal::AssertBindArgsValidity<
|
static_assert(internal::AssertBindArgsValidity<
|
||||||
@ -209,13 +220,14 @@ BindOnce(Functor&& functor, Args&&... args) {
|
|||||||
using BindState = internal::MakeBindStateType<Functor, Args...>;
|
using BindState = internal::MakeBindStateType<Functor, Args...>;
|
||||||
using UnboundRunType = MakeUnboundRunType<Functor, Args...>;
|
using UnboundRunType = MakeUnboundRunType<Functor, Args...>;
|
||||||
using Invoker = internal::Invoker<BindState, UnboundRunType>;
|
using Invoker = internal::Invoker<BindState, UnboundRunType>;
|
||||||
using CallbackType = OnceCallback<UnboundRunType>;
|
using CallbackType = CallbackT<UnboundRunType>;
|
||||||
|
|
||||||
// Store the invoke func into PolymorphicInvoke before casting it to
|
// Store the invoke func into PolymorphicInvoke before casting it to
|
||||||
// InvokeFuncStorage, so that we can ensure its type matches to
|
// InvokeFuncStorage, so that we can ensure its type matches to
|
||||||
// PolymorphicInvoke, to which CallbackType will cast back.
|
// PolymorphicInvoke, to which CallbackType will cast back.
|
||||||
using PolymorphicInvoke = typename CallbackType::PolymorphicInvoke;
|
using PolymorphicInvoke = typename CallbackType::PolymorphicInvoke;
|
||||||
PolymorphicInvoke invoke_func = &Invoker::RunOnce;
|
PolymorphicInvoke invoke_func =
|
||||||
|
GetInvokeFunc<Invoker>(std::integral_constant<bool, kIsOnce>());
|
||||||
|
|
||||||
using InvokeFuncStorage = internal::BindStateBase::InvokeFuncStorage;
|
using InvokeFuncStorage = internal::BindStateBase::InvokeFuncStorage;
|
||||||
return CallbackType(BindState::Create(
|
return CallbackType(BindState::Create(
|
||||||
@ -223,6 +235,23 @@ BindOnce(Functor&& functor, Args&&... args) {
|
|||||||
std::forward<Functor>(functor), std::forward<Args>(args)...));
|
std::forward<Functor>(functor), std::forward<Args>(args)...));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
// Bind as OnceCallback.
|
||||||
|
template <typename Functor, typename... Args>
|
||||||
|
inline OnceCallback<MakeUnboundRunType<Functor, Args...>> BindOnce(
|
||||||
|
Functor&& functor,
|
||||||
|
Args&&... args) {
|
||||||
|
static_assert(!internal::IsOnceCallback<std::decay_t<Functor>>() ||
|
||||||
|
(std::is_rvalue_reference<Functor&&>() &&
|
||||||
|
!std::is_const<std::remove_reference_t<Functor>>()),
|
||||||
|
"BindOnce requires non-const rvalue for OnceCallback binding."
|
||||||
|
" I.e.: base::BindOnce(std::move(callback)).");
|
||||||
|
|
||||||
|
return internal::BindImpl<OnceCallback>(std::forward<Functor>(functor),
|
||||||
|
std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
// Bind as RepeatingCallback.
|
// Bind as RepeatingCallback.
|
||||||
template <typename Functor, typename... Args>
|
template <typename Functor, typename... Args>
|
||||||
inline RepeatingCallback<MakeUnboundRunType<Functor, Args...>>
|
inline RepeatingCallback<MakeUnboundRunType<Functor, Args...>>
|
||||||
@ -231,36 +260,8 @@ BindRepeating(Functor&& functor, Args&&... args) {
|
|||||||
!internal::IsOnceCallback<std::decay_t<Functor>>(),
|
!internal::IsOnceCallback<std::decay_t<Functor>>(),
|
||||||
"BindRepeating cannot bind OnceCallback. Use BindOnce with std::move().");
|
"BindRepeating cannot bind OnceCallback. Use BindOnce with std::move().");
|
||||||
|
|
||||||
// This block checks if each |args| matches to the corresponding params of the
|
return internal::BindImpl<RepeatingCallback>(std::forward<Functor>(functor),
|
||||||
// target function. This check does not affect the behavior of Bind, but its
|
std::forward<Args>(args)...);
|
||||||
// error message should be more readable.
|
|
||||||
using Helper = internal::BindTypeHelper<Functor, Args...>;
|
|
||||||
using FunctorTraits = typename Helper::FunctorTraits;
|
|
||||||
using BoundArgsList = typename Helper::BoundArgsList;
|
|
||||||
using UnwrappedArgsList =
|
|
||||||
internal::MakeUnwrappedTypeList<false, FunctorTraits::is_method,
|
|
||||||
Args&&...>;
|
|
||||||
using BoundParamsList = typename Helper::BoundParamsList;
|
|
||||||
static_assert(internal::AssertBindArgsValidity<
|
|
||||||
std::make_index_sequence<Helper::num_bounds>, BoundArgsList,
|
|
||||||
UnwrappedArgsList, BoundParamsList>::ok,
|
|
||||||
"The bound args need to be convertible to the target params.");
|
|
||||||
|
|
||||||
using BindState = internal::MakeBindStateType<Functor, Args...>;
|
|
||||||
using UnboundRunType = MakeUnboundRunType<Functor, Args...>;
|
|
||||||
using Invoker = internal::Invoker<BindState, UnboundRunType>;
|
|
||||||
using CallbackType = RepeatingCallback<UnboundRunType>;
|
|
||||||
|
|
||||||
// Store the invoke func into PolymorphicInvoke before casting it to
|
|
||||||
// InvokeFuncStorage, so that we can ensure its type matches to
|
|
||||||
// PolymorphicInvoke, to which CallbackType will cast back.
|
|
||||||
using PolymorphicInvoke = typename CallbackType::PolymorphicInvoke;
|
|
||||||
PolymorphicInvoke invoke_func = &Invoker::Run;
|
|
||||||
|
|
||||||
using InvokeFuncStorage = internal::BindStateBase::InvokeFuncStorage;
|
|
||||||
return CallbackType(BindState::Create(
|
|
||||||
reinterpret_cast<InvokeFuncStorage>(invoke_func),
|
|
||||||
std::forward<Functor>(functor), std::forward<Args>(args)...));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unannotated Bind.
|
// Unannotated Bind.
|
||||||
@ -275,22 +276,27 @@ Bind(Functor&& functor, Args&&... args) {
|
|||||||
|
|
||||||
// Special cases for binding to a base::Callback without extra bound arguments.
|
// Special cases for binding to a base::Callback without extra bound arguments.
|
||||||
template <typename Signature>
|
template <typename Signature>
|
||||||
OnceCallback<Signature> BindOnce(OnceCallback<Signature> closure) {
|
OnceCallback<Signature> BindOnce(OnceCallback<Signature> callback) {
|
||||||
return closure;
|
return callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Signature>
|
||||||
|
OnceCallback<Signature> BindOnce(RepeatingCallback<Signature> callback) {
|
||||||
|
return callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Signature>
|
template <typename Signature>
|
||||||
RepeatingCallback<Signature> BindRepeating(
|
RepeatingCallback<Signature> BindRepeating(
|
||||||
RepeatingCallback<Signature> closure) {
|
RepeatingCallback<Signature> callback) {
|
||||||
return closure;
|
return callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Signature>
|
template <typename Signature>
|
||||||
Callback<Signature> Bind(Callback<Signature> closure) {
|
Callback<Signature> Bind(Callback<Signature> callback) {
|
||||||
return closure;
|
return callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unretained() allows Bind() to bind a non-refcounted class, and to disable
|
// Unretained() allows binding a non-refcounted class, and to disable
|
||||||
// refcounting on arguments that are refcounted objects.
|
// refcounting on arguments that are refcounted objects.
|
||||||
//
|
//
|
||||||
// EXAMPLE OF Unretained():
|
// EXAMPLE OF Unretained():
|
||||||
@ -302,9 +308,9 @@ Callback<Signature> Bind(Callback<Signature> closure) {
|
|||||||
//
|
//
|
||||||
// // In some function somewhere.
|
// // In some function somewhere.
|
||||||
// Foo foo;
|
// Foo foo;
|
||||||
// Closure foo_callback =
|
// OnceClosure foo_callback =
|
||||||
// Bind(&Foo::func, Unretained(&foo));
|
// BindOnce(&Foo::func, Unretained(&foo));
|
||||||
// foo_callback.Run(); // Prints "Foo:f".
|
// std::move(foo_callback).Run(); // Prints "Foo:f".
|
||||||
//
|
//
|
||||||
// Without the Unretained() wrapper on |&foo|, the above call would fail
|
// Without the Unretained() wrapper on |&foo|, the above call would fail
|
||||||
// to compile because Foo does not support the AddRef() and Release() methods.
|
// to compile because Foo does not support the AddRef() and Release() methods.
|
||||||
@ -321,13 +327,13 @@ static inline internal::UnretainedWrapper<T> Unretained(T* o) {
|
|||||||
// void foo(RefCountedBytes* bytes) {}
|
// void foo(RefCountedBytes* bytes) {}
|
||||||
//
|
//
|
||||||
// scoped_refptr<RefCountedBytes> bytes = ...;
|
// scoped_refptr<RefCountedBytes> bytes = ...;
|
||||||
// Closure callback = Bind(&foo, base::RetainedRef(bytes));
|
// OnceClosure callback = BindOnce(&foo, base::RetainedRef(bytes));
|
||||||
// callback.Run();
|
// std::move(callback).Run();
|
||||||
//
|
//
|
||||||
// Without RetainedRef, the scoped_refptr would try to implicitly convert to
|
// Without RetainedRef, the scoped_refptr would try to implicitly convert to
|
||||||
// a raw pointer and fail compilation:
|
// a raw pointer and fail compilation:
|
||||||
//
|
//
|
||||||
// Closure callback = Bind(&foo, bytes); // ERROR!
|
// OnceClosure callback = BindOnce(&foo, bytes); // ERROR!
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static inline internal::RetainedRefWrapper<T> RetainedRef(T* o) {
|
static inline internal::RetainedRefWrapper<T> RetainedRef(T* o) {
|
||||||
return internal::RetainedRefWrapper<T>(o);
|
return internal::RetainedRefWrapper<T>(o);
|
||||||
@ -337,40 +343,41 @@ static inline internal::RetainedRefWrapper<T> RetainedRef(scoped_refptr<T> o) {
|
|||||||
return internal::RetainedRefWrapper<T>(std::move(o));
|
return internal::RetainedRefWrapper<T>(std::move(o));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Owned() transfers ownership of an object to the Callback resulting from
|
// Owned() transfers ownership of an object to the callback resulting from
|
||||||
// bind; the object will be deleted when the Callback is deleted.
|
// bind; the object will be deleted when the callback is deleted.
|
||||||
//
|
//
|
||||||
// EXAMPLE OF Owned():
|
// EXAMPLE OF Owned():
|
||||||
//
|
//
|
||||||
// void foo(int* arg) { cout << *arg << endl }
|
// void foo(int* arg) { cout << *arg << endl }
|
||||||
//
|
//
|
||||||
// int* pn = new int(1);
|
// int* pn = new int(1);
|
||||||
// Closure foo_callback = Bind(&foo, Owned(pn));
|
// RepeatingClosure foo_callback = BindRepeating(&foo, Owned(pn));
|
||||||
//
|
//
|
||||||
// foo_callback.Run(); // Prints "1"
|
// foo_callback.Run(); // Prints "1"
|
||||||
// foo_callback.Run(); // Prints "1"
|
// foo_callback.Run(); // Prints "1"
|
||||||
// *n = 2;
|
// *pn = 2;
|
||||||
// foo_callback.Run(); // Prints "2"
|
// foo_callback.Run(); // Prints "2"
|
||||||
//
|
//
|
||||||
// foo_callback.Reset(); // |pn| is deleted. Also will happen when
|
// foo_callback.Reset(); // |pn| is deleted. Also will happen when
|
||||||
// // |foo_callback| goes out of scope.
|
// // |foo_callback| goes out of scope.
|
||||||
//
|
//
|
||||||
// Without Owned(), someone would have to know to delete |pn| when the last
|
// Without Owned(), someone would have to know to delete |pn| when the last
|
||||||
// reference to the Callback is deleted.
|
// reference to the callback is deleted.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static inline internal::OwnedWrapper<T> Owned(T* o) {
|
static inline internal::OwnedWrapper<T> Owned(T* o) {
|
||||||
return internal::OwnedWrapper<T>(o);
|
return internal::OwnedWrapper<T>(o);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T, typename Deleter>
|
||||||
static inline internal::OwnedWrapper<T> Owned(std::unique_ptr<T>&& ptr) {
|
static inline internal::OwnedWrapper<T, Deleter> Owned(
|
||||||
return internal::OwnedWrapper<T>(std::move(ptr));
|
std::unique_ptr<T, Deleter>&& ptr) {
|
||||||
|
return internal::OwnedWrapper<T, Deleter>(std::move(ptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Passed() is for transferring movable-but-not-copyable types (eg. unique_ptr)
|
// Passed() is for transferring movable-but-not-copyable types (eg. unique_ptr)
|
||||||
// through a Callback. Logically, this signifies a destructive transfer of
|
// through a RepeatingCallback. Logically, this signifies a destructive transfer
|
||||||
// the state of the argument into the target function. Invoking
|
// of the state of the argument into the target function. Invoking
|
||||||
// Callback::Run() twice on a Callback that was created with a Passed()
|
// RepeatingCallback::Run() twice on a callback that was created with a Passed()
|
||||||
// argument will CHECK() because the first invocation would have already
|
// argument will CHECK() because the first invocation would have already
|
||||||
// transferred ownership to the target function.
|
// transferred ownership to the target function.
|
||||||
//
|
//
|
||||||
@ -387,22 +394,22 @@ static inline internal::OwnedWrapper<T> Owned(std::unique_ptr<T>&& ptr) {
|
|||||||
//
|
//
|
||||||
// // |cb| is given ownership of Foo(). |f| is now NULL.
|
// // |cb| is given ownership of Foo(). |f| is now NULL.
|
||||||
// // You can use std::move(f) in place of &f, but it's more verbose.
|
// // You can use std::move(f) in place of &f, but it's more verbose.
|
||||||
// Closure cb = Bind(&TakesOwnership, Passed(&f));
|
// RepeatingClosure cb = BindRepeating(&TakesOwnership, Passed(&f));
|
||||||
//
|
//
|
||||||
// // Run was never called so |cb| still owns Foo() and deletes
|
// // Run was never called so |cb| still owns Foo() and deletes
|
||||||
// // it on Reset().
|
// // it on Reset().
|
||||||
// cb.Reset();
|
// cb.Reset();
|
||||||
//
|
//
|
||||||
// // |cb| is given a new Foo created by CreateFoo().
|
// // |cb| is given a new Foo created by CreateFoo().
|
||||||
// cb = Bind(&TakesOwnership, Passed(CreateFoo()));
|
// cb = BindRepeating(&TakesOwnership, Passed(CreateFoo()));
|
||||||
//
|
//
|
||||||
// // |arg| in TakesOwnership() is given ownership of Foo(). |cb|
|
// // |arg| in TakesOwnership() is given ownership of Foo(). |cb|
|
||||||
// // no longer owns Foo() and, if reset, would not delete Foo().
|
// // no longer owns Foo() and, if reset, would not delete Foo().
|
||||||
// cb.Run(); // Foo() is now transferred to |arg| and deleted.
|
// cb.Run(); // Foo() is now transferred to |arg| and deleted.
|
||||||
// cb.Run(); // This CHECK()s since Foo() already been used once.
|
// cb.Run(); // This CHECK()s since Foo() already been used once.
|
||||||
//
|
//
|
||||||
// We offer 2 syntaxes for calling Passed(). The first takes an rvalue and
|
// We offer 2 syntaxes for calling Passed(). The first takes an rvalue and is
|
||||||
// is best suited for use with the return value of a function or other temporary
|
// best suited for use with the return value of a function or other temporary
|
||||||
// rvalues. The second takes a pointer to the scoper and is just syntactic sugar
|
// rvalues. The second takes a pointer to the scoper and is just syntactic sugar
|
||||||
// to avoid having to write Passed(std::move(scoper)).
|
// to avoid having to write Passed(std::move(scoper)).
|
||||||
//
|
//
|
||||||
@ -418,21 +425,21 @@ static inline internal::PassedWrapper<T> Passed(T* scoper) {
|
|||||||
return internal::PassedWrapper<T>(std::move(*scoper));
|
return internal::PassedWrapper<T>(std::move(*scoper));
|
||||||
}
|
}
|
||||||
|
|
||||||
// IgnoreResult() is used to adapt a function or Callback with a return type to
|
// IgnoreResult() is used to adapt a function or callback with a return type to
|
||||||
// one with a void return. This is most useful if you have a function with,
|
// one with a void return. This is most useful if you have a function with,
|
||||||
// say, a pesky ignorable bool return that you want to use with PostTask or
|
// say, a pesky ignorable bool return that you want to use with PostTask or
|
||||||
// something else that expect a Callback with a void return.
|
// something else that expect a callback with a void return.
|
||||||
//
|
//
|
||||||
// EXAMPLE OF IgnoreResult():
|
// EXAMPLE OF IgnoreResult():
|
||||||
//
|
//
|
||||||
// int DoSomething(int arg) { cout << arg << endl; }
|
// int DoSomething(int arg) { cout << arg << endl; }
|
||||||
//
|
//
|
||||||
// // Assign to a Callback with a void return type.
|
// // Assign to a callback with a void return type.
|
||||||
// Callback<void(int)> cb = Bind(IgnoreResult(&DoSomething));
|
// OnceCallback<void(int)> cb = BindOnce(IgnoreResult(&DoSomething));
|
||||||
// cb->Run(1); // Prints "1".
|
// std::move(cb).Run(1); // Prints "1".
|
||||||
//
|
//
|
||||||
// // Prints "1" on |ml|.
|
// // Prints "2" on |ml|.
|
||||||
// ml->PostTask(FROM_HERE, BindOnce(IgnoreResult(&DoSomething), 1);
|
// ml->PostTask(FROM_HERE, BindOnce(IgnoreResult(&DoSomething), 2);
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static inline internal::IgnoreResultHelper<T> IgnoreResult(T data) {
|
static inline internal::IgnoreResultHelper<T> IgnoreResult(T data) {
|
||||||
return internal::IgnoreResultHelper<T>(std::move(data));
|
return internal::IgnoreResultHelper<T>(std::move(data));
|
||||||
@ -447,8 +454,9 @@ static inline internal::IgnoreResultHelper<T> IgnoreResult(T data) {
|
|||||||
// EXAMPLE OF RetainBlock():
|
// EXAMPLE OF RetainBlock():
|
||||||
//
|
//
|
||||||
// // Wrap the block and bind it to a callback.
|
// // Wrap the block and bind it to a callback.
|
||||||
// Callback<void(int)> cb = Bind(RetainBlock(^(int n) { NSLog(@"%d", n); }));
|
// OnceCallback<void(int)> cb =
|
||||||
// cb.Run(1); // Logs "1".
|
// BindOnce(RetainBlock(^(int n) { NSLog(@"%d", n); }));
|
||||||
|
// std::move(cb).Run(1); // Logs "1".
|
||||||
template <typename R, typename... Args>
|
template <typename R, typename... Args>
|
||||||
base::mac::ScopedBlock<R (^)(Args...)> RetainBlock(R (^block)(Args...)) {
|
base::mac::ScopedBlock<R (^)(Args...)> RetainBlock(R (^block)(Args...)) {
|
||||||
return base::mac::ScopedBlock<R (^)(Args...)>(block,
|
return base::mac::ScopedBlock<R (^)(Args...)>(block,
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
#include "build/build_config.h"
|
#include "build/build_config.h"
|
||||||
|
|
||||||
// This defines a set of simple functions and utilities that people want when
|
// This defines a set of simple functions and utilities that people want when
|
||||||
// using Callback<> and Bind().
|
// using {Once,Repeating}Callback<> and Bind{Once,Repeating}().
|
||||||
|
|
||||||
namespace base {
|
namespace base {
|
||||||
|
|
||||||
|
@ -104,15 +104,16 @@ struct IgnoreResultHelper {
|
|||||||
T functor_;
|
T functor_;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T, typename Deleter = std::default_delete<T>>
|
||||||
class OwnedWrapper {
|
class OwnedWrapper {
|
||||||
public:
|
public:
|
||||||
explicit OwnedWrapper(T* o) : ptr_(o) {}
|
explicit OwnedWrapper(T* o) : ptr_(o) {}
|
||||||
explicit OwnedWrapper(std::unique_ptr<T>&& ptr) : ptr_(std::move(ptr)) {}
|
explicit OwnedWrapper(std::unique_ptr<T, Deleter>&& ptr)
|
||||||
|
: ptr_(std::move(ptr)) {}
|
||||||
T* get() const { return ptr_.get(); }
|
T* get() const { return ptr_.get(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<T> ptr_;
|
std::unique_ptr<T, Deleter> ptr_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// PassedWrapper is a copyable adapter for a scoper that ignores const.
|
// PassedWrapper is a copyable adapter for a scoper that ignores const.
|
||||||
@ -354,10 +355,9 @@ template <typename Functor, typename SFINAE>
|
|||||||
struct FunctorTraits;
|
struct FunctorTraits;
|
||||||
|
|
||||||
// For empty callable types.
|
// For empty callable types.
|
||||||
// This specialization is intended to allow binding captureless lambdas by
|
// This specialization is intended to allow binding captureless lambdas, based
|
||||||
// base::Bind(), based on the fact that captureless lambdas are empty while
|
// on the fact that captureless lambdas are empty while capturing lambdas are
|
||||||
// capturing lambdas are not. This also allows any functors as far as it's an
|
// not. This also allows any functors as far as it's an empty class.
|
||||||
// empty class.
|
|
||||||
// Example:
|
// Example:
|
||||||
//
|
//
|
||||||
// // Captureless lambdas are allowed.
|
// // Captureless lambdas are allowed.
|
||||||
@ -791,10 +791,10 @@ BanUnconstructedRefCountedReceiver(const Receiver& receiver, Unused&&...) {
|
|||||||
//
|
//
|
||||||
// scoped_refptr<Foo> oo = Foo::Create();
|
// scoped_refptr<Foo> oo = Foo::Create();
|
||||||
DCHECK(receiver->HasAtLeastOneRef())
|
DCHECK(receiver->HasAtLeastOneRef())
|
||||||
<< "base::Bind() refuses to create the first reference to ref-counted "
|
<< "base::Bind{Once,Repeating}() refuses to create the first reference "
|
||||||
"objects. That is typically happens around PostTask() in their "
|
"to ref-counted objects. That typically happens around PostTask() in "
|
||||||
"constructor, and such objects can be destroyed before `new` returns "
|
"their constructor, and such objects can be destroyed before `new` "
|
||||||
"if the task resolves fast enough.";
|
"returns if the task resolves fast enough.";
|
||||||
}
|
}
|
||||||
|
|
||||||
// BindState<>
|
// BindState<>
|
||||||
@ -917,7 +917,7 @@ using MakeBindStateType =
|
|||||||
// };
|
// };
|
||||||
//
|
//
|
||||||
// WeakPtr<Foo> oo = nullptr;
|
// WeakPtr<Foo> oo = nullptr;
|
||||||
// base::Bind(&Foo::bar, oo).Run();
|
// base::BindOnce(&Foo::bar, oo).Run();
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct IsWeakReceiver : std::false_type {};
|
struct IsWeakReceiver : std::false_type {};
|
||||||
|
|
||||||
@ -953,9 +953,11 @@ struct BindUnwrapTraits<internal::RetainedRefWrapper<T>> {
|
|||||||
static T* Unwrap(const internal::RetainedRefWrapper<T>& o) { return o.get(); }
|
static T* Unwrap(const internal::RetainedRefWrapper<T>& o) { return o.get(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T, typename Deleter>
|
||||||
struct BindUnwrapTraits<internal::OwnedWrapper<T>> {
|
struct BindUnwrapTraits<internal::OwnedWrapper<T, Deleter>> {
|
||||||
static T* Unwrap(const internal::OwnedWrapper<T>& o) { return o.get(); }
|
static T* Unwrap(const internal::OwnedWrapper<T, Deleter>& o) {
|
||||||
|
return o.get();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
209
security/sandbox/chromium/base/bits.h
Normal file
209
security/sandbox/chromium/base/bits.h
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file defines some bit utilities.
|
||||||
|
|
||||||
|
#ifndef BASE_BITS_H_
|
||||||
|
#define BASE_BITS_H_
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "base/compiler_specific.h"
|
||||||
|
#include "base/logging.h"
|
||||||
|
#include "build/build_config.h"
|
||||||
|
|
||||||
|
#if defined(COMPILER_MSVC)
|
||||||
|
#include <intrin.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace base {
|
||||||
|
namespace bits {
|
||||||
|
|
||||||
|
// Returns true iff |value| is a power of 2.
|
||||||
|
template <typename T,
|
||||||
|
typename = typename std::enable_if<std::is_integral<T>::value>>
|
||||||
|
constexpr inline bool IsPowerOfTwo(T value) {
|
||||||
|
// From "Hacker's Delight": Section 2.1 Manipulating Rightmost Bits.
|
||||||
|
//
|
||||||
|
// Only positive integers with a single bit set are powers of two. If only one
|
||||||
|
// bit is set in x (e.g. 0b00000100000000) then |x-1| will have that bit set
|
||||||
|
// to zero and all bits to its right set to 1 (e.g. 0b00000011111111). Hence
|
||||||
|
// |x & (x-1)| is 0 iff x is a power of two.
|
||||||
|
return value > 0 && (value & (value - 1)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Round up |size| to a multiple of alignment, which must be a power of two.
|
||||||
|
inline size_t Align(size_t size, size_t alignment) {
|
||||||
|
DCHECK(IsPowerOfTwo(alignment));
|
||||||
|
return (size + alignment - 1) & ~(alignment - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Round down |size| to a multiple of alignment, which must be a power of two.
|
||||||
|
inline size_t AlignDown(size_t size, size_t alignment) {
|
||||||
|
DCHECK(IsPowerOfTwo(alignment));
|
||||||
|
return size & ~(alignment - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// CountLeadingZeroBits(value) returns the number of zero bits following the
|
||||||
|
// most significant 1 bit in |value| if |value| is non-zero, otherwise it
|
||||||
|
// returns {sizeof(T) * 8}.
|
||||||
|
// Example: 00100010 -> 2
|
||||||
|
//
|
||||||
|
// CountTrailingZeroBits(value) returns the number of zero bits preceding the
|
||||||
|
// least significant 1 bit in |value| if |value| is non-zero, otherwise it
|
||||||
|
// returns {sizeof(T) * 8}.
|
||||||
|
// Example: 00100010 -> 1
|
||||||
|
//
|
||||||
|
// C does not have an operator to do this, but fortunately the various
|
||||||
|
// compilers have built-ins that map to fast underlying processor instructions.
|
||||||
|
#if defined(COMPILER_MSVC)
|
||||||
|
|
||||||
|
template <typename T, unsigned bits = sizeof(T) * 8>
|
||||||
|
ALWAYS_INLINE
|
||||||
|
typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) <= 4,
|
||||||
|
unsigned>::type
|
||||||
|
CountLeadingZeroBits(T x) {
|
||||||
|
static_assert(bits > 0, "invalid instantiation");
|
||||||
|
unsigned long index;
|
||||||
|
return LIKELY(_BitScanReverse(&index, static_cast<uint32_t>(x)))
|
||||||
|
? (31 - index - (32 - bits))
|
||||||
|
: bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, unsigned bits = sizeof(T) * 8>
|
||||||
|
ALWAYS_INLINE
|
||||||
|
typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) == 8,
|
||||||
|
unsigned>::type
|
||||||
|
CountLeadingZeroBits(T x) {
|
||||||
|
static_assert(bits > 0, "invalid instantiation");
|
||||||
|
unsigned long index;
|
||||||
|
// MSVC only supplies _BitScanReverse64 when building for a 64-bit target.
|
||||||
|
#if defined(ARCH_CPU_64_BITS)
|
||||||
|
return LIKELY(_BitScanReverse64(&index, static_cast<uint64_t>(x)))
|
||||||
|
? (63 - index)
|
||||||
|
: 64;
|
||||||
|
#else
|
||||||
|
uint32_t left = static_cast<uint32_t>(x >> 32);
|
||||||
|
if (LIKELY(_BitScanReverse(&index, left)))
|
||||||
|
return 31 - index;
|
||||||
|
|
||||||
|
uint32_t right = static_cast<uint32_t>(x);
|
||||||
|
if (LIKELY(_BitScanReverse(&index, right)))
|
||||||
|
return 63 - index;
|
||||||
|
|
||||||
|
return 64;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, unsigned bits = sizeof(T) * 8>
|
||||||
|
ALWAYS_INLINE
|
||||||
|
typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) <= 4,
|
||||||
|
unsigned>::type
|
||||||
|
CountTrailingZeroBits(T x) {
|
||||||
|
static_assert(bits > 0, "invalid instantiation");
|
||||||
|
unsigned long index;
|
||||||
|
return LIKELY(_BitScanForward(&index, static_cast<uint32_t>(x))) ? index
|
||||||
|
: bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, unsigned bits = sizeof(T) * 8>
|
||||||
|
ALWAYS_INLINE
|
||||||
|
typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) == 8,
|
||||||
|
unsigned>::type
|
||||||
|
CountTrailingZeroBits(T x) {
|
||||||
|
static_assert(bits > 0, "invalid instantiation");
|
||||||
|
unsigned long index;
|
||||||
|
// MSVC only supplies _BitScanForward64 when building for a 64-bit target.
|
||||||
|
#if defined(ARCH_CPU_64_BITS)
|
||||||
|
return LIKELY(_BitScanForward64(&index, static_cast<uint64_t>(x))) ? index
|
||||||
|
: 64;
|
||||||
|
#else
|
||||||
|
uint32_t right = static_cast<uint32_t>(x);
|
||||||
|
if (LIKELY(_BitScanForward(&index, right)))
|
||||||
|
return index;
|
||||||
|
|
||||||
|
uint32_t left = static_cast<uint32_t>(x >> 32);
|
||||||
|
if (LIKELY(_BitScanForward(&index, left)))
|
||||||
|
return 32 + index;
|
||||||
|
|
||||||
|
return 64;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE uint32_t CountLeadingZeroBits32(uint32_t x) {
|
||||||
|
return CountLeadingZeroBits(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE uint64_t CountLeadingZeroBits64(uint64_t x) {
|
||||||
|
return CountLeadingZeroBits(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(COMPILER_GCC)
|
||||||
|
|
||||||
|
// __builtin_clz has undefined behaviour for an input of 0, even though there's
|
||||||
|
// clearly a return value that makes sense, and even though some processor clz
|
||||||
|
// instructions have defined behaviour for 0. We could drop to raw __asm__ to
|
||||||
|
// do better, but we'll avoid doing that unless we see proof that we need to.
|
||||||
|
template <typename T, unsigned bits = sizeof(T) * 8>
|
||||||
|
ALWAYS_INLINE
|
||||||
|
typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) <= 8,
|
||||||
|
unsigned>::type
|
||||||
|
CountLeadingZeroBits(T value) {
|
||||||
|
static_assert(bits > 0, "invalid instantiation");
|
||||||
|
return LIKELY(value)
|
||||||
|
? bits == 64
|
||||||
|
? __builtin_clzll(static_cast<uint64_t>(value))
|
||||||
|
: __builtin_clz(static_cast<uint32_t>(value)) - (32 - bits)
|
||||||
|
: bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, unsigned bits = sizeof(T) * 8>
|
||||||
|
ALWAYS_INLINE
|
||||||
|
typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) <= 8,
|
||||||
|
unsigned>::type
|
||||||
|
CountTrailingZeroBits(T value) {
|
||||||
|
return LIKELY(value) ? bits == 64
|
||||||
|
? __builtin_ctzll(static_cast<uint64_t>(value))
|
||||||
|
: __builtin_ctz(static_cast<uint32_t>(value))
|
||||||
|
: bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE uint32_t CountLeadingZeroBits32(uint32_t x) {
|
||||||
|
return CountLeadingZeroBits(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE uint64_t CountLeadingZeroBits64(uint64_t x) {
|
||||||
|
return CountLeadingZeroBits(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ALWAYS_INLINE size_t CountLeadingZeroBitsSizeT(size_t x) {
|
||||||
|
return CountLeadingZeroBits(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE size_t CountTrailingZeroBitsSizeT(size_t x) {
|
||||||
|
return CountTrailingZeroBits(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the integer i such as 2^i <= n < 2^(i+1)
|
||||||
|
inline int Log2Floor(uint32_t n) {
|
||||||
|
return 31 - CountLeadingZeroBits(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the integer i such as 2^(i-1) < n <= 2^i
|
||||||
|
inline int Log2Ceiling(uint32_t n) {
|
||||||
|
// When n == 0, we want the function to return -1.
|
||||||
|
// When n == 0, (n - 1) will underflow to 0xFFFFFFFF, which is
|
||||||
|
// why the statement below starts with (n ? 32 : -1).
|
||||||
|
return (n ? 32 : -1) - CountLeadingZeroBits(n - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace bits
|
||||||
|
} // namespace base
|
||||||
|
|
||||||
|
#endif // BASE_BITS_H_
|
@ -2,8 +2,9 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
//
|
//
|
||||||
// NOTE: Header files that do not require the full definition of Callback or
|
// NOTE: Header files that do not require the full definition of
|
||||||
// Closure should #include "base/callback_forward.h" instead of this file.
|
// base::{Once,Repeating}Callback or base::{Once,Repeating}Closure should
|
||||||
|
// #include "base/callback_forward.h" instead of this file.
|
||||||
|
|
||||||
#ifndef BASE_CALLBACK_H_
|
#ifndef BASE_CALLBACK_H_
|
||||||
#define BASE_CALLBACK_H_
|
#define BASE_CALLBACK_H_
|
||||||
@ -42,7 +43,7 @@
|
|||||||
//
|
//
|
||||||
// Callbacks also support cancellation. A common use is binding the receiver
|
// Callbacks also support cancellation. A common use is binding the receiver
|
||||||
// object as a WeakPtr<T>. If that weak pointer is invalidated, calling Run()
|
// object as a WeakPtr<T>. If that weak pointer is invalidated, calling Run()
|
||||||
// will be a no-op. Note that |is_cancelled()| and |is_null()| are distinct:
|
// will be a no-op. Note that |IsCancelled()| and |is_null()| are distinct:
|
||||||
// simply cancelling a callback will not also make it null.
|
// simply cancelling a callback will not also make it null.
|
||||||
//
|
//
|
||||||
// base::Callback is currently a type alias for base::RepeatingCallback. In the
|
// base::Callback is currently a type alias for base::RepeatingCallback. In the
|
||||||
@ -117,10 +118,14 @@ class RepeatingCallback<R(Args...)> : public internal::CallbackBaseCopyable {
|
|||||||
RepeatingCallback(RepeatingCallback&&) noexcept = default;
|
RepeatingCallback(RepeatingCallback&&) noexcept = default;
|
||||||
RepeatingCallback& operator=(RepeatingCallback&&) noexcept = default;
|
RepeatingCallback& operator=(RepeatingCallback&&) noexcept = default;
|
||||||
|
|
||||||
bool Equals(const RepeatingCallback& other) const {
|
bool operator==(const RepeatingCallback& other) const {
|
||||||
return EqualsInternal(other);
|
return EqualsInternal(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool operator!=(const RepeatingCallback& other) const {
|
||||||
|
return !operator==(other);
|
||||||
|
}
|
||||||
|
|
||||||
R Run(Args... args) const & {
|
R Run(Args... args) const & {
|
||||||
PolymorphicInvoke f =
|
PolymorphicInvoke f =
|
||||||
reinterpret_cast<PolymorphicInvoke>(this->polymorphic_invoke());
|
reinterpret_cast<PolymorphicInvoke>(this->polymorphic_invoke());
|
||||||
@ -135,7 +140,7 @@ class RepeatingCallback<R(Args...)> : public internal::CallbackBaseCopyable {
|
|||||||
RepeatingCallback cb = std::move(*this);
|
RepeatingCallback cb = std::move(*this);
|
||||||
PolymorphicInvoke f =
|
PolymorphicInvoke f =
|
||||||
reinterpret_cast<PolymorphicInvoke>(cb.polymorphic_invoke());
|
reinterpret_cast<PolymorphicInvoke>(cb.polymorphic_invoke());
|
||||||
return f(cb.bind_state_.get(), std::forward<Args>(args)...);
|
return f(std::move(cb).bind_state_.get(), std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -16,8 +16,9 @@ class RepeatingCallback;
|
|||||||
template <typename Signature>
|
template <typename Signature>
|
||||||
using Callback = RepeatingCallback<Signature>;
|
using Callback = RepeatingCallback<Signature>;
|
||||||
|
|
||||||
// Syntactic sugar to make Callback<void()> easier to declare since it
|
// Syntactic sugar to make OnceClosure<void()> and RepeatingClosure<void()>
|
||||||
// will be used in a lot of APIs with delayed execution.
|
// easier to declare since they will be used in a lot of APIs with delayed
|
||||||
|
// execution.
|
||||||
using OnceClosure = OnceCallback<void()>;
|
using OnceClosure = OnceCallback<void()>;
|
||||||
using RepeatingClosure = RepeatingCallback<void()>;
|
using RepeatingClosure = RepeatingCallback<void()>;
|
||||||
using Closure = Callback<void()>;
|
using Closure = Callback<void()>;
|
||||||
|
@ -19,14 +19,19 @@ struct FakeBindState;
|
|||||||
|
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
class CallbackBase;
|
|
||||||
class CallbackBaseCopyable;
|
|
||||||
|
|
||||||
class BindStateBase;
|
class BindStateBase;
|
||||||
|
class FinallyExecutorCommon;
|
||||||
|
class ThenAndCatchExecutorCommon;
|
||||||
|
|
||||||
|
template <typename ReturnType>
|
||||||
|
class PostTaskExecutor;
|
||||||
|
|
||||||
template <typename Functor, typename... BoundArgs>
|
template <typename Functor, typename... BoundArgs>
|
||||||
struct BindState;
|
struct BindState;
|
||||||
|
|
||||||
|
class CallbackBase;
|
||||||
|
class CallbackBaseCopyable;
|
||||||
|
|
||||||
struct BindStateBaseRefCountTraits {
|
struct BindStateBaseRefCountTraits {
|
||||||
static void Destruct(const BindStateBase*);
|
static void Destruct(const BindStateBase*);
|
||||||
};
|
};
|
||||||
@ -40,11 +45,10 @@ using PassingType = std::conditional_t<std::is_scalar<T>::value, T, T&&>;
|
|||||||
// DoInvoke function to perform the function execution. This allows
|
// DoInvoke function to perform the function execution. This allows
|
||||||
// us to shield the Callback class from the types of the bound argument via
|
// us to shield the Callback class from the types of the bound argument via
|
||||||
// "type erasure."
|
// "type erasure."
|
||||||
// At the base level, the only task is to add reference counting data. Don't use
|
// At the base level, the only task is to add reference counting data. Avoid
|
||||||
// RefCountedThreadSafe since it requires the destructor to be a virtual method.
|
// using or inheriting any virtual functions. Creating a vtable for every
|
||||||
// Creating a vtable for every BindState template instantiation results in a lot
|
// BindState template instantiation results in a lot of bloat. Its only task is
|
||||||
// of bloat. Its only task is to call the destructor which can be done with a
|
// to call the destructor which can be done with a function pointer.
|
||||||
// function pointer.
|
|
||||||
class BASE_EXPORT BindStateBase
|
class BASE_EXPORT BindStateBase
|
||||||
: public RefCountedThreadSafe<BindStateBase, BindStateBaseRefCountTraits> {
|
: public RefCountedThreadSafe<BindStateBase, BindStateBaseRefCountTraits> {
|
||||||
public:
|
public:
|
||||||
@ -135,6 +139,12 @@ class BASE_EXPORT CallbackBase {
|
|||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
friend class FinallyExecutorCommon;
|
||||||
|
friend class ThenAndCatchExecutorCommon;
|
||||||
|
|
||||||
|
template <typename ReturnType>
|
||||||
|
friend class PostTaskExecutor;
|
||||||
|
|
||||||
using InvokeFuncStorage = BindStateBase::InvokeFuncStorage;
|
using InvokeFuncStorage = BindStateBase::InvokeFuncStorage;
|
||||||
|
|
||||||
// Returns true if this callback equals |other|. |other| may be null.
|
// Returns true if this callback equals |other|. |other| may be null.
|
||||||
|
@ -9,6 +9,10 @@
|
|||||||
|
|
||||||
#if defined(COMPILER_MSVC)
|
#if defined(COMPILER_MSVC)
|
||||||
|
|
||||||
|
#if !defined(__clang__)
|
||||||
|
#error "Only clang-cl is supported on Windows, see https://crbug.com/988071"
|
||||||
|
#endif
|
||||||
|
|
||||||
// Macros for suppressing and disabling warnings on MSVC.
|
// Macros for suppressing and disabling warnings on MSVC.
|
||||||
//
|
//
|
||||||
// Warning numbers are enumerated at:
|
// Warning numbers are enumerated at:
|
||||||
@ -22,8 +26,8 @@
|
|||||||
|
|
||||||
// MSVC_PUSH_DISABLE_WARNING pushes |n| onto a stack of warnings to be disabled.
|
// MSVC_PUSH_DISABLE_WARNING pushes |n| onto a stack of warnings to be disabled.
|
||||||
// The warning remains disabled until popped by MSVC_POP_WARNING.
|
// The warning remains disabled until popped by MSVC_POP_WARNING.
|
||||||
#define MSVC_PUSH_DISABLE_WARNING(n) __pragma(warning(push)) \
|
#define MSVC_PUSH_DISABLE_WARNING(n) \
|
||||||
__pragma(warning(disable:n))
|
__pragma(warning(push)) __pragma(warning(disable : n))
|
||||||
|
|
||||||
// Pop effects of innermost MSVC_PUSH_* macro.
|
// Pop effects of innermost MSVC_PUSH_* macro.
|
||||||
#define MSVC_POP_WARNING() __pragma(warning(pop))
|
#define MSVC_POP_WARNING() __pragma(warning(pop))
|
||||||
@ -137,7 +141,7 @@
|
|||||||
// For member functions, the implicit this parameter counts as index 1.
|
// For member functions, the implicit this parameter counts as index 1.
|
||||||
#if defined(COMPILER_GCC) || defined(__clang__)
|
#if defined(COMPILER_GCC) || defined(__clang__)
|
||||||
#define PRINTF_FORMAT(format_param, dots_param) \
|
#define PRINTF_FORMAT(format_param, dots_param) \
|
||||||
__attribute__((format(printf, format_param, dots_param)))
|
__attribute__((format(printf, format_param, dots_param)))
|
||||||
#else
|
#else
|
||||||
#define PRINTF_FORMAT(format_param, dots_param)
|
#define PRINTF_FORMAT(format_param, dots_param)
|
||||||
#endif
|
#endif
|
||||||
@ -166,14 +170,14 @@
|
|||||||
// Mark a memory region fully initialized.
|
// Mark a memory region fully initialized.
|
||||||
// Use this to annotate code that deliberately reads uninitialized data, for
|
// Use this to annotate code that deliberately reads uninitialized data, for
|
||||||
// example a GC scavenging root set pointers from the stack.
|
// example a GC scavenging root set pointers from the stack.
|
||||||
#define MSAN_UNPOISON(p, size) __msan_unpoison(p, size)
|
#define MSAN_UNPOISON(p, size) __msan_unpoison(p, size)
|
||||||
|
|
||||||
// Check a memory region for initializedness, as if it was being used here.
|
// Check a memory region for initializedness, as if it was being used here.
|
||||||
// If any bits are uninitialized, crash with an MSan report.
|
// If any bits are uninitialized, crash with an MSan report.
|
||||||
// Use this to sanitize data which MSan won't be able to track, e.g. before
|
// Use this to sanitize data which MSan won't be able to track, e.g. before
|
||||||
// passing data to another process via shared memory.
|
// passing data to another process via shared memory.
|
||||||
#define MSAN_CHECK_MEM_IS_INITIALIZED(p, size) \
|
#define MSAN_CHECK_MEM_IS_INITIALIZED(p, size) \
|
||||||
__msan_check_mem_is_initialized(p, size)
|
__msan_check_mem_is_initialized(p, size)
|
||||||
#else // MEMORY_SANITIZER
|
#else // MEMORY_SANITIZER
|
||||||
#define MSAN_UNPOISON(p, size)
|
#define MSAN_UNPOISON(p, size)
|
||||||
#define MSAN_CHECK_MEM_IS_INITIALIZED(p, size)
|
#define MSAN_CHECK_MEM_IS_INITIALIZED(p, size)
|
||||||
@ -238,4 +242,57 @@
|
|||||||
#define PRETTY_FUNCTION __func__
|
#define PRETTY_FUNCTION __func__
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if !defined(CPU_ARM_NEON)
|
||||||
|
#if defined(__arm__)
|
||||||
|
#if !defined(__ARMEB__) && !defined(__ARM_EABI__) && !defined(__EABI__) && \
|
||||||
|
!defined(__VFP_FP__) && !defined(_WIN32_WCE) && !defined(ANDROID)
|
||||||
|
#error Chromium does not support middle endian architecture
|
||||||
|
#endif
|
||||||
|
#if defined(__ARM_NEON__)
|
||||||
|
#define CPU_ARM_NEON 1
|
||||||
|
#endif
|
||||||
|
#endif // defined(__arm__)
|
||||||
|
#endif // !defined(CPU_ARM_NEON)
|
||||||
|
|
||||||
|
#if !defined(HAVE_MIPS_MSA_INTRINSICS)
|
||||||
|
#if defined(__mips_msa) && defined(__mips_isa_rev) && (__mips_isa_rev >= 5)
|
||||||
|
#define HAVE_MIPS_MSA_INTRINSICS 1
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__clang__) && __has_attribute(uninitialized)
|
||||||
|
// Attribute "uninitialized" disables -ftrivial-auto-var-init=pattern for
|
||||||
|
// the specified variable.
|
||||||
|
// Library-wide alternative is
|
||||||
|
// 'configs -= [ "//build/config/compiler:default_init_stack_vars" ]' in .gn
|
||||||
|
// file.
|
||||||
|
//
|
||||||
|
// See "init_stack_vars" in build/config/compiler/BUILD.gn and
|
||||||
|
// http://crbug.com/977230
|
||||||
|
// "init_stack_vars" is enabled for non-official builds and we hope to enable it
|
||||||
|
// in official build in 2020 as well. The flag writes fixed pattern into
|
||||||
|
// uninitialized parts of all local variables. In rare cases such initialization
|
||||||
|
// is undesirable and attribute can be used:
|
||||||
|
// 1. Degraded performance
|
||||||
|
// In most cases compiler is able to remove additional stores. E.g. if memory is
|
||||||
|
// never accessed or properly initialized later. Preserved stores mostly will
|
||||||
|
// not affect program performance. However if compiler failed on some
|
||||||
|
// performance critical code we can get a visible regression in a benchmark.
|
||||||
|
// 2. memset, memcpy calls
|
||||||
|
// Compiler may replaces some memory writes with memset or memcpy calls. This is
|
||||||
|
// not -ftrivial-auto-var-init specific, but it can happen more likely with the
|
||||||
|
// flag. It can be a problem if code is not linked with C run-time library.
|
||||||
|
//
|
||||||
|
// Note: The flag is security risk mitigation feature. So in future the
|
||||||
|
// attribute uses should be avoided when possible. However to enable this
|
||||||
|
// mitigation on the most of the code we need to be less strict now and minimize
|
||||||
|
// number of exceptions later. So if in doubt feel free to use attribute, but
|
||||||
|
// please document the problem for someone who is going to cleanup it later.
|
||||||
|
// E.g. platform, bot, benchmark or test name in patch description or next to
|
||||||
|
// the attribute.
|
||||||
|
#define STACK_UNINITIALIZED __attribute__((uninitialized))
|
||||||
|
#else
|
||||||
|
#define STACK_UNINITIALIZED
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif // BASE_COMPILER_SPECIFIC_H_
|
#endif // BASE_COMPILER_SPECIFIC_H_
|
||||||
|
145
security/sandbox/chromium/base/containers/buffer_iterator.h
Normal file
145
security/sandbox/chromium/base/containers/buffer_iterator.h
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
// Copyright 2019 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef BASE_CONTAINERS_BUFFER_ITERATOR_H_
|
||||||
|
#define BASE_CONTAINERS_BUFFER_ITERATOR_H_
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "base/bit_cast.h"
|
||||||
|
#include "base/containers/span.h"
|
||||||
|
#include "base/numerics/checked_math.h"
|
||||||
|
|
||||||
|
namespace base {
|
||||||
|
|
||||||
|
// BufferIterator is a bounds-checked container utility to access variable-
|
||||||
|
// length, heterogeneous structures contained within a buffer. If the data are
|
||||||
|
// homogeneous, use base::span<> instead.
|
||||||
|
//
|
||||||
|
// After being created with a weakly-owned buffer, BufferIterator returns
|
||||||
|
// pointers to structured data within the buffer. After each method call that
|
||||||
|
// returns data in the buffer, the iterator position is advanced by the byte
|
||||||
|
// size of the object (or span of objects) returned. If there are not enough
|
||||||
|
// bytes remaining in the buffer to return the requested object(s), a nullptr
|
||||||
|
// or empty span is returned.
|
||||||
|
//
|
||||||
|
// This class is similar to base::Pickle, which should be preferred for
|
||||||
|
// serializing to disk. Pickle versions its header and does not support writing
|
||||||
|
// structures, which are problematic for serialization due to struct padding and
|
||||||
|
// version shear concerns.
|
||||||
|
//
|
||||||
|
// Example usage:
|
||||||
|
//
|
||||||
|
// std::vector<uint8_t> buffer(4096);
|
||||||
|
// if (!ReadSomeData(&buffer, buffer.size())) {
|
||||||
|
// LOG(ERROR) << "Failed to read data.";
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// BufferIterator<uint8_t> iterator(buffer);
|
||||||
|
// uint32_t* num_items = iterator.Object<uint32_t>();
|
||||||
|
// if (!num_items) {
|
||||||
|
// LOG(ERROR) << "No num_items field."
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// base::span<const item_struct> items =
|
||||||
|
// iterator.Span<item_struct>(*num_items);
|
||||||
|
// if (items.size() != *num_items) {
|
||||||
|
// LOG(ERROR) << "Not enough items.";
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // ... validate the objects in |items|.
|
||||||
|
template <typename B>
|
||||||
|
class BufferIterator {
|
||||||
|
public:
|
||||||
|
static_assert(std::is_same<std::remove_const_t<B>, char>::value ||
|
||||||
|
std::is_same<std::remove_const_t<B>, unsigned char>::value,
|
||||||
|
"Underlying buffer type must be char-type.");
|
||||||
|
|
||||||
|
BufferIterator() {}
|
||||||
|
BufferIterator(B* data, size_t size)
|
||||||
|
: BufferIterator(make_span(data, size)) {}
|
||||||
|
explicit BufferIterator(span<B> buffer)
|
||||||
|
: buffer_(buffer), remaining_(buffer) {}
|
||||||
|
~BufferIterator() {}
|
||||||
|
|
||||||
|
// Returns a pointer to a mutable structure T in the buffer at the current
|
||||||
|
// position. On success, the iterator position is advanced by sizeof(T). If
|
||||||
|
// there are not sizeof(T) bytes remaining in the buffer, returns nullptr.
|
||||||
|
template <typename T,
|
||||||
|
typename =
|
||||||
|
typename std::enable_if_t<std::is_trivially_copyable<T>::value>>
|
||||||
|
T* MutableObject() {
|
||||||
|
size_t size = sizeof(T);
|
||||||
|
size_t next_position;
|
||||||
|
if (!CheckAdd(position(), size).AssignIfValid(&next_position))
|
||||||
|
return nullptr;
|
||||||
|
if (next_position > total_size())
|
||||||
|
return nullptr;
|
||||||
|
T* t = bit_cast<T*>(remaining_.data());
|
||||||
|
remaining_ = remaining_.subspan(size);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a const pointer to an object of type T in the buffer at the current
|
||||||
|
// position.
|
||||||
|
template <typename T,
|
||||||
|
typename =
|
||||||
|
typename std::enable_if_t<std::is_trivially_copyable<T>::value>>
|
||||||
|
const T* Object() {
|
||||||
|
return MutableObject<const T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a span of |count| T objects in the buffer at the current position.
|
||||||
|
// On success, the iterator position is advanced by |sizeof(T) * count|. If
|
||||||
|
// there are not enough bytes remaining in the buffer to fulfill the request,
|
||||||
|
// returns an empty span.
|
||||||
|
template <typename T,
|
||||||
|
typename =
|
||||||
|
typename std::enable_if_t<std::is_trivially_copyable<T>::value>>
|
||||||
|
span<T> MutableSpan(size_t count) {
|
||||||
|
size_t size;
|
||||||
|
if (!CheckMul(sizeof(T), count).AssignIfValid(&size))
|
||||||
|
return span<T>();
|
||||||
|
size_t next_position;
|
||||||
|
if (!CheckAdd(position(), size).AssignIfValid(&next_position))
|
||||||
|
return span<T>();
|
||||||
|
if (next_position > total_size())
|
||||||
|
return span<T>();
|
||||||
|
auto result = span<T>(bit_cast<T*>(remaining_.data()), count);
|
||||||
|
remaining_ = remaining_.subspan(size);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a span to |count| const objects of type T in the buffer at the
|
||||||
|
// current position.
|
||||||
|
template <typename T,
|
||||||
|
typename =
|
||||||
|
typename std::enable_if_t<std::is_trivially_copyable<T>::value>>
|
||||||
|
span<const T> Span(size_t count) {
|
||||||
|
return MutableSpan<const T>(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resets the iterator position to the absolute offset |to|.
|
||||||
|
void Seek(size_t to) { remaining_ = buffer_.subspan(to); }
|
||||||
|
|
||||||
|
// Returns the total size of the underlying buffer.
|
||||||
|
size_t total_size() { return buffer_.size(); }
|
||||||
|
|
||||||
|
// Returns the current position in the buffer.
|
||||||
|
size_t position() { return buffer_.size_bytes() - remaining_.size_bytes(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// The original buffer that the iterator was constructed with.
|
||||||
|
const span<B> buffer_;
|
||||||
|
// A subspan of |buffer_| containing the remaining bytes to iterate over.
|
||||||
|
span<B> remaining_;
|
||||||
|
// Copy and assign allowed.
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace base
|
||||||
|
|
||||||
|
#endif // BASE_CONTAINERS_BUFFER_ITERATOR_H_
|
205
security/sandbox/chromium/base/containers/checked_iterators.h
Normal file
205
security/sandbox/chromium/base/containers/checked_iterators.h
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
// Copyright 2018 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef BASE_CONTAINERS_CHECKED_ITERATORS_H_
|
||||||
|
#define BASE_CONTAINERS_CHECKED_ITERATORS_H_
|
||||||
|
|
||||||
|
#include <iterator>
|
||||||
|
#include <memory>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "base/containers/util.h"
|
||||||
|
#include "base/logging.h"
|
||||||
|
|
||||||
|
namespace base {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class CheckedContiguousIterator {
|
||||||
|
public:
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
using value_type = std::remove_cv_t<T>;
|
||||||
|
using pointer = T*;
|
||||||
|
using reference = T&;
|
||||||
|
using iterator_category = std::random_access_iterator_tag;
|
||||||
|
|
||||||
|
// Required for converting constructor below.
|
||||||
|
template <typename U>
|
||||||
|
friend class CheckedContiguousIterator;
|
||||||
|
|
||||||
|
constexpr CheckedContiguousIterator() = default;
|
||||||
|
constexpr CheckedContiguousIterator(T* start, const T* end)
|
||||||
|
: CheckedContiguousIterator(start, start, end) {}
|
||||||
|
constexpr CheckedContiguousIterator(const T* start, T* current, const T* end)
|
||||||
|
: start_(start), current_(current), end_(end) {
|
||||||
|
CHECK_LE(start, current);
|
||||||
|
CHECK_LE(current, end);
|
||||||
|
}
|
||||||
|
constexpr CheckedContiguousIterator(const CheckedContiguousIterator& other) =
|
||||||
|
default;
|
||||||
|
|
||||||
|
// Converting constructor allowing conversions like CCI<T> to CCI<const T>,
|
||||||
|
// but disallowing CCI<const T> to CCI<T> or CCI<Derived> to CCI<Base>, which
|
||||||
|
// are unsafe. Furthermore, this is the same condition as used by the
|
||||||
|
// converting constructors of std::span<T> and std::unique_ptr<T[]>.
|
||||||
|
// See https://wg21.link/n4042 for details.
|
||||||
|
template <
|
||||||
|
typename U,
|
||||||
|
std::enable_if_t<std::is_convertible<U (*)[], T (*)[]>::value>* = nullptr>
|
||||||
|
constexpr CheckedContiguousIterator(const CheckedContiguousIterator<U>& other)
|
||||||
|
: start_(other.start_), current_(other.current_), end_(other.end_) {
|
||||||
|
// We explicitly don't delegate to the 3-argument constructor here. Its
|
||||||
|
// CHECKs would be redundant, since we expect |other| to maintain its own
|
||||||
|
// invariant. However, DCHECKs never hurt anybody. Presumably.
|
||||||
|
DCHECK_LE(other.start_, other.current_);
|
||||||
|
DCHECK_LE(other.current_, other.end_);
|
||||||
|
}
|
||||||
|
|
||||||
|
~CheckedContiguousIterator() = default;
|
||||||
|
|
||||||
|
constexpr CheckedContiguousIterator& operator=(
|
||||||
|
const CheckedContiguousIterator& other) = default;
|
||||||
|
|
||||||
|
constexpr bool operator==(const CheckedContiguousIterator& other) const {
|
||||||
|
CheckComparable(other);
|
||||||
|
return current_ == other.current_;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool operator!=(const CheckedContiguousIterator& other) const {
|
||||||
|
CheckComparable(other);
|
||||||
|
return current_ != other.current_;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool operator<(const CheckedContiguousIterator& other) const {
|
||||||
|
CheckComparable(other);
|
||||||
|
return current_ < other.current_;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool operator<=(const CheckedContiguousIterator& other) const {
|
||||||
|
CheckComparable(other);
|
||||||
|
return current_ <= other.current_;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool operator>(const CheckedContiguousIterator& other) const {
|
||||||
|
CheckComparable(other);
|
||||||
|
return current_ > other.current_;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool operator>=(const CheckedContiguousIterator& other) const {
|
||||||
|
CheckComparable(other);
|
||||||
|
return current_ >= other.current_;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr CheckedContiguousIterator& operator++() {
|
||||||
|
CHECK_NE(current_, end_);
|
||||||
|
++current_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr CheckedContiguousIterator operator++(int) {
|
||||||
|
CheckedContiguousIterator old = *this;
|
||||||
|
++*this;
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr CheckedContiguousIterator& operator--() {
|
||||||
|
CHECK_NE(current_, start_);
|
||||||
|
--current_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr CheckedContiguousIterator operator--(int) {
|
||||||
|
CheckedContiguousIterator old = *this;
|
||||||
|
--*this;
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr CheckedContiguousIterator& operator+=(difference_type rhs) {
|
||||||
|
if (rhs > 0) {
|
||||||
|
CHECK_LE(rhs, end_ - current_);
|
||||||
|
} else {
|
||||||
|
CHECK_LE(-rhs, current_ - start_);
|
||||||
|
}
|
||||||
|
current_ += rhs;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr CheckedContiguousIterator operator+(difference_type rhs) const {
|
||||||
|
CheckedContiguousIterator it = *this;
|
||||||
|
it += rhs;
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr CheckedContiguousIterator& operator-=(difference_type rhs) {
|
||||||
|
if (rhs < 0) {
|
||||||
|
CHECK_LE(-rhs, end_ - current_);
|
||||||
|
} else {
|
||||||
|
CHECK_LE(rhs, current_ - start_);
|
||||||
|
}
|
||||||
|
current_ -= rhs;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr CheckedContiguousIterator operator-(difference_type rhs) const {
|
||||||
|
CheckedContiguousIterator it = *this;
|
||||||
|
it -= rhs;
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr friend difference_type operator-(
|
||||||
|
const CheckedContiguousIterator& lhs,
|
||||||
|
const CheckedContiguousIterator& rhs) {
|
||||||
|
CHECK_EQ(lhs.start_, rhs.start_);
|
||||||
|
CHECK_EQ(lhs.end_, rhs.end_);
|
||||||
|
return lhs.current_ - rhs.current_;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr reference operator*() const {
|
||||||
|
CHECK_NE(current_, end_);
|
||||||
|
return *current_;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr pointer operator->() const {
|
||||||
|
CHECK_NE(current_, end_);
|
||||||
|
return current_;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr reference operator[](difference_type rhs) const {
|
||||||
|
CHECK_GE(rhs, 0);
|
||||||
|
CHECK_LT(rhs, end_ - current_);
|
||||||
|
return current_[rhs];
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool IsRangeMoveSafe(const CheckedContiguousIterator& from_begin,
|
||||||
|
const CheckedContiguousIterator& from_end,
|
||||||
|
const CheckedContiguousIterator& to)
|
||||||
|
WARN_UNUSED_RESULT {
|
||||||
|
if (from_end < from_begin)
|
||||||
|
return false;
|
||||||
|
const auto from_begin_uintptr = get_uintptr(from_begin.current_);
|
||||||
|
const auto from_end_uintptr = get_uintptr(from_end.current_);
|
||||||
|
const auto to_begin_uintptr = get_uintptr(to.current_);
|
||||||
|
const auto to_end_uintptr =
|
||||||
|
get_uintptr((to + std::distance(from_begin, from_end)).current_);
|
||||||
|
|
||||||
|
return to_begin_uintptr >= from_end_uintptr ||
|
||||||
|
to_end_uintptr <= from_begin_uintptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
constexpr void CheckComparable(const CheckedContiguousIterator& other) const {
|
||||||
|
CHECK_EQ(start_, other.start_);
|
||||||
|
CHECK_EQ(end_, other.end_);
|
||||||
|
}
|
||||||
|
|
||||||
|
const T* start_ = nullptr;
|
||||||
|
T* current_ = nullptr;
|
||||||
|
const T* end_ = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using CheckedContiguousConstIterator = CheckedContiguousIterator<const T>;
|
||||||
|
|
||||||
|
} // namespace base
|
||||||
|
|
||||||
|
#endif // BASE_CONTAINERS_CHECKED_ITERATORS_H_
|
@ -14,6 +14,7 @@
|
|||||||
#include "base/containers/vector_buffer.h"
|
#include "base/containers/vector_buffer.h"
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
#include "base/macros.h"
|
#include "base/macros.h"
|
||||||
|
#include "base/stl_util.h"
|
||||||
#include "base/template_util.h"
|
#include "base/template_util.h"
|
||||||
|
|
||||||
// base::circular_deque is similar to std::deque. Unlike std::deque, the
|
// base::circular_deque is similar to std::deque. Unlike std::deque, the
|
||||||
@ -521,15 +522,15 @@ class circular_deque {
|
|||||||
return buffer_[i - right_size];
|
return buffer_[i - right_size];
|
||||||
}
|
}
|
||||||
value_type& at(size_type i) {
|
value_type& at(size_type i) {
|
||||||
return const_cast<value_type&>(
|
return const_cast<value_type&>(as_const(*this).at(i));
|
||||||
const_cast<const circular_deque*>(this)->at(i));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
value_type& operator[](size_type i) { return at(i); }
|
value_type& operator[](size_type i) {
|
||||||
const value_type& operator[](size_type i) const {
|
return const_cast<value_type&>(as_const(*this)[i]);
|
||||||
return const_cast<circular_deque*>(this)->at(i);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const value_type& operator[](size_type i) const { return at(i); }
|
||||||
|
|
||||||
value_type& front() {
|
value_type& front() {
|
||||||
DCHECK(!empty());
|
DCHECK(!empty());
|
||||||
return buffer_[begin_];
|
return buffer_[begin_];
|
||||||
|
530
security/sandbox/chromium/base/containers/span.h
Normal file
530
security/sandbox/chromium/base/containers/span.h
Normal file
@ -0,0 +1,530 @@
|
|||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef BASE_CONTAINERS_SPAN_H_
|
||||||
|
#define BASE_CONTAINERS_SPAN_H_
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <iterator>
|
||||||
|
#include <limits>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "base/containers/checked_iterators.h"
|
||||||
|
#include "base/logging.h"
|
||||||
|
#include "base/macros.h"
|
||||||
|
#include "base/stl_util.h"
|
||||||
|
|
||||||
|
namespace base {
|
||||||
|
|
||||||
|
// [views.constants]
|
||||||
|
constexpr size_t dynamic_extent = std::numeric_limits<size_t>::max();
|
||||||
|
|
||||||
|
template <typename T, size_t Extent = dynamic_extent>
|
||||||
|
class span;
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct ExtentImpl : std::integral_constant<size_t, dynamic_extent> {};
|
||||||
|
|
||||||
|
template <typename T, size_t N>
|
||||||
|
struct ExtentImpl<T[N]> : std::integral_constant<size_t, N> {};
|
||||||
|
|
||||||
|
template <typename T, size_t N>
|
||||||
|
struct ExtentImpl<std::array<T, N>> : std::integral_constant<size_t, N> {};
|
||||||
|
|
||||||
|
template <typename T, size_t N>
|
||||||
|
struct ExtentImpl<base::span<T, N>> : std::integral_constant<size_t, N> {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using Extent = ExtentImpl<std::remove_cv_t<std::remove_reference_t<T>>>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct IsSpanImpl : std::false_type {};
|
||||||
|
|
||||||
|
template <typename T, size_t Extent>
|
||||||
|
struct IsSpanImpl<span<T, Extent>> : std::true_type {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using IsSpan = IsSpanImpl<std::decay_t<T>>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct IsStdArrayImpl : std::false_type {};
|
||||||
|
|
||||||
|
template <typename T, size_t N>
|
||||||
|
struct IsStdArrayImpl<std::array<T, N>> : std::true_type {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using IsStdArray = IsStdArrayImpl<std::decay_t<T>>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using IsCArray = std::is_array<std::remove_reference_t<T>>;
|
||||||
|
|
||||||
|
template <typename From, typename To>
|
||||||
|
using IsLegalDataConversion = std::is_convertible<From (*)[], To (*)[]>;
|
||||||
|
|
||||||
|
template <typename Container, typename T>
|
||||||
|
using ContainerHasConvertibleData = IsLegalDataConversion<
|
||||||
|
std::remove_pointer_t<decltype(base::data(std::declval<Container>()))>,
|
||||||
|
T>;
|
||||||
|
|
||||||
|
template <typename Container>
|
||||||
|
using ContainerHasIntegralSize =
|
||||||
|
std::is_integral<decltype(base::size(std::declval<Container>()))>;
|
||||||
|
|
||||||
|
template <typename From, size_t FromExtent, typename To, size_t ToExtent>
|
||||||
|
using EnableIfLegalSpanConversion =
|
||||||
|
std::enable_if_t<(ToExtent == dynamic_extent || ToExtent == FromExtent) &&
|
||||||
|
IsLegalDataConversion<From, To>::value>;
|
||||||
|
|
||||||
|
// SFINAE check if Array can be converted to a span<T>.
|
||||||
|
template <typename Array, typename T, size_t Extent>
|
||||||
|
using EnableIfSpanCompatibleArray =
|
||||||
|
std::enable_if_t<(Extent == dynamic_extent ||
|
||||||
|
Extent == internal::Extent<Array>::value) &&
|
||||||
|
ContainerHasConvertibleData<Array, T>::value>;
|
||||||
|
|
||||||
|
// SFINAE check if Container can be converted to a span<T>.
|
||||||
|
template <typename Container, typename T>
|
||||||
|
using IsSpanCompatibleContainer =
|
||||||
|
std::conditional_t<!IsSpan<Container>::value &&
|
||||||
|
!IsStdArray<Container>::value &&
|
||||||
|
!IsCArray<Container>::value &&
|
||||||
|
ContainerHasConvertibleData<Container, T>::value &&
|
||||||
|
ContainerHasIntegralSize<Container>::value,
|
||||||
|
std::true_type,
|
||||||
|
std::false_type>;
|
||||||
|
|
||||||
|
template <typename Container, typename T>
|
||||||
|
using EnableIfSpanCompatibleContainer =
|
||||||
|
std::enable_if_t<IsSpanCompatibleContainer<Container, T>::value>;
|
||||||
|
|
||||||
|
template <typename Container, typename T, size_t Extent>
|
||||||
|
using EnableIfSpanCompatibleContainerAndSpanIsDynamic =
|
||||||
|
std::enable_if_t<IsSpanCompatibleContainer<Container, T>::value &&
|
||||||
|
Extent == dynamic_extent>;
|
||||||
|
|
||||||
|
// A helper template for storing the size of a span. Spans with static extents
|
||||||
|
// don't require additional storage, since the extent itself is specified in the
|
||||||
|
// template parameter.
|
||||||
|
template <size_t Extent>
|
||||||
|
class ExtentStorage {
|
||||||
|
public:
|
||||||
|
constexpr explicit ExtentStorage(size_t size) noexcept {}
|
||||||
|
constexpr size_t size() const noexcept { return Extent; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Specialization of ExtentStorage for dynamic extents, which do require
|
||||||
|
// explicit storage for the size.
|
||||||
|
template <>
|
||||||
|
struct ExtentStorage<dynamic_extent> {
|
||||||
|
constexpr explicit ExtentStorage(size_t size) noexcept : size_(size) {}
|
||||||
|
constexpr size_t size() const noexcept { return size_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t size_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
// A span is a value type that represents an array of elements of type T. Since
|
||||||
|
// it only consists of a pointer to memory with an associated size, it is very
|
||||||
|
// light-weight. It is cheap to construct, copy, move and use spans, so that
|
||||||
|
// users are encouraged to use it as a pass-by-value parameter. A span does not
|
||||||
|
// own the underlying memory, so care must be taken to ensure that a span does
|
||||||
|
// not outlive the backing store.
|
||||||
|
//
|
||||||
|
// span is somewhat analogous to StringPiece, but with arbitrary element types,
|
||||||
|
// allowing mutation if T is non-const.
|
||||||
|
//
|
||||||
|
// span is implicitly convertible from C++ arrays, as well as most [1]
|
||||||
|
// container-like types that provide a data() and size() method (such as
|
||||||
|
// std::vector<T>). A mutable span<T> can also be implicitly converted to an
|
||||||
|
// immutable span<const T>.
|
||||||
|
//
|
||||||
|
// Consider using a span for functions that take a data pointer and size
|
||||||
|
// parameter: it allows the function to still act on an array-like type, while
|
||||||
|
// allowing the caller code to be a bit more concise.
|
||||||
|
//
|
||||||
|
// For read-only data access pass a span<const T>: the caller can supply either
|
||||||
|
// a span<const T> or a span<T>, while the callee will have a read-only view.
|
||||||
|
// For read-write access a mutable span<T> is required.
|
||||||
|
//
|
||||||
|
// Without span:
|
||||||
|
// Read-Only:
|
||||||
|
// // std::string HexEncode(const uint8_t* data, size_t size);
|
||||||
|
// std::vector<uint8_t> data_buffer = GenerateData();
|
||||||
|
// std::string r = HexEncode(data_buffer.data(), data_buffer.size());
|
||||||
|
//
|
||||||
|
// Mutable:
|
||||||
|
// // ssize_t SafeSNPrintf(char* buf, size_t N, const char* fmt, Args...);
|
||||||
|
// char str_buffer[100];
|
||||||
|
// SafeSNPrintf(str_buffer, sizeof(str_buffer), "Pi ~= %lf", 3.14);
|
||||||
|
//
|
||||||
|
// With span:
|
||||||
|
// Read-Only:
|
||||||
|
// // std::string HexEncode(base::span<const uint8_t> data);
|
||||||
|
// std::vector<uint8_t> data_buffer = GenerateData();
|
||||||
|
// std::string r = HexEncode(data_buffer);
|
||||||
|
//
|
||||||
|
// Mutable:
|
||||||
|
// // ssize_t SafeSNPrintf(base::span<char>, const char* fmt, Args...);
|
||||||
|
// char str_buffer[100];
|
||||||
|
// SafeSNPrintf(str_buffer, "Pi ~= %lf", 3.14);
|
||||||
|
//
|
||||||
|
// Spans with "const" and pointers
|
||||||
|
// -------------------------------
|
||||||
|
//
|
||||||
|
// Const and pointers can get confusing. Here are vectors of pointers and their
|
||||||
|
// corresponding spans:
|
||||||
|
//
|
||||||
|
// const std::vector<int*> => base::span<int* const>
|
||||||
|
// std::vector<const int*> => base::span<const int*>
|
||||||
|
// const std::vector<const int*> => base::span<const int* const>
|
||||||
|
//
|
||||||
|
// Differences from the C++20 draft
|
||||||
|
// --------------------------------
|
||||||
|
//
|
||||||
|
// http://eel.is/c++draft/views contains the latest C++20 draft of std::span.
|
||||||
|
// Chromium tries to follow the draft as close as possible. Differences between
|
||||||
|
// the draft and the implementation are documented in subsections below.
|
||||||
|
//
|
||||||
|
// Differences from [span.objectrep]:
|
||||||
|
// - as_bytes() and as_writable_bytes() return spans of uint8_t instead of
|
||||||
|
// std::byte (std::byte is a C++17 feature)
|
||||||
|
//
|
||||||
|
// Differences from [span.cons]:
|
||||||
|
// - Constructing a static span (i.e. Extent != dynamic_extent) from a dynamic
|
||||||
|
// sized container (e.g. std::vector) requires an explicit conversion (in the
|
||||||
|
// C++20 draft this is simply UB)
|
||||||
|
//
|
||||||
|
// Differences from [span.obs]:
|
||||||
|
// - empty() is marked with WARN_UNUSED_RESULT instead of [[nodiscard]]
|
||||||
|
// ([[nodiscard]] is a C++17 feature)
|
||||||
|
//
|
||||||
|
// Furthermore, all constructors and methods are marked noexcept due to the lack
|
||||||
|
// of exceptions in Chromium.
|
||||||
|
//
|
||||||
|
// Due to the lack of class template argument deduction guides in C++14
|
||||||
|
// appropriate make_span() utility functions are provided.
|
||||||
|
|
||||||
|
// [span], class template span
|
||||||
|
template <typename T, size_t Extent>
|
||||||
|
class span : public internal::ExtentStorage<Extent> {
|
||||||
|
private:
|
||||||
|
using ExtentStorage = internal::ExtentStorage<Extent>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using element_type = T;
|
||||||
|
using value_type = std::remove_cv_t<T>;
|
||||||
|
using size_type = size_t;
|
||||||
|
using difference_type = ptrdiff_t;
|
||||||
|
using pointer = T*;
|
||||||
|
using reference = T&;
|
||||||
|
using iterator = CheckedContiguousIterator<T>;
|
||||||
|
using const_iterator = CheckedContiguousConstIterator<T>;
|
||||||
|
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||||
|
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||||
|
static constexpr size_t extent = Extent;
|
||||||
|
|
||||||
|
// [span.cons], span constructors, copy, assignment, and destructor
|
||||||
|
constexpr span() noexcept : ExtentStorage(0), data_(nullptr) {
|
||||||
|
static_assert(Extent == dynamic_extent || Extent == 0, "Invalid Extent");
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr span(T* data, size_t size) noexcept
|
||||||
|
: ExtentStorage(size), data_(data) {
|
||||||
|
CHECK(Extent == dynamic_extent || Extent == size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Artificially templatized to break ambiguity for span(ptr, 0).
|
||||||
|
template <typename = void>
|
||||||
|
constexpr span(T* begin, T* end) noexcept : span(begin, end - begin) {
|
||||||
|
// Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
|
||||||
|
CHECK(begin <= end);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <
|
||||||
|
size_t N,
|
||||||
|
typename = internal::EnableIfSpanCompatibleArray<T (&)[N], T, Extent>>
|
||||||
|
constexpr span(T (&array)[N]) noexcept : span(base::data(array), N) {}
|
||||||
|
|
||||||
|
template <
|
||||||
|
size_t N,
|
||||||
|
typename = internal::
|
||||||
|
EnableIfSpanCompatibleArray<std::array<value_type, N>&, T, Extent>>
|
||||||
|
constexpr span(std::array<value_type, N>& array) noexcept
|
||||||
|
: span(base::data(array), N) {}
|
||||||
|
|
||||||
|
template <size_t N,
|
||||||
|
typename = internal::EnableIfSpanCompatibleArray<
|
||||||
|
const std::array<value_type, N>&,
|
||||||
|
T,
|
||||||
|
Extent>>
|
||||||
|
constexpr span(const std::array<value_type, N>& array) noexcept
|
||||||
|
: span(base::data(array), N) {}
|
||||||
|
|
||||||
|
// Conversion from a container that has compatible base::data() and integral
|
||||||
|
// base::size().
|
||||||
|
template <
|
||||||
|
typename Container,
|
||||||
|
typename =
|
||||||
|
internal::EnableIfSpanCompatibleContainerAndSpanIsDynamic<Container&,
|
||||||
|
T,
|
||||||
|
Extent>>
|
||||||
|
constexpr span(Container& container) noexcept
|
||||||
|
: span(base::data(container), base::size(container)) {}
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename Container,
|
||||||
|
typename = internal::EnableIfSpanCompatibleContainerAndSpanIsDynamic<
|
||||||
|
const Container&,
|
||||||
|
T,
|
||||||
|
Extent>>
|
||||||
|
constexpr span(const Container& container) noexcept
|
||||||
|
: span(base::data(container), base::size(container)) {}
|
||||||
|
|
||||||
|
constexpr span(const span& other) noexcept = default;
|
||||||
|
|
||||||
|
// Conversions from spans of compatible types and extents: this allows a
|
||||||
|
// span<T> to be seamlessly used as a span<const T>, but not the other way
|
||||||
|
// around. If extent is not dynamic, OtherExtent has to be equal to Extent.
|
||||||
|
template <
|
||||||
|
typename U,
|
||||||
|
size_t OtherExtent,
|
||||||
|
typename =
|
||||||
|
internal::EnableIfLegalSpanConversion<U, OtherExtent, T, Extent>>
|
||||||
|
constexpr span(const span<U, OtherExtent>& other)
|
||||||
|
: span(other.data(), other.size()) {}
|
||||||
|
|
||||||
|
constexpr span& operator=(const span& other) noexcept = default;
|
||||||
|
~span() noexcept = default;
|
||||||
|
|
||||||
|
// [span.sub], span subviews
|
||||||
|
template <size_t Count>
|
||||||
|
constexpr span<T, Count> first() const noexcept {
|
||||||
|
static_assert(Extent == dynamic_extent || Count <= Extent,
|
||||||
|
"Count must not exceed Extent");
|
||||||
|
CHECK(Extent != dynamic_extent || Count <= size());
|
||||||
|
return {data(), Count};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t Count>
|
||||||
|
constexpr span<T, Count> last() const noexcept {
|
||||||
|
static_assert(Extent == dynamic_extent || Count <= Extent,
|
||||||
|
"Count must not exceed Extent");
|
||||||
|
CHECK(Extent != dynamic_extent || Count <= size());
|
||||||
|
return {data() + (size() - Count), Count};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t Offset, size_t Count = dynamic_extent>
|
||||||
|
constexpr span<T,
|
||||||
|
(Count != dynamic_extent
|
||||||
|
? Count
|
||||||
|
: (Extent != dynamic_extent ? Extent - Offset
|
||||||
|
: dynamic_extent))>
|
||||||
|
subspan() const noexcept {
|
||||||
|
static_assert(Extent == dynamic_extent || Offset <= Extent,
|
||||||
|
"Offset must not exceed Extent");
|
||||||
|
static_assert(Extent == dynamic_extent || Count == dynamic_extent ||
|
||||||
|
Count <= Extent - Offset,
|
||||||
|
"Count must not exceed Extent - Offset");
|
||||||
|
CHECK(Extent != dynamic_extent || Offset <= size());
|
||||||
|
CHECK(Extent != dynamic_extent || Count == dynamic_extent ||
|
||||||
|
Count <= size() - Offset);
|
||||||
|
return {data() + Offset, Count != dynamic_extent ? Count : size() - Offset};
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr span<T, dynamic_extent> first(size_t count) const noexcept {
|
||||||
|
// Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
|
||||||
|
CHECK(count <= size());
|
||||||
|
return {data(), count};
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr span<T, dynamic_extent> last(size_t count) const noexcept {
|
||||||
|
// Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
|
||||||
|
CHECK(count <= size());
|
||||||
|
return {data() + (size() - count), count};
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr span<T, dynamic_extent> subspan(size_t offset,
|
||||||
|
size_t count = dynamic_extent) const
|
||||||
|
noexcept {
|
||||||
|
// Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
|
||||||
|
CHECK(offset <= size());
|
||||||
|
CHECK(count == dynamic_extent || count <= size() - offset);
|
||||||
|
return {data() + offset, count != dynamic_extent ? count : size() - offset};
|
||||||
|
}
|
||||||
|
|
||||||
|
// [span.obs], span observers
|
||||||
|
constexpr size_t size() const noexcept { return ExtentStorage::size(); }
|
||||||
|
constexpr size_t size_bytes() const noexcept { return size() * sizeof(T); }
|
||||||
|
constexpr bool empty() const noexcept WARN_UNUSED_RESULT {
|
||||||
|
return size() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// [span.elem], span element access
|
||||||
|
constexpr T& operator[](size_t idx) const noexcept {
|
||||||
|
// Note: CHECK_LT is not constexpr, hence regular CHECK must be used.
|
||||||
|
CHECK(idx < size());
|
||||||
|
return *(data() + idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr T& front() const noexcept {
|
||||||
|
static_assert(Extent == dynamic_extent || Extent > 0,
|
||||||
|
"Extent must not be 0");
|
||||||
|
CHECK(Extent != dynamic_extent || !empty());
|
||||||
|
return *data();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr T& back() const noexcept {
|
||||||
|
static_assert(Extent == dynamic_extent || Extent > 0,
|
||||||
|
"Extent must not be 0");
|
||||||
|
CHECK(Extent != dynamic_extent || !empty());
|
||||||
|
return *(data() + size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr T* data() const noexcept { return data_; }
|
||||||
|
|
||||||
|
// [span.iter], span iterator support
|
||||||
|
constexpr iterator begin() const noexcept {
|
||||||
|
return iterator(data_, data_ + size());
|
||||||
|
}
|
||||||
|
constexpr iterator end() const noexcept {
|
||||||
|
return iterator(data_, data_ + size(), data_ + size());
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const_iterator cbegin() const noexcept { return begin(); }
|
||||||
|
constexpr const_iterator cend() const noexcept { return end(); }
|
||||||
|
|
||||||
|
constexpr reverse_iterator rbegin() const noexcept {
|
||||||
|
return reverse_iterator(end());
|
||||||
|
}
|
||||||
|
constexpr reverse_iterator rend() const noexcept {
|
||||||
|
return reverse_iterator(begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const_reverse_iterator crbegin() const noexcept {
|
||||||
|
return const_reverse_iterator(cend());
|
||||||
|
}
|
||||||
|
constexpr const_reverse_iterator crend() const noexcept {
|
||||||
|
return const_reverse_iterator(cbegin());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
T* data_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// span<T, Extent>::extent can not be declared inline prior to C++17, hence this
|
||||||
|
// definition is required.
|
||||||
|
template <class T, size_t Extent>
|
||||||
|
constexpr size_t span<T, Extent>::extent;
|
||||||
|
|
||||||
|
// [span.objectrep], views of object representation
|
||||||
|
template <typename T, size_t X>
|
||||||
|
span<const uint8_t, (X == dynamic_extent ? dynamic_extent : sizeof(T) * X)>
|
||||||
|
as_bytes(span<T, X> s) noexcept {
|
||||||
|
return {reinterpret_cast<const uint8_t*>(s.data()), s.size_bytes()};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T,
|
||||||
|
size_t X,
|
||||||
|
typename = std::enable_if_t<!std::is_const<T>::value>>
|
||||||
|
span<uint8_t, (X == dynamic_extent ? dynamic_extent : sizeof(T) * X)>
|
||||||
|
as_writable_bytes(span<T, X> s) noexcept {
|
||||||
|
return {reinterpret_cast<uint8_t*>(s.data()), s.size_bytes()};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type-deducing helpers for constructing a span.
|
||||||
|
template <int&... ExplicitArgumentBarrier, typename T>
|
||||||
|
constexpr span<T> make_span(T* data, size_t size) noexcept {
|
||||||
|
return {data, size};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int&... ExplicitArgumentBarrier, typename T>
|
||||||
|
constexpr span<T> make_span(T* begin, T* end) noexcept {
|
||||||
|
return {begin, end};
|
||||||
|
}
|
||||||
|
|
||||||
|
// make_span utility function that deduces both the span's value_type and extent
|
||||||
|
// from the passed in argument.
|
||||||
|
//
|
||||||
|
// Usage: auto span = base::make_span(...);
|
||||||
|
template <int&... ExplicitArgumentBarrier, typename Container>
|
||||||
|
constexpr auto make_span(Container&& container) noexcept {
|
||||||
|
using T =
|
||||||
|
std::remove_pointer_t<decltype(base::data(std::declval<Container>()))>;
|
||||||
|
using Extent = internal::Extent<Container>;
|
||||||
|
return span<T, Extent::value>(std::forward<Container>(container));
|
||||||
|
}
|
||||||
|
|
||||||
|
// make_span utility function that allows callers to explicit specify the span's
|
||||||
|
// extent, the value_type is deduced automatically. This is useful when passing
|
||||||
|
// a dynamically sized container to a method expecting static spans, when the
|
||||||
|
// container is known to have the correct size.
|
||||||
|
//
|
||||||
|
// Note: This will CHECK that N indeed matches size(container).
|
||||||
|
//
|
||||||
|
// Usage: auto static_span = base::make_span<N>(...);
|
||||||
|
template <size_t N, int&... ExplicitArgumentBarrier, typename Container>
|
||||||
|
constexpr auto make_span(Container&& container) noexcept {
|
||||||
|
using T =
|
||||||
|
std::remove_pointer_t<decltype(base::data(std::declval<Container>()))>;
|
||||||
|
return span<T, N>(base::data(container), base::size(container));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace base
|
||||||
|
|
||||||
|
// Note: std::tuple_size, std::tuple_element and std::get are specialized for
|
||||||
|
// static spans, so that they can be used in C++17's structured bindings. While
|
||||||
|
// we don't support C++17 yet, there is no harm in providing these
|
||||||
|
// specializations already.
|
||||||
|
namespace std {
|
||||||
|
|
||||||
|
// [span.tuple], tuple interface
|
||||||
|
#if defined(__clang__)
|
||||||
|
// Due to https://llvm.org/PR39871 and https://llvm.org/PR41331 and their
|
||||||
|
// respective fixes different versions of libc++ declare std::tuple_size and
|
||||||
|
// std::tuple_element either as classes or structs. In order to be able to
|
||||||
|
// specialize std::tuple_size and std::tuple_element for custom base types we
|
||||||
|
// thus need to disable -Wmismatched-tags in order to support all build
|
||||||
|
// configurations. Note that this is blessed by the standard in
|
||||||
|
// https://timsong-cpp.github.io/cppwp/n4140/dcl.type.elab#3.
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wmismatched-tags"
|
||||||
|
#endif
|
||||||
|
template <typename T, size_t X>
|
||||||
|
struct tuple_size<base::span<T, X>> : public integral_constant<size_t, X> {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct tuple_size<base::span<T, base::dynamic_extent>>; // not defined
|
||||||
|
|
||||||
|
template <size_t I, typename T, size_t X>
|
||||||
|
struct tuple_element<I, base::span<T, X>> {
|
||||||
|
static_assert(
|
||||||
|
base::dynamic_extent != X,
|
||||||
|
"std::tuple_element<> not supported for base::span<T, dynamic_extent>");
|
||||||
|
static_assert(I < X,
|
||||||
|
"Index out of bounds in std::tuple_element<> (base::span)");
|
||||||
|
using type = T;
|
||||||
|
};
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic pop // -Wmismatched-tags
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <size_t I, typename T, size_t X>
|
||||||
|
constexpr T& get(base::span<T, X> s) noexcept {
|
||||||
|
static_assert(base::dynamic_extent != X,
|
||||||
|
"std::get<> not supported for base::span<T, dynamic_extent>");
|
||||||
|
static_assert(I < X, "Index out of bounds in std::get<> (base::span)");
|
||||||
|
return s[I];
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace std
|
||||||
|
|
||||||
|
#endif // BASE_CONTAINERS_SPAN_H_
|
@ -13,7 +13,6 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "base/stl_util.h"
|
#include "base/stl_util.h"
|
||||||
#include "build/build_config.h"
|
|
||||||
|
|
||||||
#if defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX))
|
#if defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX))
|
||||||
#include "base/files/file_util.h"
|
#include "base/files/file_util.h"
|
||||||
@ -28,6 +27,44 @@
|
|||||||
|
|
||||||
namespace base {
|
namespace base {
|
||||||
|
|
||||||
|
#if defined(ARCH_CPU_X86_FAMILY)
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
std::tuple<int, int, int, int> ComputeX86FamilyAndModel(
|
||||||
|
const std::string& vendor,
|
||||||
|
int signature) {
|
||||||
|
int family = (signature >> 8) & 0xf;
|
||||||
|
int model = (signature >> 4) & 0xf;
|
||||||
|
int ext_family = 0;
|
||||||
|
int ext_model = 0;
|
||||||
|
|
||||||
|
// The "Intel 64 and IA-32 Architectures Developer's Manual: Vol. 2A"
|
||||||
|
// specifies the Extended Model is defined only when the Base Family is
|
||||||
|
// 06h or 0Fh.
|
||||||
|
// The "AMD CPUID Specification" specifies that the Extended Model is
|
||||||
|
// defined only when Base Family is 0Fh.
|
||||||
|
// Both manuals define the display model as
|
||||||
|
// {ExtendedModel[3:0],BaseModel[3:0]} in that case.
|
||||||
|
if (family == 0xf || (family == 0x6 && vendor == "GenuineIntel")) {
|
||||||
|
ext_model = (signature >> 16) & 0xf;
|
||||||
|
model += ext_model << 4;
|
||||||
|
}
|
||||||
|
// Both the "Intel 64 and IA-32 Architectures Developer's Manual: Vol. 2A"
|
||||||
|
// and the "AMD CPUID Specification" specify that the Extended Family is
|
||||||
|
// defined only when the Base Family is 0Fh.
|
||||||
|
// Both manuals define the display family as {0000b,BaseFamily[3:0]} +
|
||||||
|
// ExtendedFamily[7:0] in that case.
|
||||||
|
if (family == 0xf) {
|
||||||
|
ext_family = (signature >> 20) & 0xff;
|
||||||
|
family += ext_family;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {family, model, ext_family, ext_model};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
#endif // defined(ARCH_CPU_X86_FAMILY)
|
||||||
|
|
||||||
CPU::CPU()
|
CPU::CPU()
|
||||||
: signature_(0),
|
: signature_(0),
|
||||||
type_(0),
|
type_(0),
|
||||||
@ -48,6 +85,7 @@ CPU::CPU()
|
|||||||
has_avx2_(false),
|
has_avx2_(false),
|
||||||
has_aesni_(false),
|
has_aesni_(false),
|
||||||
has_non_stop_time_stamp_counter_(false),
|
has_non_stop_time_stamp_counter_(false),
|
||||||
|
is_running_in_vm_(false),
|
||||||
cpu_vendor_("unknown") {
|
cpu_vendor_("unknown") {
|
||||||
Initialize();
|
Initialize();
|
||||||
}
|
}
|
||||||
@ -156,7 +194,6 @@ void CPU::Initialize() {
|
|||||||
memcpy(cpu_string, &cpu_info[1], kVendorNameSize);
|
memcpy(cpu_string, &cpu_info[1], kVendorNameSize);
|
||||||
cpu_string[kVendorNameSize] = '\0';
|
cpu_string[kVendorNameSize] = '\0';
|
||||||
cpu_vendor_ = cpu_string;
|
cpu_vendor_ = cpu_string;
|
||||||
bool hypervisor = false;
|
|
||||||
|
|
||||||
// Interpret CPU feature information.
|
// Interpret CPU feature information.
|
||||||
if (num_ids > 0) {
|
if (num_ids > 0) {
|
||||||
@ -167,11 +204,9 @@ void CPU::Initialize() {
|
|||||||
}
|
}
|
||||||
signature_ = cpu_info[0];
|
signature_ = cpu_info[0];
|
||||||
stepping_ = cpu_info[0] & 0xf;
|
stepping_ = cpu_info[0] & 0xf;
|
||||||
model_ = ((cpu_info[0] >> 4) & 0xf) + ((cpu_info[0] >> 12) & 0xf0);
|
|
||||||
family_ = (cpu_info[0] >> 8) & 0xf;
|
|
||||||
type_ = (cpu_info[0] >> 12) & 0x3;
|
type_ = (cpu_info[0] >> 12) & 0x3;
|
||||||
ext_model_ = (cpu_info[0] >> 16) & 0xf;
|
std::tie(family_, model_, ext_family_, ext_model_) =
|
||||||
ext_family_ = (cpu_info[0] >> 20) & 0xff;
|
internal::ComputeX86FamilyAndModel(cpu_vendor_, signature_);
|
||||||
has_mmx_ = (cpu_info[3] & 0x00800000) != 0;
|
has_mmx_ = (cpu_info[3] & 0x00800000) != 0;
|
||||||
has_sse_ = (cpu_info[3] & 0x02000000) != 0;
|
has_sse_ = (cpu_info[3] & 0x02000000) != 0;
|
||||||
has_sse2_ = (cpu_info[3] & 0x04000000) != 0;
|
has_sse2_ = (cpu_info[3] & 0x04000000) != 0;
|
||||||
@ -186,7 +221,7 @@ void CPU::Initialize() {
|
|||||||
// This is checking for any hypervisor. Hypervisors may choose not to
|
// This is checking for any hypervisor. Hypervisors may choose not to
|
||||||
// announce themselves. Hypervisors trap CPUID and sometimes return
|
// announce themselves. Hypervisors trap CPUID and sometimes return
|
||||||
// different results to underlying hardware.
|
// different results to underlying hardware.
|
||||||
hypervisor = (cpu_info[2] & 0x80000000) != 0;
|
is_running_in_vm_ = (cpu_info[2] & 0x80000000) != 0;
|
||||||
|
|
||||||
// AVX instructions will generate an illegal instruction exception unless
|
// AVX instructions will generate an illegal instruction exception unless
|
||||||
// a) they are supported by the CPU,
|
// a) they are supported by the CPU,
|
||||||
@ -235,7 +270,7 @@ void CPU::Initialize() {
|
|||||||
has_non_stop_time_stamp_counter_ = (cpu_info[3] & (1 << 8)) != 0;
|
has_non_stop_time_stamp_counter_ = (cpu_info[3] & (1 << 8)) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!has_non_stop_time_stamp_counter_ && hypervisor) {
|
if (!has_non_stop_time_stamp_counter_ && is_running_in_vm_) {
|
||||||
int cpu_info_hv[4] = {};
|
int cpu_info_hv[4] = {};
|
||||||
__cpuid(cpu_info_hv, 0x40000000);
|
__cpuid(cpu_info_hv, 0x40000000);
|
||||||
if (cpu_info_hv[1] == 0x7263694D && // Micr
|
if (cpu_info_hv[1] == 0x7263694D && // Micr
|
||||||
@ -251,8 +286,14 @@ void CPU::Initialize() {
|
|||||||
has_non_stop_time_stamp_counter_ = true;
|
has_non_stop_time_stamp_counter_ = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#elif defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX))
|
#elif defined(ARCH_CPU_ARM_FAMILY)
|
||||||
|
#if (defined(OS_ANDROID) || defined(OS_LINUX))
|
||||||
cpu_brand_ = *CpuInfoBrand();
|
cpu_brand_ = *CpuInfoBrand();
|
||||||
|
#elif defined(OS_WIN)
|
||||||
|
// Windows makes high-resolution thread timing information available in
|
||||||
|
// user-space.
|
||||||
|
has_non_stop_time_stamp_counter_ = true;
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,11 +6,25 @@
|
|||||||
#define BASE_CPU_H_
|
#define BASE_CPU_H_
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
#include "base/base_export.h"
|
#include "base/base_export.h"
|
||||||
|
#include "build/build_config.h"
|
||||||
|
|
||||||
namespace base {
|
namespace base {
|
||||||
|
|
||||||
|
#if defined(ARCH_CPU_X86_FAMILY)
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// Compute the CPU family and model based on the vendor and CPUID signature.
|
||||||
|
// Returns in order: family, model, extended family, extended model.
|
||||||
|
BASE_EXPORT std::tuple<int, int, int, int> ComputeX86FamilyAndModel(
|
||||||
|
const std::string& vendor,
|
||||||
|
int signature);
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
#endif // defined(ARCH_CPU_X86_FAMILY)
|
||||||
|
|
||||||
// Query information about the processor.
|
// Query information about the processor.
|
||||||
class BASE_EXPORT CPU final {
|
class BASE_EXPORT CPU final {
|
||||||
public:
|
public:
|
||||||
@ -52,6 +66,7 @@ class BASE_EXPORT CPU final {
|
|||||||
bool has_non_stop_time_stamp_counter() const {
|
bool has_non_stop_time_stamp_counter() const {
|
||||||
return has_non_stop_time_stamp_counter_;
|
return has_non_stop_time_stamp_counter_;
|
||||||
}
|
}
|
||||||
|
bool is_running_in_vm() const { return is_running_in_vm_; }
|
||||||
|
|
||||||
IntelMicroArchitecture GetIntelMicroArchitecture() const;
|
IntelMicroArchitecture GetIntelMicroArchitecture() const;
|
||||||
const std::string& cpu_brand() const { return cpu_brand_; }
|
const std::string& cpu_brand() const { return cpu_brand_; }
|
||||||
@ -79,6 +94,7 @@ class BASE_EXPORT CPU final {
|
|||||||
bool has_avx2_;
|
bool has_avx2_;
|
||||||
bool has_aesni_;
|
bool has_aesni_;
|
||||||
bool has_non_stop_time_stamp_counter_;
|
bool has_non_stop_time_stamp_counter_;
|
||||||
|
bool is_running_in_vm_;
|
||||||
std::string cpu_vendor_;
|
std::string cpu_vendor_;
|
||||||
std::string cpu_brand_;
|
std::string cpu_brand_;
|
||||||
};
|
};
|
||||||
|
104
security/sandbox/chromium/base/debug/crash_logging.h
Normal file
104
security/sandbox/chromium/base/debug/crash_logging.h
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef BASE_DEBUG_CRASH_LOGGING_H_
|
||||||
|
#define BASE_DEBUG_CRASH_LOGGING_H_
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "base/base_export.h"
|
||||||
|
#include "base/macros.h"
|
||||||
|
#include "base/strings/string_piece.h"
|
||||||
|
|
||||||
|
namespace base {
|
||||||
|
namespace debug {
|
||||||
|
|
||||||
|
// A crash key is an annotation that is carried along with a crash report, to
|
||||||
|
// provide additional debugging information beyond a stack trace. Crash keys
|
||||||
|
// have a name and a string value.
|
||||||
|
//
|
||||||
|
// The preferred API is //components/crash/core/common:crash_key, however not
|
||||||
|
// all clients can hold a direct dependency on that target. The API provided
|
||||||
|
// in this file indirects the dependency.
|
||||||
|
//
|
||||||
|
// Example usage:
|
||||||
|
// static CrashKeyString* crash_key =
|
||||||
|
// AllocateCrashKeyString("name", CrashKeySize::Size32);
|
||||||
|
// SetCrashKeyString(crash_key, "value");
|
||||||
|
// ClearCrashKeyString(crash_key);
|
||||||
|
|
||||||
|
// The maximum length for a crash key's value must be one of the following
|
||||||
|
// pre-determined values.
|
||||||
|
enum class CrashKeySize {
|
||||||
|
Size32 = 32,
|
||||||
|
Size64 = 64,
|
||||||
|
Size256 = 256,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CrashKeyString;
|
||||||
|
|
||||||
|
// Allocates a new crash key with the specified |name| with storage for a
|
||||||
|
// value up to length |size|. This will return null if the crash key system is
|
||||||
|
// not initialized.
|
||||||
|
BASE_EXPORT CrashKeyString* AllocateCrashKeyString(const char name[],
|
||||||
|
CrashKeySize size);
|
||||||
|
|
||||||
|
// Stores |value| into the specified |crash_key|. The |crash_key| may be null
|
||||||
|
// if AllocateCrashKeyString() returned null. If |value| is longer than the
|
||||||
|
// size with which the key was allocated, it will be truncated.
|
||||||
|
BASE_EXPORT void SetCrashKeyString(CrashKeyString* crash_key,
|
||||||
|
base::StringPiece value);
|
||||||
|
|
||||||
|
// Clears any value that was stored in |crash_key|. The |crash_key| may be
|
||||||
|
// null.
|
||||||
|
BASE_EXPORT void ClearCrashKeyString(CrashKeyString* crash_key);
|
||||||
|
|
||||||
|
// A scoper that sets the specified key to value for the lifetime of the
|
||||||
|
// object, and clears it on destruction.
|
||||||
|
class BASE_EXPORT ScopedCrashKeyString {
|
||||||
|
public:
|
||||||
|
ScopedCrashKeyString(CrashKeyString* crash_key, base::StringPiece value);
|
||||||
|
~ScopedCrashKeyString();
|
||||||
|
|
||||||
|
private:
|
||||||
|
CrashKeyString* const crash_key_;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(ScopedCrashKeyString);
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// The following declarations are used to initialize the crash key system
|
||||||
|
// in //base by providing implementations for the above functions.
|
||||||
|
|
||||||
|
// The virtual interface that provides the implementation for the crash key
|
||||||
|
// API. This is implemented by a higher-layer component, and the instance is
|
||||||
|
// set using the function below.
|
||||||
|
class CrashKeyImplementation {
|
||||||
|
public:
|
||||||
|
virtual ~CrashKeyImplementation() = default;
|
||||||
|
|
||||||
|
virtual CrashKeyString* Allocate(const char name[], CrashKeySize size) = 0;
|
||||||
|
virtual void Set(CrashKeyString* crash_key, base::StringPiece value) = 0;
|
||||||
|
virtual void Clear(CrashKeyString* crash_key) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initializes the crash key system in base by replacing the existing
|
||||||
|
// implementation, if it exists, with |impl|. The |impl| is copied into base.
|
||||||
|
BASE_EXPORT void SetCrashKeyImplementation(
|
||||||
|
std::unique_ptr<CrashKeyImplementation> impl);
|
||||||
|
|
||||||
|
// The base structure for a crash key, storing the allocation metadata.
|
||||||
|
struct CrashKeyString {
|
||||||
|
constexpr CrashKeyString(const char name[], CrashKeySize size)
|
||||||
|
: name(name), size(size) {}
|
||||||
|
const char* const name;
|
||||||
|
const CrashKeySize size;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace debug
|
||||||
|
} // namespace base
|
||||||
|
|
||||||
|
#endif // BASE_DEBUG_CRASH_LOGGING_H_
|
@ -38,6 +38,12 @@ BASE_EXPORT void BreakDebugger();
|
|||||||
BASE_EXPORT void SetSuppressDebugUI(bool suppress);
|
BASE_EXPORT void SetSuppressDebugUI(bool suppress);
|
||||||
BASE_EXPORT bool IsDebugUISuppressed();
|
BASE_EXPORT bool IsDebugUISuppressed();
|
||||||
|
|
||||||
|
// If a debugger is present, verifies that it is properly set up, and DCHECK()s
|
||||||
|
// if misconfigured. Currently only verifies that //tools/gdb/gdbinit has been
|
||||||
|
// sourced when using gdb on Linux and //tools/lldb/lldbinit.py has been sourced
|
||||||
|
// when using lldb on macOS.
|
||||||
|
BASE_EXPORT void VerifyDebugger();
|
||||||
|
|
||||||
} // namespace debug
|
} // namespace debug
|
||||||
} // namespace base
|
} // namespace base
|
||||||
|
|
||||||
|
@ -19,29 +19,23 @@
|
|||||||
#endif // defined(OS_WIN)
|
#endif // defined(OS_WIN)
|
||||||
|
|
||||||
// TODO(peria): Enable profiling on Windows.
|
// TODO(peria): Enable profiling on Windows.
|
||||||
#if BUILDFLAG(ENABLE_PROFILING) && !defined(NO_TCMALLOC) && !defined(OS_WIN)
|
#if BUILDFLAG(ENABLE_PROFILING) && BUILDFLAG(USE_TCMALLOC) && !defined(OS_WIN)
|
||||||
|
|
||||||
#if BUILDFLAG(USE_NEW_TCMALLOC)
|
|
||||||
#include "third_party/tcmalloc/chromium/src/gperftools/profiler.h"
|
#include "third_party/tcmalloc/chromium/src/gperftools/profiler.h"
|
||||||
#else
|
|
||||||
#include "third_party/tcmalloc/gperftools-2.0/chromium/src/gperftools/profiler.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace base {
|
namespace base {
|
||||||
namespace debug {
|
namespace debug {
|
||||||
|
|
||||||
// TODO(peria): Enable profiling on Windows.
|
// TODO(peria): Enable profiling on Windows.
|
||||||
#if BUILDFLAG(ENABLE_PROFILING) && !defined(NO_TCMALLOC) && !defined(OS_WIN)
|
#if BUILDFLAG(ENABLE_PROFILING) && BUILDFLAG(USE_TCMALLOC) && !defined(OS_WIN)
|
||||||
|
|
||||||
static int profile_count = 0;
|
static int profile_count = 0;
|
||||||
|
|
||||||
void StartProfiling(const std::string& name) {
|
void StartProfiling(const std::string& name) {
|
||||||
++profile_count;
|
++profile_count;
|
||||||
std::string full_name(name);
|
std::string full_name(name);
|
||||||
std::string pid = IntToString(GetCurrentProcId());
|
std::string pid = NumberToString(GetCurrentProcId());
|
||||||
std::string count = IntToString(profile_count);
|
std::string count = NumberToString(profile_count);
|
||||||
ReplaceSubstringsAfterOffset(&full_name, 0, "{pid}", pid);
|
ReplaceSubstringsAfterOffset(&full_name, 0, "{pid}", pid);
|
||||||
ReplaceSubstringsAfterOffset(&full_name, 0, "{count}", count);
|
ReplaceSubstringsAfterOffset(&full_name, 0, "{count}", count);
|
||||||
ProfilerStart(full_name.c_str());
|
ProfilerStart(full_name.c_str());
|
||||||
@ -158,7 +152,7 @@ FunctionType FindFunctionInImports(const char* function_name) {
|
|||||||
base::win::PEImage image(CURRENT_MODULE());
|
base::win::PEImage image(CURRENT_MODULE());
|
||||||
|
|
||||||
FunctionSearchContext ctx = { function_name, NULL };
|
FunctionSearchContext ctx = { function_name, NULL };
|
||||||
image.EnumImportChunks(FindResolutionFunctionInImports, &ctx);
|
image.EnumImportChunks(FindResolutionFunctionInImports, &ctx, nullptr);
|
||||||
|
|
||||||
return reinterpret_cast<FunctionType>(ctx.function);
|
return reinterpret_cast<FunctionType>(ctx.function);
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,6 @@
|
|||||||
|
|
||||||
#include "base/environment.h"
|
#include "base/environment.h"
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "base/memory/ptr_util.h"
|
#include "base/memory/ptr_util.h"
|
||||||
#include "base/strings/string_piece.h"
|
#include "base/strings/string_piece.h"
|
||||||
#include "base/strings/string_util.h"
|
#include "base/strings/string_util.h"
|
||||||
@ -101,23 +97,6 @@ class EnvironmentImpl : public Environment {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Parses a null-terminated input string of an environment block. The key is
|
|
||||||
// placed into the given string, and the total length of the line, including
|
|
||||||
// the terminating null, is returned.
|
|
||||||
size_t ParseEnvLine(const NativeEnvironmentString::value_type* input,
|
|
||||||
NativeEnvironmentString* key) {
|
|
||||||
// Skip to the equals or end of the string, this is the key.
|
|
||||||
size_t cur = 0;
|
|
||||||
while (input[cur] && input[cur] != '=')
|
|
||||||
cur++;
|
|
||||||
*key = NativeEnvironmentString(&input[0], cur);
|
|
||||||
|
|
||||||
// Now just skip to the end of the string.
|
|
||||||
while (input[cur])
|
|
||||||
cur++;
|
|
||||||
return cur + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace env_vars {
|
namespace env_vars {
|
||||||
@ -141,97 +120,4 @@ bool Environment::HasVar(StringPiece variable_name) {
|
|||||||
return GetVar(variable_name, nullptr);
|
return GetVar(variable_name, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(OS_WIN)
|
|
||||||
|
|
||||||
string16 AlterEnvironment(const wchar_t* env,
|
|
||||||
const EnvironmentMap& changes) {
|
|
||||||
string16 result;
|
|
||||||
|
|
||||||
// First copy all unmodified values to the output.
|
|
||||||
size_t cur_env = 0;
|
|
||||||
string16 key;
|
|
||||||
while (env[cur_env]) {
|
|
||||||
const wchar_t* line = &env[cur_env];
|
|
||||||
size_t line_length = ParseEnvLine(line, &key);
|
|
||||||
|
|
||||||
// Keep only values not specified in the change vector.
|
|
||||||
EnvironmentMap::const_iterator found_change = changes.find(key);
|
|
||||||
if (found_change == changes.end())
|
|
||||||
result.append(line, line_length);
|
|
||||||
|
|
||||||
cur_env += line_length;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now append all modified and new values.
|
|
||||||
for (EnvironmentMap::const_iterator i = changes.begin();
|
|
||||||
i != changes.end(); ++i) {
|
|
||||||
if (!i->second.empty()) {
|
|
||||||
result.append(i->first);
|
|
||||||
result.push_back('=');
|
|
||||||
result.append(i->second);
|
|
||||||
result.push_back(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// An additional null marks the end of the list. We always need a double-null
|
|
||||||
// in case nothing was added above.
|
|
||||||
if (result.empty())
|
|
||||||
result.push_back(0);
|
|
||||||
result.push_back(0);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
|
|
||||||
|
|
||||||
std::unique_ptr<char* []> AlterEnvironment(const char* const* const env,
|
|
||||||
const EnvironmentMap& changes) {
|
|
||||||
std::string value_storage; // Holds concatenated null-terminated strings.
|
|
||||||
std::vector<size_t> result_indices; // Line indices into value_storage.
|
|
||||||
|
|
||||||
// First build up all of the unchanged environment strings. These are
|
|
||||||
// null-terminated of the form "key=value".
|
|
||||||
std::string key;
|
|
||||||
for (size_t i = 0; env[i]; i++) {
|
|
||||||
size_t line_length = ParseEnvLine(env[i], &key);
|
|
||||||
|
|
||||||
// Keep only values not specified in the change vector.
|
|
||||||
auto found_change = changes.find(key);
|
|
||||||
if (found_change == changes.end()) {
|
|
||||||
result_indices.push_back(value_storage.size());
|
|
||||||
value_storage.append(env[i], line_length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now append all modified and new values.
|
|
||||||
for (const auto& i : changes) {
|
|
||||||
if (!i.second.empty()) {
|
|
||||||
result_indices.push_back(value_storage.size());
|
|
||||||
value_storage.append(i.first);
|
|
||||||
value_storage.push_back('=');
|
|
||||||
value_storage.append(i.second);
|
|
||||||
value_storage.push_back(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t pointer_count_required =
|
|
||||||
result_indices.size() + 1 + // Null-terminated array of pointers.
|
|
||||||
(value_storage.size() + sizeof(char*) - 1) / sizeof(char*); // Buffer.
|
|
||||||
std::unique_ptr<char* []> result(new char*[pointer_count_required]);
|
|
||||||
|
|
||||||
// The string storage goes after the array of pointers.
|
|
||||||
char* storage_data = reinterpret_cast<char*>(
|
|
||||||
&result.get()[result_indices.size() + 1]);
|
|
||||||
if (!value_storage.empty())
|
|
||||||
memcpy(storage_data, value_storage.data(), value_storage.size());
|
|
||||||
|
|
||||||
// Fill array of pointers at the beginning of the result.
|
|
||||||
for (size_t i = 0; i < result_indices.size(); i++)
|
|
||||||
result[i] = &storage_data[result_indices[i]];
|
|
||||||
result[result_indices.size()] = 0; // Null terminator.
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // OS_POSIX || OS_FUCHSIA
|
|
||||||
|
|
||||||
} // namespace base
|
} // namespace base
|
||||||
|
@ -38,52 +38,23 @@ class BASE_EXPORT Environment {
|
|||||||
// Syntactic sugar for GetVar(variable_name, nullptr);
|
// Syntactic sugar for GetVar(variable_name, nullptr);
|
||||||
virtual bool HasVar(StringPiece variable_name);
|
virtual bool HasVar(StringPiece variable_name);
|
||||||
|
|
||||||
// Returns true on success, otherwise returns false.
|
// Returns true on success, otherwise returns false. This method should not
|
||||||
|
// be called in a multi-threaded process.
|
||||||
virtual bool SetVar(StringPiece variable_name,
|
virtual bool SetVar(StringPiece variable_name,
|
||||||
const std::string& new_value) = 0;
|
const std::string& new_value) = 0;
|
||||||
|
|
||||||
// Returns true on success, otherwise returns false.
|
// Returns true on success, otherwise returns false. This method should not
|
||||||
|
// be called in a multi-threaded process.
|
||||||
virtual bool UnSetVar(StringPiece variable_name) = 0;
|
virtual bool UnSetVar(StringPiece variable_name) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#if defined(OS_WIN)
|
#if defined(OS_WIN)
|
||||||
|
using NativeEnvironmentString = std::wstring;
|
||||||
typedef string16 NativeEnvironmentString;
|
|
||||||
typedef std::map<NativeEnvironmentString, NativeEnvironmentString>
|
|
||||||
EnvironmentMap;
|
|
||||||
|
|
||||||
// Returns a modified environment vector constructed from the given environment
|
|
||||||
// and the list of changes given in |changes|. Each key in the environment is
|
|
||||||
// matched against the first element of the pairs. In the event of a match, the
|
|
||||||
// value is replaced by the second of the pair, unless the second is empty, in
|
|
||||||
// which case the key-value is removed.
|
|
||||||
//
|
|
||||||
// This Windows version takes and returns a Windows-style environment block
|
|
||||||
// which is a concatenated list of null-terminated 16-bit strings. The end is
|
|
||||||
// marked by a double-null terminator. The size of the returned string will
|
|
||||||
// include the terminators.
|
|
||||||
BASE_EXPORT string16 AlterEnvironment(const wchar_t* env,
|
|
||||||
const EnvironmentMap& changes);
|
|
||||||
|
|
||||||
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
|
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||||
|
using NativeEnvironmentString = std::string;
|
||||||
typedef std::string NativeEnvironmentString;
|
|
||||||
typedef std::map<NativeEnvironmentString, NativeEnvironmentString>
|
|
||||||
EnvironmentMap;
|
|
||||||
|
|
||||||
// See general comments for the Windows version above.
|
|
||||||
//
|
|
||||||
// This Posix version takes and returns a Posix-style environment block, which
|
|
||||||
// is a null-terminated list of pointers to null-terminated strings. The
|
|
||||||
// returned array will have appended to it the storage for the array itself so
|
|
||||||
// there is only one pointer to manage, but this means that you can't copy the
|
|
||||||
// array without keeping the original around.
|
|
||||||
BASE_EXPORT std::unique_ptr<char* []> AlterEnvironment(
|
|
||||||
const char* const* env,
|
|
||||||
const EnvironmentMap& changes);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
using EnvironmentMap =
|
||||||
|
std::map<NativeEnvironmentString, NativeEnvironmentString>;
|
||||||
|
|
||||||
} // namespace base
|
} // namespace base
|
||||||
|
|
||||||
|
@ -104,6 +104,7 @@
|
|||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
#include <iosfwd>
|
#include <iosfwd>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -152,7 +153,7 @@ class BASE_EXPORT FilePath {
|
|||||||
#if defined(OS_WIN)
|
#if defined(OS_WIN)
|
||||||
// On Windows, for Unicode-aware applications, native pathnames are wchar_t
|
// On Windows, for Unicode-aware applications, native pathnames are wchar_t
|
||||||
// arrays encoded in UTF-16.
|
// arrays encoded in UTF-16.
|
||||||
typedef base::string16 StringType;
|
typedef std::wstring StringType;
|
||||||
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
|
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||||
// On most platforms, native pathnames are char arrays, and the encoding
|
// On most platforms, native pathnames are char arrays, and the encoding
|
||||||
// may or may not be specified. On Mac OS X, native pathnames are encoded
|
// may or may not be specified. On Mac OS X, native pathnames are encoded
|
||||||
|
25
security/sandbox/chromium/base/files/file_path_constants.cc
Normal file
25
security/sandbox/chromium/base/files/file_path_constants.cc
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// Copyright 2013 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include "base/files/file_path.h"
|
||||||
|
#include "base/stl_util.h"
|
||||||
|
|
||||||
|
namespace base {
|
||||||
|
|
||||||
|
#if defined(FILE_PATH_USES_WIN_SEPARATORS)
|
||||||
|
const FilePath::CharType FilePath::kSeparators[] = FILE_PATH_LITERAL("\\/");
|
||||||
|
#else // FILE_PATH_USES_WIN_SEPARATORS
|
||||||
|
const FilePath::CharType FilePath::kSeparators[] = FILE_PATH_LITERAL("/");
|
||||||
|
#endif // FILE_PATH_USES_WIN_SEPARATORS
|
||||||
|
|
||||||
|
const size_t FilePath::kSeparatorsLength = base::size(kSeparators);
|
||||||
|
|
||||||
|
const FilePath::CharType FilePath::kCurrentDirectory[] = FILE_PATH_LITERAL(".");
|
||||||
|
const FilePath::CharType FilePath::kParentDirectory[] = FILE_PATH_LITERAL("..");
|
||||||
|
|
||||||
|
const FilePath::CharType FilePath::kExtensionSeparator = FILE_PATH_LITERAL('.');
|
||||||
|
|
||||||
|
} // namespace base
|
@ -2,7 +2,11 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
#include "base/hash.h"
|
#include "base/hash/hash.h"
|
||||||
|
|
||||||
|
#include "base/rand_util.h"
|
||||||
|
#include "base/third_party/cityhash/city.h"
|
||||||
|
#include "build/build_config.h"
|
||||||
|
|
||||||
// Definition in base/third_party/superfasthash/superfasthash.c. (Third-party
|
// Definition in base/third_party/superfasthash/superfasthash.c. (Third-party
|
||||||
// code did not come with its own header file, so declaring the function here.)
|
// code did not come with its own header file, so declaring the function here.)
|
||||||
@ -11,34 +15,18 @@ extern "C" uint32_t SuperFastHash(const char* data, int len);
|
|||||||
|
|
||||||
namespace base {
|
namespace base {
|
||||||
|
|
||||||
uint32_t Hash(const void* data, size_t length) {
|
namespace {
|
||||||
// Currently our in-memory hash is the same as the persistent hash. The
|
|
||||||
// split between in-memory and persistent hash functions is maintained to
|
|
||||||
// allow the in-memory hash function to be updated in the future.
|
|
||||||
return PersistentHash(data, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t Hash(const std::string& str) {
|
size_t FastHashImpl(base::span<const uint8_t> data) {
|
||||||
return PersistentHash(str.data(), str.size());
|
// We use the updated CityHash within our namespace (not the deprecated
|
||||||
}
|
// version from third_party/smhasher).
|
||||||
|
#if defined(ARCH_CPU_64_BITS)
|
||||||
uint32_t Hash(const string16& str) {
|
return base::internal::cityhash_v111::CityHash64(
|
||||||
return PersistentHash(str.data(), str.size() * sizeof(char16));
|
reinterpret_cast<const char*>(data.data()), data.size());
|
||||||
}
|
#else
|
||||||
|
return base::internal::cityhash_v111::CityHash32(
|
||||||
uint32_t PersistentHash(const void* data, size_t length) {
|
reinterpret_cast<const char*>(data.data()), data.size());
|
||||||
// This hash function must not change, since it is designed to be persistable
|
#endif
|
||||||
// to disk.
|
|
||||||
if (length > static_cast<size_t>(std::numeric_limits<int>::max())) {
|
|
||||||
NOTREACHED();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return ::SuperFastHash(reinterpret_cast<const char*>(data),
|
|
||||||
static_cast<int>(length));
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t PersistentHash(const std::string& str) {
|
|
||||||
return PersistentHash(str.data(), str.size());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implement hashing for pairs of at-most 32 bit integer values.
|
// Implement hashing for pairs of at-most 32 bit integer values.
|
||||||
@ -50,7 +38,7 @@ uint32_t PersistentHash(const std::string& str) {
|
|||||||
// h32(x32, y32) = (h64(x32, y32) * rand_odd64 + rand16 * 2^16) % 2^64 / 2^32
|
// h32(x32, y32) = (h64(x32, y32) * rand_odd64 + rand16 * 2^16) % 2^64 / 2^32
|
||||||
//
|
//
|
||||||
// Contact danakj@chromium.org for any questions.
|
// Contact danakj@chromium.org for any questions.
|
||||||
size_t HashInts32(uint32_t value1, uint32_t value2) {
|
size_t HashInts32Impl(uint32_t value1, uint32_t value2) {
|
||||||
uint64_t value1_64 = value1;
|
uint64_t value1_64 = value1;
|
||||||
uint64_t hash64 = (value1_64 << 32) | value2;
|
uint64_t hash64 = (value1_64 << 32) | value2;
|
||||||
|
|
||||||
@ -71,7 +59,7 @@ size_t HashInts32(uint32_t value1, uint32_t value2) {
|
|||||||
// breaking the two 64-bit inputs into 4 32-bit values:
|
// breaking the two 64-bit inputs into 4 32-bit values:
|
||||||
// http://opendatastructures.org/versions/edition-0.1d/ods-java/node33.html#SECTION00832000000000000000
|
// http://opendatastructures.org/versions/edition-0.1d/ods-java/node33.html#SECTION00832000000000000000
|
||||||
// Then we reduce our result to 32 bits if required, similar to above.
|
// Then we reduce our result to 32 bits if required, similar to above.
|
||||||
size_t HashInts64(uint64_t value1, uint64_t value2) {
|
size_t HashInts64Impl(uint64_t value1, uint64_t value2) {
|
||||||
uint32_t short_random1 = 842304669U;
|
uint32_t short_random1 = 842304669U;
|
||||||
uint32_t short_random2 = 619063811U;
|
uint32_t short_random2 = 619063811U;
|
||||||
uint32_t short_random3 = 937041849U;
|
uint32_t short_random3 = 937041849U;
|
||||||
@ -101,4 +89,79 @@ size_t HashInts64(uint64_t value1, uint64_t value2) {
|
|||||||
return high_bits;
|
return high_bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The random seed is used to perturb the output of base::FastHash() and
|
||||||
|
// base::HashInts() so that it is only deterministic within the lifetime of a
|
||||||
|
// process. This prevents inadvertent dependencies on the underlying
|
||||||
|
// implementation, e.g. anything that persists the hash value and expects it to
|
||||||
|
// be unchanging will break.
|
||||||
|
//
|
||||||
|
// Note: this is the same trick absl uses to generate a random seed. This is
|
||||||
|
// more robust than using base::RandBytes(), which can fail inside a sandboxed
|
||||||
|
// environment. Note that without ASLR, the seed won't be quite as random...
|
||||||
|
#if DCHECK_IS_ON()
|
||||||
|
constexpr const void* kSeed = &kSeed;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T Scramble(T input) {
|
||||||
|
#if DCHECK_IS_ON()
|
||||||
|
return HashInts64Impl(input, reinterpret_cast<uintptr_t>(kSeed));
|
||||||
|
#else
|
||||||
|
return input;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
size_t FastHash(base::span<const uint8_t> data) {
|
||||||
|
return Scramble(FastHashImpl(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Hash(const void* data, size_t length) {
|
||||||
|
// Currently our in-memory hash is the same as the persistent hash. The
|
||||||
|
// split between in-memory and persistent hash functions is maintained to
|
||||||
|
// allow the in-memory hash function to be updated in the future.
|
||||||
|
return PersistentHash(data, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Hash(const std::string& str) {
|
||||||
|
return PersistentHash(as_bytes(make_span(str)));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Hash(const string16& str) {
|
||||||
|
return PersistentHash(as_bytes(make_span(str)));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t PersistentHash(span<const uint8_t> data) {
|
||||||
|
// This hash function must not change, since it is designed to be persistable
|
||||||
|
// to disk.
|
||||||
|
if (data.size() > static_cast<size_t>(std::numeric_limits<int>::max())) {
|
||||||
|
NOTREACHED();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return ::SuperFastHash(reinterpret_cast<const char*>(data.data()),
|
||||||
|
static_cast<int>(data.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t PersistentHash(const void* data, size_t length) {
|
||||||
|
return PersistentHash(make_span(static_cast<const uint8_t*>(data), length));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t PersistentHash(const std::string& str) {
|
||||||
|
return PersistentHash(str.data(), str.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t HashInts32(uint32_t value1, uint32_t value2) {
|
||||||
|
return Scramble(HashInts32Impl(value1, value2));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implement hashing for pairs of up-to 64-bit integer values.
|
||||||
|
// We use the compound integer hash method to produce a 64-bit hash code, by
|
||||||
|
// breaking the two 64-bit inputs into 4 32-bit values:
|
||||||
|
// http://opendatastructures.org/versions/edition-0.1d/ods-java/node33.html#SECTION00832000000000000000
|
||||||
|
// Then we reduce our result to 32 bits if required, similar to above.
|
||||||
|
size_t HashInts64(uint64_t value1, uint64_t value2) {
|
||||||
|
return Scramble(HashInts64Impl(value1, value2));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace base
|
} // namespace base
|
@ -2,8 +2,8 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
#ifndef BASE_HASH_H_
|
#ifndef BASE_HASH_HASH_H_
|
||||||
#define BASE_HASH_H_
|
#define BASE_HASH_HASH_H_
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@ -13,26 +13,42 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "base/base_export.h"
|
#include "base/base_export.h"
|
||||||
|
#include "base/containers/span.h"
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
#include "base/strings/string16.h"
|
#include "base/strings/string16.h"
|
||||||
|
#include "base/strings/string_piece.h"
|
||||||
|
|
||||||
namespace base {
|
namespace base {
|
||||||
|
|
||||||
// Computes a hash of a memory buffer. This hash function is subject to change
|
// WARNING: This hash functions should not be used for any cryptographic
|
||||||
// in the future, so use only for temporary in-memory structures. If you need
|
// purpose.
|
||||||
// to persist a change on disk or between computers, use PersistentHash().
|
|
||||||
//
|
// Deprecated: Computes a hash of a memory buffer, use FastHash() instead.
|
||||||
// WARNING: This hash function should not be used for any cryptographic purpose.
|
// If you need to persist a change on disk or between computers, use
|
||||||
|
// PersistentHash().
|
||||||
|
// TODO(https://crbug.com/1025358): Migrate client code to new hash function.
|
||||||
BASE_EXPORT uint32_t Hash(const void* data, size_t length);
|
BASE_EXPORT uint32_t Hash(const void* data, size_t length);
|
||||||
BASE_EXPORT uint32_t Hash(const std::string& str);
|
BASE_EXPORT uint32_t Hash(const std::string& str);
|
||||||
BASE_EXPORT uint32_t Hash(const string16& str);
|
BASE_EXPORT uint32_t Hash(const string16& str);
|
||||||
|
|
||||||
|
// Really *fast* and high quality hash.
|
||||||
|
// Recommended hash function for general use, we pick the best performant
|
||||||
|
// hash for each build target.
|
||||||
|
// It is prone to be updated whenever a newer/faster hash function is
|
||||||
|
// publicly available.
|
||||||
|
// May changed without warning, do not expect stability of outputs.
|
||||||
|
BASE_EXPORT size_t FastHash(base::span<const uint8_t> data);
|
||||||
|
inline size_t FastHash(StringPiece str) {
|
||||||
|
return FastHash(as_bytes(make_span(str)));
|
||||||
|
}
|
||||||
|
|
||||||
// Computes a hash of a memory buffer. This hash function must not change so
|
// Computes a hash of a memory buffer. This hash function must not change so
|
||||||
// that code can use the hashed values for persistent storage purposes or
|
// that code can use the hashed values for persistent storage purposes or
|
||||||
// sending across the network. If a new persistent hash function is desired, a
|
// sending across the network. If a new persistent hash function is desired, a
|
||||||
// new version will have to be added in addition.
|
// new version will have to be added in addition.
|
||||||
//
|
//
|
||||||
// WARNING: This hash function should not be used for any cryptographic purpose.
|
// WARNING: This hash function should not be used for any cryptographic purpose.
|
||||||
|
BASE_EXPORT uint32_t PersistentHash(base::span<const uint8_t> data);
|
||||||
BASE_EXPORT uint32_t PersistentHash(const void* data, size_t length);
|
BASE_EXPORT uint32_t PersistentHash(const void* data, size_t length);
|
||||||
BASE_EXPORT uint32_t PersistentHash(const std::string& str);
|
BASE_EXPORT uint32_t PersistentHash(const std::string& str);
|
||||||
|
|
||||||
@ -67,4 +83,4 @@ struct IntPairHash<std::pair<Type1, Type2>> {
|
|||||||
|
|
||||||
} // namespace base
|
} // namespace base
|
||||||
|
|
||||||
#endif // BASE_HASH_H_
|
#endif // BASE_HASH_HASH_H_
|
168
security/sandbox/chromium/base/immediate_crash.h
Normal file
168
security/sandbox/chromium/base/immediate_crash.h
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
// Copyright 2019 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef BASE_IMMEDIATE_CRASH_H_
|
||||||
|
#define BASE_IMMEDIATE_CRASH_H_
|
||||||
|
|
||||||
|
#include "build/build_config.h"
|
||||||
|
|
||||||
|
// Crashes in the fastest possible way with no attempt at logging.
|
||||||
|
// There are several constraints; see http://crbug.com/664209 for more context.
|
||||||
|
//
|
||||||
|
// - TRAP_SEQUENCE_() must be fatal. It should not be possible to ignore the
|
||||||
|
// resulting exception or simply hit 'continue' to skip over it in a debugger.
|
||||||
|
// - Different instances of TRAP_SEQUENCE_() must not be folded together, to
|
||||||
|
// ensure crash reports are debuggable. Unlike __builtin_trap(), asm volatile
|
||||||
|
// blocks will not be folded together.
|
||||||
|
// Note: TRAP_SEQUENCE_() previously required an instruction with a unique
|
||||||
|
// nonce since unlike clang, GCC folds together identical asm volatile
|
||||||
|
// blocks.
|
||||||
|
// - TRAP_SEQUENCE_() must produce a signal that is distinct from an invalid
|
||||||
|
// memory access.
|
||||||
|
// - TRAP_SEQUENCE_() must be treated as a set of noreturn instructions.
|
||||||
|
// __builtin_unreachable() is used to provide that hint here. clang also uses
|
||||||
|
// this as a heuristic to pack the instructions in the function epilogue to
|
||||||
|
// improve code density.
|
||||||
|
//
|
||||||
|
// Additional properties that are nice to have:
|
||||||
|
// - TRAP_SEQUENCE_() should be as compact as possible.
|
||||||
|
// - The first instruction of TRAP_SEQUENCE_() should not change, to avoid
|
||||||
|
// shifting crash reporting clusters. As a consequence of this, explicit
|
||||||
|
// assembly is preferred over intrinsics.
|
||||||
|
// Note: this last bullet point may no longer be true, and may be removed in
|
||||||
|
// the future.
|
||||||
|
|
||||||
|
// Note: TRAP_SEQUENCE Is currently split into two macro helpers due to the fact
|
||||||
|
// that clang emits an actual instruction for __builtin_unreachable() on certain
|
||||||
|
// platforms (see https://crbug.com/958675). In addition, the int3/bkpt/brk will
|
||||||
|
// be removed in followups, so splitting it up like this now makes it easy to
|
||||||
|
// land the followups.
|
||||||
|
|
||||||
|
#if defined(COMPILER_GCC)
|
||||||
|
|
||||||
|
#if defined(OS_NACL)
|
||||||
|
|
||||||
|
// Crash report accuracy is not guaranteed on NaCl.
|
||||||
|
#define TRAP_SEQUENCE1_() __builtin_trap()
|
||||||
|
#define TRAP_SEQUENCE2_() asm volatile("")
|
||||||
|
|
||||||
|
#elif defined(ARCH_CPU_X86_FAMILY)
|
||||||
|
|
||||||
|
// TODO(https://crbug.com/958675): In theory, it should be possible to use just
|
||||||
|
// int3. However, there are a number of crashes with SIGILL as the exception
|
||||||
|
// code, so it seems likely that there's a signal handler that allows execution
|
||||||
|
// to continue after SIGTRAP.
|
||||||
|
#define TRAP_SEQUENCE1_() asm volatile("int3")
|
||||||
|
|
||||||
|
#if defined(OS_MACOSX)
|
||||||
|
// Intentionally empty: __builtin_unreachable() is always part of the sequence
|
||||||
|
// (see IMMEDIATE_CRASH below) and already emits a ud2 on Mac.
|
||||||
|
#define TRAP_SEQUENCE2_() asm volatile("")
|
||||||
|
#else
|
||||||
|
#define TRAP_SEQUENCE2_() asm volatile("ud2")
|
||||||
|
#endif // defined(OS_MACOSX)
|
||||||
|
|
||||||
|
#elif defined(ARCH_CPU_ARMEL)
|
||||||
|
|
||||||
|
// bkpt will generate a SIGBUS when running on armv7 and a SIGTRAP when running
|
||||||
|
// as a 32 bit userspace app on arm64. There doesn't seem to be any way to
|
||||||
|
// cause a SIGTRAP from userspace without using a syscall (which would be a
|
||||||
|
// problem for sandboxing).
|
||||||
|
// TODO(https://crbug.com/958675): Remove bkpt from this sequence.
|
||||||
|
#define TRAP_SEQUENCE1_() asm volatile("bkpt #0")
|
||||||
|
#define TRAP_SEQUENCE2_() asm volatile("udf #0")
|
||||||
|
|
||||||
|
#elif defined(ARCH_CPU_ARM64)
|
||||||
|
|
||||||
|
// This will always generate a SIGTRAP on arm64.
|
||||||
|
// TODO(https://crbug.com/958675): Remove brk from this sequence.
|
||||||
|
#define TRAP_SEQUENCE1_() asm volatile("brk #0")
|
||||||
|
#define TRAP_SEQUENCE2_() asm volatile("hlt #0")
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
// Crash report accuracy will not be guaranteed on other architectures, but at
|
||||||
|
// least this will crash as expected.
|
||||||
|
#define TRAP_SEQUENCE1_() __builtin_trap()
|
||||||
|
#define TRAP_SEQUENCE2_() asm volatile("")
|
||||||
|
|
||||||
|
#endif // ARCH_CPU_*
|
||||||
|
|
||||||
|
#elif defined(COMPILER_MSVC)
|
||||||
|
|
||||||
|
#if !defined(__clang__)
|
||||||
|
|
||||||
|
// MSVC x64 doesn't support inline asm, so use the MSVC intrinsic.
|
||||||
|
#define TRAP_SEQUENCE1_() __debugbreak()
|
||||||
|
#define TRAP_SEQUENCE2_()
|
||||||
|
|
||||||
|
#elif defined(ARCH_CPU_ARM64)
|
||||||
|
|
||||||
|
// Windows ARM64 uses "BRK #F000" as its breakpoint instruction, and
|
||||||
|
// __debugbreak() generates that in both VC++ and clang.
|
||||||
|
#define TRAP_SEQUENCE1_() __debugbreak()
|
||||||
|
// Intentionally empty: __builtin_unreachable() is always part of the sequence
|
||||||
|
// (see IMMEDIATE_CRASH below) and already emits a ud2 on Win64,
|
||||||
|
// https://crbug.com/958373
|
||||||
|
#define TRAP_SEQUENCE2_() __asm volatile("")
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define TRAP_SEQUENCE1_() asm volatile("int3")
|
||||||
|
#define TRAP_SEQUENCE2_() asm volatile("ud2")
|
||||||
|
|
||||||
|
#endif // __clang__
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#error No supported trap sequence!
|
||||||
|
|
||||||
|
#endif // COMPILER_GCC
|
||||||
|
|
||||||
|
#define TRAP_SEQUENCE_() \
|
||||||
|
do { \
|
||||||
|
TRAP_SEQUENCE1_(); \
|
||||||
|
TRAP_SEQUENCE2_(); \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
|
// CHECK() and the trap sequence can be invoked from a constexpr function.
|
||||||
|
// This could make compilation fail on GCC, as it forbids directly using inline
|
||||||
|
// asm inside a constexpr function. However, it allows calling a lambda
|
||||||
|
// expression including the same asm.
|
||||||
|
// The side effect is that the top of the stacktrace will not point to the
|
||||||
|
// calling function, but to this anonymous lambda. This is still useful as the
|
||||||
|
// full name of the lambda will typically include the name of the function that
|
||||||
|
// calls CHECK() and the debugger will still break at the right line of code.
|
||||||
|
#if !defined(COMPILER_GCC)
|
||||||
|
|
||||||
|
#define WRAPPED_TRAP_SEQUENCE_() TRAP_SEQUENCE_()
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define WRAPPED_TRAP_SEQUENCE_() \
|
||||||
|
do { \
|
||||||
|
[] { TRAP_SEQUENCE_(); }(); \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
|
#endif // !defined(COMPILER_GCC)
|
||||||
|
|
||||||
|
#if defined(__clang__) || defined(COMPILER_GCC)
|
||||||
|
|
||||||
|
// __builtin_unreachable() hints to the compiler that this is noreturn and can
|
||||||
|
// be packed in the function epilogue.
|
||||||
|
#define IMMEDIATE_CRASH() \
|
||||||
|
({ \
|
||||||
|
WRAPPED_TRAP_SEQUENCE_(); \
|
||||||
|
__builtin_unreachable(); \
|
||||||
|
})
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
// This is supporting non-chromium user of logging.h to build with MSVC, like
|
||||||
|
// pdfium. On MSVC there is no __builtin_unreachable().
|
||||||
|
#define IMMEDIATE_CRASH() WRAPPED_TRAP_SEQUENCE_()
|
||||||
|
|
||||||
|
#endif // defined(__clang__) || defined(COMPILER_GCC)
|
||||||
|
|
||||||
|
#endif // BASE_IMMEDIATE_CRASH_H_
|
@ -43,7 +43,7 @@ Location::Location(const char* function_name,
|
|||||||
std::string Location::ToString() const {
|
std::string Location::ToString() const {
|
||||||
if (has_source_info()) {
|
if (has_source_info()) {
|
||||||
return std::string(function_name_) + "@" + file_name_ + ":" +
|
return std::string(function_name_) + "@" + file_name_ + ":" +
|
||||||
IntToString(line_number_);
|
NumberToString(line_number_);
|
||||||
}
|
}
|
||||||
return StringPrintf("pc:%p", program_counter_);
|
return StringPrintf("pc:%p", program_counter_);
|
||||||
}
|
}
|
||||||
@ -69,6 +69,25 @@ NOINLINE Location Location::CreateFromHere(const char* function_name,
|
|||||||
return Location(function_name, file_name, line_number, RETURN_ADDRESS());
|
return Location(function_name, file_name, line_number, RETURN_ADDRESS());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if SUPPORTS_LOCATION_BUILTINS && BUILDFLAG(ENABLE_LOCATION_SOURCE)
|
||||||
|
// static
|
||||||
|
NOINLINE Location Location::Current(const char* function_name,
|
||||||
|
const char* file_name,
|
||||||
|
int line_number) {
|
||||||
|
return Location(function_name, file_name, line_number, RETURN_ADDRESS());
|
||||||
|
}
|
||||||
|
#elif SUPPORTS_LOCATION_BUILTINS
|
||||||
|
// static
|
||||||
|
NOINLINE Location Location::Current(const char* file_name) {
|
||||||
|
return Location(file_name, RETURN_ADDRESS());
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// static
|
||||||
|
NOINLINE Location Location::Current() {
|
||||||
|
return Location(nullptr, RETURN_ADDRESS());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
NOINLINE const void* GetProgramCounter() {
|
NOINLINE const void* GetProgramCounter() {
|
||||||
return RETURN_ADDRESS();
|
return RETURN_ADDRESS();
|
||||||
|
@ -8,14 +8,29 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <functional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "base/base_export.h"
|
#include "base/base_export.h"
|
||||||
#include "base/debug/debugging_buildflags.h"
|
#include "base/debug/debugging_buildflags.h"
|
||||||
#include "base/hash.h"
|
#include "base/hash/hash.h"
|
||||||
|
#include "build/build_config.h"
|
||||||
|
|
||||||
namespace base {
|
namespace base {
|
||||||
|
|
||||||
|
#if defined(__has_builtin)
|
||||||
|
// Clang allows detection of these builtins.
|
||||||
|
#define SUPPORTS_LOCATION_BUILTINS \
|
||||||
|
(__has_builtin(__builtin_FUNCTION) && __has_builtin(__builtin_FILE) && \
|
||||||
|
__has_builtin(__builtin_LINE))
|
||||||
|
#elif defined(COMPILER_GCC) && __GNUC__ >= 7
|
||||||
|
// GCC has supported these for a long time, but they point at the function
|
||||||
|
// declaration in the case of default arguments, rather than at the call site.
|
||||||
|
#define SUPPORTS_LOCATION_BUILTINS 1
|
||||||
|
#else
|
||||||
|
#define SUPPORTS_LOCATION_BUILTINS 0
|
||||||
|
#endif
|
||||||
|
|
||||||
// Location provides basic info where of an object was constructed, or was
|
// Location provides basic info where of an object was constructed, or was
|
||||||
// significantly brought to life.
|
// significantly brought to life.
|
||||||
class BASE_EXPORT Location {
|
class BASE_EXPORT Location {
|
||||||
@ -73,6 +88,16 @@ class BASE_EXPORT Location {
|
|||||||
const char* file_name,
|
const char* file_name,
|
||||||
int line_number);
|
int line_number);
|
||||||
|
|
||||||
|
#if SUPPORTS_LOCATION_BUILTINS && BUILDFLAG(ENABLE_LOCATION_SOURCE)
|
||||||
|
static Location Current(const char* function_name = __builtin_FUNCTION(),
|
||||||
|
const char* file_name = __builtin_FILE(),
|
||||||
|
int line_number = __builtin_LINE());
|
||||||
|
#elif SUPPORTS_LOCATION_BUILTINS
|
||||||
|
static Location Current(const char* file_name = __builtin_FILE());
|
||||||
|
#else
|
||||||
|
static Location Current();
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const char* function_name_ = nullptr;
|
const char* function_name_ = nullptr;
|
||||||
const char* file_name_ = nullptr;
|
const char* file_name_ = nullptr;
|
||||||
@ -108,7 +133,7 @@ template <>
|
|||||||
struct hash<::base::Location> {
|
struct hash<::base::Location> {
|
||||||
std::size_t operator()(const ::base::Location& loc) const {
|
std::size_t operator()(const ::base::Location& loc) const {
|
||||||
const void* program_counter = loc.program_counter();
|
const void* program_counter = loc.program_counter();
|
||||||
return base::Hash(&program_counter, sizeof(void*));
|
return base::FastHash(base::as_bytes(base::make_span(&program_counter, 1)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <cstdint>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
@ -17,13 +18,18 @@
|
|||||||
#include "base/base_export.h"
|
#include "base/base_export.h"
|
||||||
#include "base/callback_forward.h"
|
#include "base/callback_forward.h"
|
||||||
#include "base/compiler_specific.h"
|
#include "base/compiler_specific.h"
|
||||||
#include "base/debug/debugger.h"
|
#include "base/immediate_crash.h"
|
||||||
|
#include "base/logging_buildflags.h"
|
||||||
#include "base/macros.h"
|
#include "base/macros.h"
|
||||||
#include "base/scoped_clear_last_error.h"
|
#include "base/scoped_clear_last_error.h"
|
||||||
#include "base/strings/string_piece_forward.h"
|
#include "base/strings/string_piece_forward.h"
|
||||||
#include "base/template_util.h"
|
#include "base/template_util.h"
|
||||||
#include "build/build_config.h"
|
#include "build/build_config.h"
|
||||||
|
|
||||||
|
#if defined(OS_CHROMEOS)
|
||||||
|
#include <cstdio>
|
||||||
|
#endif
|
||||||
|
|
||||||
//
|
//
|
||||||
// Optional message capabilities
|
// Optional message capabilities
|
||||||
// -----------------------------
|
// -----------------------------
|
||||||
@ -162,27 +168,35 @@ namespace logging {
|
|||||||
|
|
||||||
// TODO(avi): do we want to do a unification of character types here?
|
// TODO(avi): do we want to do a unification of character types here?
|
||||||
#if defined(OS_WIN)
|
#if defined(OS_WIN)
|
||||||
typedef base::char16 PathChar;
|
typedef wchar_t PathChar;
|
||||||
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
|
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||||
typedef char PathChar;
|
typedef char PathChar;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Where to record logging output? A flat file and/or system debug log
|
// A bitmask of potential logging destinations.
|
||||||
// via OutputDebugString.
|
using LoggingDestination = uint32_t;
|
||||||
enum LoggingDestination {
|
// Specifies where logs will be written. Multiple destinations can be specified
|
||||||
|
// with bitwise OR.
|
||||||
|
// Unless destination is LOG_NONE, all logs with severity ERROR and above will
|
||||||
|
// be written to stderr in addition to the specified destination.
|
||||||
|
enum : uint32_t {
|
||||||
LOG_NONE = 0,
|
LOG_NONE = 0,
|
||||||
LOG_TO_FILE = 1 << 0,
|
LOG_TO_FILE = 1 << 0,
|
||||||
LOG_TO_SYSTEM_DEBUG_LOG = 1 << 1,
|
LOG_TO_SYSTEM_DEBUG_LOG = 1 << 1,
|
||||||
|
LOG_TO_STDERR = 1 << 2,
|
||||||
|
|
||||||
LOG_TO_ALL = LOG_TO_FILE | LOG_TO_SYSTEM_DEBUG_LOG,
|
LOG_TO_ALL = LOG_TO_FILE | LOG_TO_SYSTEM_DEBUG_LOG | LOG_TO_STDERR,
|
||||||
|
|
||||||
// On Windows, use a file next to the exe; on POSIX platforms, where
|
// On Windows, use a file next to the exe.
|
||||||
// it may not even be possible to locate the executable on disk, use
|
// On POSIX platforms, where it may not even be possible to locate the
|
||||||
// stderr.
|
// executable on disk, use stderr.
|
||||||
#if defined(OS_WIN)
|
// On Fuchsia, use the Fuchsia logging service.
|
||||||
LOG_DEFAULT = LOG_TO_FILE,
|
#if defined(OS_FUCHSIA) || defined(OS_NACL)
|
||||||
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
|
|
||||||
LOG_DEFAULT = LOG_TO_SYSTEM_DEBUG_LOG,
|
LOG_DEFAULT = LOG_TO_SYSTEM_DEBUG_LOG,
|
||||||
|
#elif defined(OS_WIN)
|
||||||
|
LOG_DEFAULT = LOG_TO_FILE,
|
||||||
|
#elif defined(OS_POSIX)
|
||||||
|
LOG_DEFAULT = LOG_TO_SYSTEM_DEBUG_LOG | LOG_TO_STDERR,
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -200,21 +214,22 @@ enum LogLockingState { LOCK_LOG_FILE, DONT_LOCK_LOG_FILE };
|
|||||||
enum OldFileDeletionState { DELETE_OLD_LOG_FILE, APPEND_TO_OLD_LOG_FILE };
|
enum OldFileDeletionState { DELETE_OLD_LOG_FILE, APPEND_TO_OLD_LOG_FILE };
|
||||||
|
|
||||||
struct BASE_EXPORT LoggingSettings {
|
struct BASE_EXPORT LoggingSettings {
|
||||||
// The defaults values are:
|
// Equivalent to logging destination enum, but allows for multiple
|
||||||
//
|
// destinations.
|
||||||
// logging_dest: LOG_DEFAULT
|
uint32_t logging_dest = LOG_DEFAULT;
|
||||||
// log_file: NULL
|
|
||||||
// lock_log: LOCK_LOG_FILE
|
|
||||||
// delete_old: APPEND_TO_OLD_LOG_FILE
|
|
||||||
LoggingSettings();
|
|
||||||
|
|
||||||
LoggingDestination logging_dest;
|
// The four settings below have an effect only when LOG_TO_FILE is
|
||||||
|
|
||||||
// The three settings below have an effect only when LOG_TO_FILE is
|
|
||||||
// set in |logging_dest|.
|
// set in |logging_dest|.
|
||||||
const PathChar* log_file;
|
const PathChar* log_file_path = nullptr;
|
||||||
LogLockingState lock_log;
|
LogLockingState lock_log = LOCK_LOG_FILE;
|
||||||
OldFileDeletionState delete_old;
|
OldFileDeletionState delete_old = APPEND_TO_OLD_LOG_FILE;
|
||||||
|
#if defined(OS_CHROMEOS)
|
||||||
|
// Contains an optional file that logs should be written to. If present,
|
||||||
|
// |log_file_path| will be ignored, and the logging system will take ownership
|
||||||
|
// of the FILE. If there's an error writing to this file, no fallback paths
|
||||||
|
// will be opened.
|
||||||
|
FILE* log_file = nullptr;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
// Define different names for the BaseInitLoggingImpl() function depending on
|
// Define different names for the BaseInitLoggingImpl() function depending on
|
||||||
@ -300,10 +315,10 @@ BASE_EXPORT void SetShowErrorDialogs(bool enable_dialogs);
|
|||||||
// however clients can use this function to override with their own handling
|
// however clients can use this function to override with their own handling
|
||||||
// (e.g. a silent one for Unit Tests)
|
// (e.g. a silent one for Unit Tests)
|
||||||
using LogAssertHandlerFunction =
|
using LogAssertHandlerFunction =
|
||||||
base::Callback<void(const char* file,
|
base::RepeatingCallback<void(const char* file,
|
||||||
int line,
|
int line,
|
||||||
const base::StringPiece message,
|
const base::StringPiece message,
|
||||||
const base::StringPiece stack_trace)>;
|
const base::StringPiece stack_trace)>;
|
||||||
|
|
||||||
class BASE_EXPORT ScopedLogAssertHandler {
|
class BASE_EXPORT ScopedLogAssertHandler {
|
||||||
public:
|
public:
|
||||||
@ -515,9 +530,9 @@ BASE_EXPORT extern std::ostream* g_swallow_stream;
|
|||||||
class CheckOpResult {
|
class CheckOpResult {
|
||||||
public:
|
public:
|
||||||
// |message| must be non-null if and only if the check failed.
|
// |message| must be non-null if and only if the check failed.
|
||||||
CheckOpResult(std::string* message) : message_(message) {}
|
constexpr CheckOpResult(std::string* message) : message_(message) {}
|
||||||
// Returns true if the check succeeded.
|
// Returns true if the check succeeded.
|
||||||
operator bool() const { return !message_; }
|
constexpr operator bool() const { return !message_; }
|
||||||
// Returns the message.
|
// Returns the message.
|
||||||
std::string* message() { return message_; }
|
std::string* message() { return message_; }
|
||||||
|
|
||||||
@ -525,110 +540,6 @@ class CheckOpResult {
|
|||||||
std::string* message_;
|
std::string* message_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Crashes in the fastest possible way with no attempt at logging.
|
|
||||||
// There are different constraints to satisfy here, see http://crbug.com/664209
|
|
||||||
// for more context:
|
|
||||||
// - The trap instructions, and hence the PC value at crash time, have to be
|
|
||||||
// distinct and not get folded into the same opcode by the compiler.
|
|
||||||
// On Linux/Android this is tricky because GCC still folds identical
|
|
||||||
// asm volatile blocks. The workaround is generating distinct opcodes for
|
|
||||||
// each CHECK using the __COUNTER__ macro.
|
|
||||||
// - The debug info for the trap instruction has to be attributed to the source
|
|
||||||
// line that has the CHECK(), to make crash reports actionable. This rules
|
|
||||||
// out the ability of using a inline function, at least as long as clang
|
|
||||||
// doesn't support attribute(artificial).
|
|
||||||
// - Failed CHECKs should produce a signal that is distinguishable from an
|
|
||||||
// invalid memory access, to improve the actionability of crash reports.
|
|
||||||
// - The compiler should treat the CHECK as no-return instructions, so that the
|
|
||||||
// trap code can be efficiently packed in the prologue of the function and
|
|
||||||
// doesn't interfere with the main execution flow.
|
|
||||||
// - When debugging, developers shouldn't be able to accidentally step over a
|
|
||||||
// CHECK. This is achieved by putting opcodes that will cause a non
|
|
||||||
// continuable exception after the actual trap instruction.
|
|
||||||
// - Don't cause too much binary bloat.
|
|
||||||
#if defined(COMPILER_GCC)
|
|
||||||
|
|
||||||
#if defined(ARCH_CPU_X86_FAMILY) && !defined(OS_NACL)
|
|
||||||
// int 3 will generate a SIGTRAP.
|
|
||||||
#define TRAP_SEQUENCE() \
|
|
||||||
asm volatile( \
|
|
||||||
"int3; ud2; push %0;" ::"i"(static_cast<unsigned char>(__COUNTER__)))
|
|
||||||
|
|
||||||
#elif defined(ARCH_CPU_ARMEL) && !defined(OS_NACL)
|
|
||||||
// bkpt will generate a SIGBUS when running on armv7 and a SIGTRAP when running
|
|
||||||
// as a 32 bit userspace app on arm64. There doesn't seem to be any way to
|
|
||||||
// cause a SIGTRAP from userspace without using a syscall (which would be a
|
|
||||||
// problem for sandboxing).
|
|
||||||
#define TRAP_SEQUENCE() \
|
|
||||||
asm volatile("bkpt #0; udf %0;" ::"i"(__COUNTER__ % 256))
|
|
||||||
|
|
||||||
#elif defined(ARCH_CPU_ARM64) && !defined(OS_NACL)
|
|
||||||
// This will always generate a SIGTRAP on arm64.
|
|
||||||
#define TRAP_SEQUENCE() \
|
|
||||||
asm volatile("brk #0; hlt %0;" ::"i"(__COUNTER__ % 65536))
|
|
||||||
|
|
||||||
#else
|
|
||||||
// Crash report accuracy will not be guaranteed on other architectures, but at
|
|
||||||
// least this will crash as expected.
|
|
||||||
#define TRAP_SEQUENCE() __builtin_trap()
|
|
||||||
#endif // ARCH_CPU_*
|
|
||||||
|
|
||||||
#elif defined(COMPILER_MSVC)
|
|
||||||
|
|
||||||
// Clang is cleverer about coalescing int3s, so we need to add a unique-ish
|
|
||||||
// instruction following the __debugbreak() to have it emit distinct locations
|
|
||||||
// for CHECKs rather than collapsing them all together. It would be nice to use
|
|
||||||
// a short intrinsic to do this (and perhaps have only one implementation for
|
|
||||||
// both clang and MSVC), however clang-cl currently does not support intrinsics.
|
|
||||||
// On the flip side, MSVC x64 doesn't support inline asm. So, we have to have
|
|
||||||
// two implementations. Normally clang-cl's version will be 5 bytes (1 for
|
|
||||||
// `int3`, 2 for `ud2`, 2 for `push byte imm`, however, TODO(scottmg):
|
|
||||||
// https://crbug.com/694670 clang-cl doesn't currently support %'ing
|
|
||||||
// __COUNTER__, so eventually it will emit the dword form of push.
|
|
||||||
// TODO(scottmg): Reinvestigate a short sequence that will work on both
|
|
||||||
// compilers once clang supports more intrinsics. See https://crbug.com/693713.
|
|
||||||
#if !defined(__clang__)
|
|
||||||
#define TRAP_SEQUENCE() __debugbreak()
|
|
||||||
#elif defined(ARCH_CPU_ARM64)
|
|
||||||
#define TRAP_SEQUENCE() \
|
|
||||||
__asm volatile("brk #0\n hlt %0\n" ::"i"(__COUNTER__ % 65536));
|
|
||||||
#else
|
|
||||||
#define TRAP_SEQUENCE() ({ {__asm int 3 __asm ud2 __asm push __COUNTER__}; })
|
|
||||||
#endif // __clang__
|
|
||||||
|
|
||||||
#else
|
|
||||||
#error Port
|
|
||||||
#endif // COMPILER_GCC
|
|
||||||
|
|
||||||
// CHECK() and the trap sequence can be invoked from a constexpr function.
|
|
||||||
// This could make compilation fail on GCC, as it forbids directly using inline
|
|
||||||
// asm inside a constexpr function. However, it allows calling a lambda
|
|
||||||
// expression including the same asm.
|
|
||||||
// The side effect is that the top of the stacktrace will not point to the
|
|
||||||
// calling function, but to this anonymous lambda. This is still useful as the
|
|
||||||
// full name of the lambda will typically include the name of the function that
|
|
||||||
// calls CHECK() and the debugger will still break at the right line of code.
|
|
||||||
#if !defined(COMPILER_GCC)
|
|
||||||
#define WRAPPED_TRAP_SEQUENCE() TRAP_SEQUENCE()
|
|
||||||
#else
|
|
||||||
#define WRAPPED_TRAP_SEQUENCE() \
|
|
||||||
do { \
|
|
||||||
[] { TRAP_SEQUENCE(); }(); \
|
|
||||||
} while (false)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(__clang__) || defined(COMPILER_GCC)
|
|
||||||
#define IMMEDIATE_CRASH() \
|
|
||||||
({ \
|
|
||||||
WRAPPED_TRAP_SEQUENCE(); \
|
|
||||||
__builtin_unreachable(); \
|
|
||||||
})
|
|
||||||
#else
|
|
||||||
// This is supporting non-chromium user of logging.h to build with MSVC, like
|
|
||||||
// pdfium. On MSVC there is no __builtin_unreachable().
|
|
||||||
#define IMMEDIATE_CRASH() WRAPPED_TRAP_SEQUENCE()
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// CHECK dies with a fatal error if condition is not true. It is *not*
|
// CHECK dies with a fatal error if condition is not true. It is *not*
|
||||||
// controlled by NDEBUG, so the check will be executed regardless of
|
// controlled by NDEBUG, so the check will be executed regardless of
|
||||||
// compilation mode.
|
// compilation mode.
|
||||||
@ -659,26 +570,6 @@ class CheckOpResult {
|
|||||||
|
|
||||||
#else // !(OFFICIAL_BUILD && NDEBUG)
|
#else // !(OFFICIAL_BUILD && NDEBUG)
|
||||||
|
|
||||||
#if defined(_PREFAST_) && defined(OS_WIN)
|
|
||||||
// Use __analysis_assume to tell the VC++ static analysis engine that
|
|
||||||
// assert conditions are true, to suppress warnings. The LAZY_STREAM
|
|
||||||
// parameter doesn't reference 'condition' in /analyze builds because
|
|
||||||
// this evaluation confuses /analyze. The !! before condition is because
|
|
||||||
// __analysis_assume gets confused on some conditions:
|
|
||||||
// http://randomascii.wordpress.com/2011/09/13/analyze-for-visual-studio-the-ugly-part-5/
|
|
||||||
|
|
||||||
#define CHECK(condition) \
|
|
||||||
__analysis_assume(!!(condition)), \
|
|
||||||
LAZY_STREAM(LOG_STREAM(FATAL), false) \
|
|
||||||
<< "Check failed: " #condition ". "
|
|
||||||
|
|
||||||
#define PCHECK(condition) \
|
|
||||||
__analysis_assume(!!(condition)), \
|
|
||||||
LAZY_STREAM(PLOG_STREAM(FATAL), false) \
|
|
||||||
<< "Check failed: " #condition ". "
|
|
||||||
|
|
||||||
#else // _PREFAST_
|
|
||||||
|
|
||||||
// Do as much work as possible out of line to reduce inline code size.
|
// Do as much work as possible out of line to reduce inline code size.
|
||||||
#define CHECK(condition) \
|
#define CHECK(condition) \
|
||||||
LAZY_STREAM(::logging::LogMessage(__FILE__, __LINE__, #condition).stream(), \
|
LAZY_STREAM(::logging::LogMessage(__FILE__, __LINE__, #condition).stream(), \
|
||||||
@ -688,8 +579,6 @@ class CheckOpResult {
|
|||||||
LAZY_STREAM(PLOG_STREAM(FATAL), !ANALYZER_ASSUME_TRUE(condition)) \
|
LAZY_STREAM(PLOG_STREAM(FATAL), !ANALYZER_ASSUME_TRUE(condition)) \
|
||||||
<< "Check failed: " #condition ". "
|
<< "Check failed: " #condition ". "
|
||||||
|
|
||||||
#endif // _PREFAST_
|
|
||||||
|
|
||||||
// Helper macro for binary operators.
|
// Helper macro for binary operators.
|
||||||
// Don't use this macro directly in your code, use CHECK_EQ et al below.
|
// Don't use this macro directly in your code, use CHECK_EQ et al below.
|
||||||
// The 'switch' is used to prevent the 'else' from being ambiguous when the
|
// The 'switch' is used to prevent the 'else' from being ambiguous when the
|
||||||
@ -718,6 +607,16 @@ MakeCheckOpValueString(std::ostream* os, const T& v) {
|
|||||||
(*os) << v;
|
(*os) << v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Overload for types that no operator<< but do have .ToString() defined.
|
||||||
|
template <typename T>
|
||||||
|
inline typename std::enable_if<
|
||||||
|
!base::internal::SupportsOstreamOperator<const T&>::value &&
|
||||||
|
base::internal::SupportsToString<const T&>::value,
|
||||||
|
void>::type
|
||||||
|
MakeCheckOpValueString(std::ostream* os, const T& v) {
|
||||||
|
(*os) << v.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
// Provide an overload for functions and function pointers. Function pointers
|
// Provide an overload for functions and function pointers. Function pointers
|
||||||
// don't implicitly convert to void* but do implicitly convert to bool, so
|
// don't implicitly convert to void* but do implicitly convert to bool, so
|
||||||
// without this function pointers are always printed as 1 or 0. (MSVC isn't
|
// without this function pointers are always printed as 1 or 0. (MSVC isn't
|
||||||
@ -786,20 +685,21 @@ std::string* MakeCheckOpString<std::string, std::string>(
|
|||||||
// The checked condition is wrapped with ANALYZER_ASSUME_TRUE, which under
|
// The checked condition is wrapped with ANALYZER_ASSUME_TRUE, which under
|
||||||
// static analysis builds, blocks analysis of the current path if the
|
// static analysis builds, blocks analysis of the current path if the
|
||||||
// condition is false.
|
// condition is false.
|
||||||
#define DEFINE_CHECK_OP_IMPL(name, op) \
|
#define DEFINE_CHECK_OP_IMPL(name, op) \
|
||||||
template <class t1, class t2> \
|
template <class t1, class t2> \
|
||||||
inline std::string* Check##name##Impl(const t1& v1, const t2& v2, \
|
constexpr std::string* Check##name##Impl(const t1& v1, const t2& v2, \
|
||||||
const char* names) { \
|
const char* names) { \
|
||||||
if (ANALYZER_ASSUME_TRUE(v1 op v2)) \
|
if (ANALYZER_ASSUME_TRUE(v1 op v2)) \
|
||||||
return NULL; \
|
return nullptr; \
|
||||||
else \
|
else \
|
||||||
return ::logging::MakeCheckOpString(v1, v2, names); \
|
return ::logging::MakeCheckOpString(v1, v2, names); \
|
||||||
} \
|
} \
|
||||||
inline std::string* Check##name##Impl(int v1, int v2, const char* names) { \
|
constexpr std::string* Check##name##Impl(int v1, int v2, \
|
||||||
if (ANALYZER_ASSUME_TRUE(v1 op v2)) \
|
const char* names) { \
|
||||||
return NULL; \
|
if (ANALYZER_ASSUME_TRUE(v1 op v2)) \
|
||||||
else \
|
return nullptr; \
|
||||||
return ::logging::MakeCheckOpString(v1, v2, names); \
|
else \
|
||||||
|
return ::logging::MakeCheckOpString(v1, v2, names); \
|
||||||
}
|
}
|
||||||
DEFINE_CHECK_OP_IMPL(EQ, ==)
|
DEFINE_CHECK_OP_IMPL(EQ, ==)
|
||||||
DEFINE_CHECK_OP_IMPL(NE, !=)
|
DEFINE_CHECK_OP_IMPL(NE, !=)
|
||||||
@ -817,9 +717,9 @@ DEFINE_CHECK_OP_IMPL(GT, > )
|
|||||||
#define CHECK_GT(val1, val2) CHECK_OP(GT, > , val1, val2)
|
#define CHECK_GT(val1, val2) CHECK_OP(GT, > , val1, val2)
|
||||||
|
|
||||||
#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
|
#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
|
||||||
#define DCHECK_IS_ON() 0
|
#define DCHECK_IS_ON() false
|
||||||
#else
|
#else
|
||||||
#define DCHECK_IS_ON() 1
|
#define DCHECK_IS_ON() true
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Definitions for DLOG et al.
|
// Definitions for DLOG et al.
|
||||||
@ -885,21 +785,6 @@ const LogSeverity LOG_DCHECK = LOG_FATAL;
|
|||||||
// DCHECK_IS_ON() is true. When DCHECK_IS_ON() is false, the macros use
|
// DCHECK_IS_ON() is true. When DCHECK_IS_ON() is false, the macros use
|
||||||
// EAT_STREAM_PARAMETERS to avoid expressions that would create temporaries.
|
// EAT_STREAM_PARAMETERS to avoid expressions that would create temporaries.
|
||||||
|
|
||||||
#if defined(_PREFAST_) && defined(OS_WIN)
|
|
||||||
// See comments on the previous use of __analysis_assume.
|
|
||||||
|
|
||||||
#define DCHECK(condition) \
|
|
||||||
__analysis_assume(!!(condition)), \
|
|
||||||
LAZY_STREAM(LOG_STREAM(DCHECK), false) \
|
|
||||||
<< "Check failed: " #condition ". "
|
|
||||||
|
|
||||||
#define DPCHECK(condition) \
|
|
||||||
__analysis_assume(!!(condition)), \
|
|
||||||
LAZY_STREAM(PLOG_STREAM(DCHECK), false) \
|
|
||||||
<< "Check failed: " #condition ". "
|
|
||||||
|
|
||||||
#else // !(defined(_PREFAST_) && defined(OS_WIN))
|
|
||||||
|
|
||||||
#if DCHECK_IS_ON()
|
#if DCHECK_IS_ON()
|
||||||
|
|
||||||
#define DCHECK(condition) \
|
#define DCHECK(condition) \
|
||||||
@ -916,8 +801,6 @@ const LogSeverity LOG_DCHECK = LOG_FATAL;
|
|||||||
|
|
||||||
#endif // DCHECK_IS_ON()
|
#endif // DCHECK_IS_ON()
|
||||||
|
|
||||||
#endif // defined(_PREFAST_) && defined(OS_WIN)
|
|
||||||
|
|
||||||
// Helper macro for binary operators.
|
// Helper macro for binary operators.
|
||||||
// Don't use this macro directly in your code, use DCHECK_EQ et al below.
|
// Don't use this macro directly in your code, use DCHECK_EQ et al below.
|
||||||
// The 'switch' is used to prevent the 'else' from being ambiguous when the
|
// The 'switch' is used to prevent the 'else' from being ambiguous when the
|
||||||
@ -982,7 +865,7 @@ const LogSeverity LOG_DCHECK = LOG_FATAL;
|
|||||||
#define DCHECK_GE(val1, val2) DCHECK_OP(GE, >=, val1, val2)
|
#define DCHECK_GE(val1, val2) DCHECK_OP(GE, >=, val1, val2)
|
||||||
#define DCHECK_GT(val1, val2) DCHECK_OP(GT, > , val1, val2)
|
#define DCHECK_GT(val1, val2) DCHECK_OP(GT, > , val1, val2)
|
||||||
|
|
||||||
#if !DCHECK_IS_ON() && defined(OS_CHROMEOS)
|
#if BUILDFLAG(ENABLE_LOG_ERROR_NOT_REACHED)
|
||||||
// Implement logging of NOTREACHED() as a dedicated function to get function
|
// Implement logging of NOTREACHED() as a dedicated function to get function
|
||||||
// call overhead down to a minimum.
|
// call overhead down to a minimum.
|
||||||
void LogErrorNotReached(const char* file, int line);
|
void LogErrorNotReached(const char* file, int line);
|
||||||
@ -1038,6 +921,7 @@ class BASE_EXPORT LogMessage {
|
|||||||
// The file and line information passed in to the constructor.
|
// The file and line information passed in to the constructor.
|
||||||
const char* file_;
|
const char* file_;
|
||||||
const int line_;
|
const int line_;
|
||||||
|
const char* file_basename_;
|
||||||
|
|
||||||
// This is useful since the LogMessage class uses a lot of Win32 calls
|
// This is useful since the LogMessage class uses a lot of Win32 calls
|
||||||
// that will lose the value of GLE and the code that called the log function
|
// that will lose the value of GLE and the code that called the log function
|
||||||
@ -1117,6 +1001,14 @@ class BASE_EXPORT ErrnoLogMessage {
|
|||||||
// after this call.
|
// after this call.
|
||||||
BASE_EXPORT void CloseLogFile();
|
BASE_EXPORT void CloseLogFile();
|
||||||
|
|
||||||
|
#if defined(OS_CHROMEOS)
|
||||||
|
// Returns a new file handle that will write to the same destination as the
|
||||||
|
// currently open log file. Returns nullptr if logging to a file is disabled,
|
||||||
|
// or if opening the file failed. This is intended to be used to initialize
|
||||||
|
// logging in child processes that are unable to open files.
|
||||||
|
BASE_EXPORT FILE* DuplicateLogFILE();
|
||||||
|
#endif
|
||||||
|
|
||||||
// Async signal safe logging mechanism.
|
// Async signal safe logging mechanism.
|
||||||
BASE_EXPORT void RawLog(int level, const char* message);
|
BASE_EXPORT void RawLog(int level, const char* message);
|
||||||
|
|
||||||
@ -1135,7 +1027,7 @@ BASE_EXPORT void RawLog(int level, const char* message);
|
|||||||
BASE_EXPORT bool IsLoggingToFileEnabled();
|
BASE_EXPORT bool IsLoggingToFileEnabled();
|
||||||
|
|
||||||
// Returns the default log file path.
|
// Returns the default log file path.
|
||||||
BASE_EXPORT base::string16 GetLogFileFullPath();
|
BASE_EXPORT std::wstring GetLogFileFullPath();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} // namespace logging
|
} // namespace logging
|
||||||
@ -1173,18 +1065,13 @@ inline std::ostream& operator<<(std::ostream& out, const std::wstring& wstr) {
|
|||||||
#define NOTIMPLEMENTED_MSG "NOT IMPLEMENTED"
|
#define NOTIMPLEMENTED_MSG "NOT IMPLEMENTED"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(OS_ANDROID) && defined(OFFICIAL_BUILD)
|
#define NOTIMPLEMENTED() DLOG(ERROR) << NOTIMPLEMENTED_MSG
|
||||||
#define NOTIMPLEMENTED() EAT_STREAM_PARAMETERS
|
#define NOTIMPLEMENTED_LOG_ONCE() \
|
||||||
#define NOTIMPLEMENTED_LOG_ONCE() EAT_STREAM_PARAMETERS
|
do { \
|
||||||
#else
|
static bool logged_once = false; \
|
||||||
#define NOTIMPLEMENTED() LOG(ERROR) << NOTIMPLEMENTED_MSG
|
DLOG_IF(ERROR, !logged_once) << NOTIMPLEMENTED_MSG; \
|
||||||
#define NOTIMPLEMENTED_LOG_ONCE() \
|
logged_once = true; \
|
||||||
do { \
|
} while (0); \
|
||||||
static bool logged_once = false; \
|
|
||||||
LOG_IF(ERROR, !logged_once) << NOTIMPLEMENTED_MSG; \
|
|
||||||
logged_once = true; \
|
|
||||||
} while (0); \
|
|
||||||
EAT_STREAM_PARAMETERS
|
EAT_STREAM_PARAMETERS
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // BASE_LOGGING_H_
|
#endif // BASE_LOGGING_H_
|
||||||
|
@ -10,6 +10,10 @@
|
|||||||
#ifndef BASE_MACROS_H_
|
#ifndef BASE_MACROS_H_
|
||||||
#define BASE_MACROS_H_
|
#define BASE_MACROS_H_
|
||||||
|
|
||||||
|
// ALL DISALLOW_xxx MACROS ARE DEPRECATED; DO NOT USE IN NEW CODE.
|
||||||
|
// Use explicit deletions instead. See the section on copyability/movability in
|
||||||
|
// //styleguide/c++/c++-dos-and-donts.md for more information.
|
||||||
|
|
||||||
// Put this in the declarations for a class to be uncopyable.
|
// Put this in the declarations for a class to be uncopyable.
|
||||||
#define DISALLOW_COPY(TypeName) \
|
#define DISALLOW_COPY(TypeName) \
|
||||||
TypeName(const TypeName&) = delete
|
TypeName(const TypeName&) = delete
|
||||||
|
@ -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 "base/memory/ref_counted.h"
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
#include "base/threading/thread_collision_warner.h"
|
#include "base/threading/thread_collision_warner.h"
|
||||||
|
|
||||||
namespace base {
|
namespace base {
|
||||||
@ -32,15 +35,35 @@ RefCountedThreadSafeBase::~RefCountedThreadSafeBase() {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// This is a security check. In 32-bit-archs, an attacker would run out of
|
// For security and correctness, we check the arithmetic on ref counts.
|
||||||
// address space after allocating at most 2^32 scoped_refptrs. This replicates
|
//
|
||||||
// that boundary for 64-bit-archs.
|
// In an attempt to avoid binary bloat (from inlining the `CHECK`), we define
|
||||||
|
// these functions out-of-line. However, compilers are wily. Further testing may
|
||||||
|
// show that `NOINLINE` helps or hurts.
|
||||||
|
//
|
||||||
#if defined(ARCH_CPU_64_BITS)
|
#if defined(ARCH_CPU_64_BITS)
|
||||||
void RefCountedBase::AddRefImpl() const {
|
void RefCountedBase::AddRefImpl() const {
|
||||||
// Check if |ref_count_| overflow only on 64 bit archs since the number of
|
// An attacker could induce use-after-free bugs, and potentially exploit them,
|
||||||
// objects may exceed 2^32.
|
// by creating so many references to a ref-counted object that the reference
|
||||||
// To avoid the binary size bloat, use non-inline function here.
|
// count overflows. On 32-bit architectures, there is not enough address space
|
||||||
CHECK(++ref_count_ > 0);
|
// to succeed. But on 64-bit architectures, it might indeed be possible.
|
||||||
|
// Therefore, we can elide the check for arithmetic overflow on 32-bit, but we
|
||||||
|
// must check on 64-bit.
|
||||||
|
//
|
||||||
|
// Make sure the addition didn't wrap back around to 0. This form of check
|
||||||
|
// works because we assert that `ref_count_` is an unsigned integer type.
|
||||||
|
CHECK(++ref_count_ != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RefCountedBase::ReleaseImpl() const {
|
||||||
|
// Make sure the subtraction didn't wrap back around from 0 to the max value.
|
||||||
|
// That could cause memory leaks, and may induce application-semantic
|
||||||
|
// correctness or safety bugs. (E.g. what if we really needed that object to
|
||||||
|
// be destroyed at the right time?)
|
||||||
|
//
|
||||||
|
// Note that unlike with overflow, underflow could also happen on 32-bit
|
||||||
|
// architectures. Arguably, we should do this check on32-bit machines too.
|
||||||
|
CHECK(--ref_count_ != std::numeric_limits<decltype(ref_count_)>::max());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ class BASE_EXPORT RefCountedBase {
|
|||||||
|
|
||||||
// Returns true if the object should self-delete.
|
// Returns true if the object should self-delete.
|
||||||
bool Release() const {
|
bool Release() const {
|
||||||
--ref_count_;
|
ReleaseImpl();
|
||||||
|
|
||||||
// TODO(maruel): Add back once it doesn't assert 500 times/sec.
|
// TODO(maruel): Add back once it doesn't assert 500 times/sec.
|
||||||
// Current thread books the critical section "AddRelease"
|
// Current thread books the critical section "AddRelease"
|
||||||
@ -126,8 +126,10 @@ class BASE_EXPORT RefCountedBase {
|
|||||||
|
|
||||||
#if defined(ARCH_CPU_64_BITS)
|
#if defined(ARCH_CPU_64_BITS)
|
||||||
void AddRefImpl() const;
|
void AddRefImpl() const;
|
||||||
|
void ReleaseImpl() const;
|
||||||
#else
|
#else
|
||||||
void AddRefImpl() const { ++ref_count_; }
|
void AddRefImpl() const { ++ref_count_; }
|
||||||
|
void ReleaseImpl() const { --ref_count_; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if DCHECK_IS_ON()
|
#if DCHECK_IS_ON()
|
||||||
@ -135,6 +137,8 @@ class BASE_EXPORT RefCountedBase {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
mutable uint32_t ref_count_ = 0;
|
mutable uint32_t ref_count_ = 0;
|
||||||
|
static_assert(std::is_unsigned<decltype(ref_count_)>::value,
|
||||||
|
"ref_count_ must be an unsigned type.");
|
||||||
|
|
||||||
#if DCHECK_IS_ON()
|
#if DCHECK_IS_ON()
|
||||||
mutable bool needs_adopt_ref_ = false;
|
mutable bool needs_adopt_ref_ = false;
|
||||||
@ -444,6 +448,16 @@ class RefCountedData
|
|||||||
~RefCountedData() = default;
|
~RefCountedData() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool operator==(const RefCountedData<T>& lhs, const RefCountedData<T>& rhs) {
|
||||||
|
return lhs.data == rhs.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool operator!=(const RefCountedData<T>& lhs, const RefCountedData<T>& rhs) {
|
||||||
|
return !(lhs == rhs);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace base
|
} // namespace base
|
||||||
|
|
||||||
#endif // BASE_MEMORY_REF_COUNTED_H_
|
#endif // BASE_MEMORY_REF_COUNTED_H_
|
||||||
|
@ -25,10 +25,17 @@ class RefCounted;
|
|||||||
template <class, typename>
|
template <class, typename>
|
||||||
class RefCountedThreadSafe;
|
class RefCountedThreadSafe;
|
||||||
class SequencedTaskRunner;
|
class SequencedTaskRunner;
|
||||||
|
class WrappedPromise;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
scoped_refptr<T> AdoptRef(T* t);
|
scoped_refptr<T> AdoptRef(T* t);
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
class BasePromise;
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
namespace subtle {
|
namespace subtle {
|
||||||
|
|
||||||
enum AdoptRefTag { kAdoptRefTag };
|
enum AdoptRefTag { kAdoptRefTag };
|
||||||
@ -171,8 +178,16 @@ class scoped_refptr {
|
|||||||
|
|
||||||
constexpr scoped_refptr() = default;
|
constexpr scoped_refptr() = default;
|
||||||
|
|
||||||
// Constructs from raw pointer. constexpr if |p| is null.
|
// Allow implicit construction from nullptr.
|
||||||
constexpr scoped_refptr(T* p) : ptr_(p) {
|
constexpr scoped_refptr(std::nullptr_t) {}
|
||||||
|
|
||||||
|
// Constructs from a raw pointer. Note that this constructor allows implicit
|
||||||
|
// conversion from T* to scoped_refptr<T> which is strongly discouraged. If
|
||||||
|
// you are creating a new ref-counted object please use
|
||||||
|
// base::MakeRefCounted<T>() or base::WrapRefCounted<T>(). Otherwise you
|
||||||
|
// should move or copy construct from an existing scoped_refptr<T> to the
|
||||||
|
// ref-counted object.
|
||||||
|
scoped_refptr(T* p) : ptr_(p) {
|
||||||
if (ptr_)
|
if (ptr_)
|
||||||
AddRef(ptr_);
|
AddRef(ptr_);
|
||||||
}
|
}
|
||||||
@ -221,6 +236,11 @@ class scoped_refptr {
|
|||||||
return ptr_;
|
return ptr_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scoped_refptr& operator=(std::nullptr_t) {
|
||||||
|
reset();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
scoped_refptr& operator=(T* p) { return *this = scoped_refptr(p); }
|
scoped_refptr& operator=(T* p) { return *this = scoped_refptr(p); }
|
||||||
|
|
||||||
// Unified assignment operator.
|
// Unified assignment operator.
|
||||||
@ -260,6 +280,11 @@ class scoped_refptr {
|
|||||||
friend scoped_refptr<U> base::AdoptRef(U*);
|
friend scoped_refptr<U> base::AdoptRef(U*);
|
||||||
friend class ::base::SequencedTaskRunner;
|
friend class ::base::SequencedTaskRunner;
|
||||||
|
|
||||||
|
// Friend access so these classes can use the constructor below as part of a
|
||||||
|
// binary size optimization.
|
||||||
|
friend class ::base::internal::BasePromise;
|
||||||
|
friend class ::base::WrappedPromise;
|
||||||
|
|
||||||
// Returns the owned pointer (if any), releasing ownership to the caller. The
|
// Returns the owned pointer (if any), releasing ownership to the caller. The
|
||||||
// caller is responsible for managing the lifetime of the reference.
|
// caller is responsible for managing the lifetime of the reference.
|
||||||
T* release();
|
T* release();
|
||||||
|
@ -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
|
|
115
security/sandbox/chromium/base/memory/shared_memory_mapping.cc
Normal file
115
security/sandbox/chromium/base/memory/shared_memory_mapping.cc
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
// Copyright 2018 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "base/memory/shared_memory_mapping.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "base/logging.h"
|
||||||
|
#include "base/memory/shared_memory_tracker.h"
|
||||||
|
#include "base/unguessable_token.h"
|
||||||
|
#include "build/build_config.h"
|
||||||
|
|
||||||
|
#if defined(OS_POSIX)
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(OS_WIN)
|
||||||
|
#include <aclapi.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(OS_MACOSX) && !defined(OS_IOS)
|
||||||
|
#include <mach/mach_vm.h>
|
||||||
|
#include "base/mac/mach_logging.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(OS_FUCHSIA)
|
||||||
|
#include <lib/zx/vmar.h>
|
||||||
|
#include "base/fuchsia/fuchsia_logging.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace base {
|
||||||
|
|
||||||
|
SharedMemoryMapping::SharedMemoryMapping() = default;
|
||||||
|
|
||||||
|
SharedMemoryMapping::SharedMemoryMapping(SharedMemoryMapping&& mapping) noexcept
|
||||||
|
: memory_(mapping.memory_),
|
||||||
|
size_(mapping.size_),
|
||||||
|
mapped_size_(mapping.mapped_size_),
|
||||||
|
guid_(mapping.guid_) {
|
||||||
|
mapping.memory_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedMemoryMapping& SharedMemoryMapping::operator=(
|
||||||
|
SharedMemoryMapping&& mapping) noexcept {
|
||||||
|
Unmap();
|
||||||
|
memory_ = mapping.memory_;
|
||||||
|
size_ = mapping.size_;
|
||||||
|
mapped_size_ = mapping.mapped_size_;
|
||||||
|
guid_ = mapping.guid_;
|
||||||
|
mapping.memory_ = nullptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedMemoryMapping::~SharedMemoryMapping() {
|
||||||
|
Unmap();
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedMemoryMapping::SharedMemoryMapping(void* memory,
|
||||||
|
size_t size,
|
||||||
|
size_t mapped_size,
|
||||||
|
const UnguessableToken& guid)
|
||||||
|
: memory_(memory), size_(size), mapped_size_(mapped_size), guid_(guid) {
|
||||||
|
SharedMemoryTracker::GetInstance()->IncrementMemoryUsage(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SharedMemoryMapping::Unmap() {
|
||||||
|
if (!IsValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
SharedMemoryTracker::GetInstance()->DecrementMemoryUsage(*this);
|
||||||
|
#if defined(OS_WIN)
|
||||||
|
if (!UnmapViewOfFile(memory_))
|
||||||
|
DPLOG(ERROR) << "UnmapViewOfFile";
|
||||||
|
#elif defined(OS_FUCHSIA)
|
||||||
|
uintptr_t addr = reinterpret_cast<uintptr_t>(memory_);
|
||||||
|
zx_status_t status = zx::vmar::root_self()->unmap(addr, mapped_size_);
|
||||||
|
if (status != ZX_OK)
|
||||||
|
ZX_DLOG(ERROR, status) << "zx_vmar_unmap";
|
||||||
|
#elif defined(OS_MACOSX) && !defined(OS_IOS)
|
||||||
|
kern_return_t kr = mach_vm_deallocate(
|
||||||
|
mach_task_self(), reinterpret_cast<mach_vm_address_t>(memory_),
|
||||||
|
mapped_size_);
|
||||||
|
MACH_DLOG_IF(ERROR, kr != KERN_SUCCESS, kr) << "mach_vm_deallocate";
|
||||||
|
#else
|
||||||
|
if (munmap(memory_, mapped_size_) < 0)
|
||||||
|
DPLOG(ERROR) << "munmap";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadOnlySharedMemoryMapping::ReadOnlySharedMemoryMapping() = default;
|
||||||
|
ReadOnlySharedMemoryMapping::ReadOnlySharedMemoryMapping(
|
||||||
|
ReadOnlySharedMemoryMapping&&) noexcept = default;
|
||||||
|
ReadOnlySharedMemoryMapping& ReadOnlySharedMemoryMapping::operator=(
|
||||||
|
ReadOnlySharedMemoryMapping&&) noexcept = default;
|
||||||
|
ReadOnlySharedMemoryMapping::ReadOnlySharedMemoryMapping(
|
||||||
|
void* address,
|
||||||
|
size_t size,
|
||||||
|
size_t mapped_size,
|
||||||
|
const UnguessableToken& guid)
|
||||||
|
: SharedMemoryMapping(address, size, mapped_size, guid) {}
|
||||||
|
|
||||||
|
WritableSharedMemoryMapping::WritableSharedMemoryMapping() = default;
|
||||||
|
WritableSharedMemoryMapping::WritableSharedMemoryMapping(
|
||||||
|
WritableSharedMemoryMapping&&) noexcept = default;
|
||||||
|
WritableSharedMemoryMapping& WritableSharedMemoryMapping::operator=(
|
||||||
|
WritableSharedMemoryMapping&&) noexcept = default;
|
||||||
|
WritableSharedMemoryMapping::WritableSharedMemoryMapping(
|
||||||
|
void* address,
|
||||||
|
size_t size,
|
||||||
|
size_t mapped_size,
|
||||||
|
const UnguessableToken& guid)
|
||||||
|
: SharedMemoryMapping(address, size, mapped_size, guid) {}
|
||||||
|
|
||||||
|
} // namespace base
|
252
security/sandbox/chromium/base/memory/shared_memory_mapping.h
Normal file
252
security/sandbox/chromium/base/memory/shared_memory_mapping.h
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
// Copyright 2018 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef BASE_MEMORY_SHARED_MEMORY_MAPPING_H_
|
||||||
|
#define BASE_MEMORY_SHARED_MEMORY_MAPPING_H_
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "base/containers/buffer_iterator.h"
|
||||||
|
#include "base/containers/span.h"
|
||||||
|
#include "base/macros.h"
|
||||||
|
#include "base/unguessable_token.h"
|
||||||
|
|
||||||
|
namespace base {
|
||||||
|
|
||||||
|
namespace subtle {
|
||||||
|
class PlatformSharedMemoryRegion;
|
||||||
|
} // namespace subtle
|
||||||
|
|
||||||
|
// Base class for scoped handles to a shared memory mapping created from a
|
||||||
|
// shared memory region. Created shared memory mappings remain valid even if the
|
||||||
|
// creator region is transferred or destroyed.
|
||||||
|
//
|
||||||
|
// Each mapping has an UnguessableToken that identifies the shared memory region
|
||||||
|
// it was created from. This is used for memory metrics, to avoid overcounting
|
||||||
|
// shared memory.
|
||||||
|
class BASE_EXPORT SharedMemoryMapping {
|
||||||
|
public:
|
||||||
|
// Default constructor initializes an invalid instance.
|
||||||
|
SharedMemoryMapping();
|
||||||
|
|
||||||
|
// Move operations are allowed.
|
||||||
|
SharedMemoryMapping(SharedMemoryMapping&& mapping) noexcept;
|
||||||
|
SharedMemoryMapping& operator=(SharedMemoryMapping&& mapping) noexcept;
|
||||||
|
|
||||||
|
// Unmaps the region if the mapping is valid.
|
||||||
|
virtual ~SharedMemoryMapping();
|
||||||
|
|
||||||
|
// Returns true iff the mapping is valid. False means there is no
|
||||||
|
// corresponding area of memory.
|
||||||
|
bool IsValid() const { return memory_ != nullptr; }
|
||||||
|
|
||||||
|
// Returns the logical size of the mapping in bytes. This is precisely the
|
||||||
|
// size requested by whoever created the mapping, and it is always less than
|
||||||
|
// or equal to |mapped_size()|. This is undefined for invalid instances.
|
||||||
|
size_t size() const {
|
||||||
|
DCHECK(IsValid());
|
||||||
|
return size_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the actual size of the mapping in bytes. This is always at least
|
||||||
|
// as large as |size()| but may be larger due to platform mapping alignment
|
||||||
|
// constraints. This is undefined for invalid instances.
|
||||||
|
size_t mapped_size() const {
|
||||||
|
DCHECK(IsValid());
|
||||||
|
return mapped_size_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns 128-bit GUID of the region this mapping belongs to.
|
||||||
|
const UnguessableToken& guid() const {
|
||||||
|
DCHECK(IsValid());
|
||||||
|
return guid_;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
SharedMemoryMapping(void* address,
|
||||||
|
size_t size,
|
||||||
|
size_t mapped_size,
|
||||||
|
const UnguessableToken& guid);
|
||||||
|
void* raw_memory_ptr() const { return memory_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class SharedMemoryTracker;
|
||||||
|
|
||||||
|
void Unmap();
|
||||||
|
|
||||||
|
void* memory_ = nullptr;
|
||||||
|
size_t size_ = 0;
|
||||||
|
size_t mapped_size_ = 0;
|
||||||
|
UnguessableToken guid_;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(SharedMemoryMapping);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Class modeling a read-only mapping of a shared memory region into the
|
||||||
|
// current process' address space. This is created by ReadOnlySharedMemoryRegion
|
||||||
|
// instances.
|
||||||
|
class BASE_EXPORT ReadOnlySharedMemoryMapping : public SharedMemoryMapping {
|
||||||
|
public:
|
||||||
|
// Default constructor initializes an invalid instance.
|
||||||
|
ReadOnlySharedMemoryMapping();
|
||||||
|
|
||||||
|
// Move operations are allowed.
|
||||||
|
ReadOnlySharedMemoryMapping(ReadOnlySharedMemoryMapping&&) noexcept;
|
||||||
|
ReadOnlySharedMemoryMapping& operator=(
|
||||||
|
ReadOnlySharedMemoryMapping&&) noexcept;
|
||||||
|
|
||||||
|
// Returns the base address of the mapping. This is read-only memory. This is
|
||||||
|
// page-aligned. This is nullptr for invalid instances.
|
||||||
|
const void* memory() const { return raw_memory_ptr(); }
|
||||||
|
|
||||||
|
// Returns a pointer to a page-aligned const T if the mapping is valid and
|
||||||
|
// large enough to contain a T, or nullptr otherwise.
|
||||||
|
template <typename T>
|
||||||
|
const T* GetMemoryAs() const {
|
||||||
|
static_assert(std::is_trivially_copyable<T>::value,
|
||||||
|
"Copying non-trivially-copyable object across memory spaces "
|
||||||
|
"is dangerous");
|
||||||
|
if (!IsValid())
|
||||||
|
return nullptr;
|
||||||
|
if (sizeof(T) > size())
|
||||||
|
return nullptr;
|
||||||
|
return static_cast<const T*>(raw_memory_ptr());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a span of const T. The number of elements is autodeduced from the
|
||||||
|
// size of the shared memory mapping. The number of elements may be
|
||||||
|
// autodeduced as zero, i.e. the mapping is invalid or the size of the mapping
|
||||||
|
// isn't large enough to contain even one T: in that case, an empty span
|
||||||
|
// will be returned. The first element, if any, is guaranteed to be
|
||||||
|
// page-aligned.
|
||||||
|
template <typename T>
|
||||||
|
span<const T> GetMemoryAsSpan() const {
|
||||||
|
static_assert(std::is_trivially_copyable<T>::value,
|
||||||
|
"Copying non-trivially-copyable object across memory spaces "
|
||||||
|
"is dangerous");
|
||||||
|
if (!IsValid())
|
||||||
|
return span<const T>();
|
||||||
|
size_t count = size() / sizeof(T);
|
||||||
|
return GetMemoryAsSpan<T>(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a span of const T with |count| elements if the mapping is valid and
|
||||||
|
// large enough to contain |count| elements, or an empty span otherwise. The
|
||||||
|
// first element, if any, is guaranteed to be page-aligned.
|
||||||
|
template <typename T>
|
||||||
|
span<const T> GetMemoryAsSpan(size_t count) const {
|
||||||
|
static_assert(std::is_trivially_copyable<T>::value,
|
||||||
|
"Copying non-trivially-copyable object across memory spaces "
|
||||||
|
"is dangerous");
|
||||||
|
if (!IsValid())
|
||||||
|
return span<const T>();
|
||||||
|
if (size() / sizeof(T) < count)
|
||||||
|
return span<const T>();
|
||||||
|
return span<const T>(static_cast<const T*>(raw_memory_ptr()), count);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a BufferIterator of const T.
|
||||||
|
template <typename T>
|
||||||
|
BufferIterator<const T> GetMemoryAsBufferIterator() const {
|
||||||
|
return BufferIterator<const T>(GetMemoryAsSpan<T>());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class ReadOnlySharedMemoryRegion;
|
||||||
|
ReadOnlySharedMemoryMapping(void* address,
|
||||||
|
size_t size,
|
||||||
|
size_t mapped_size,
|
||||||
|
const UnguessableToken& guid);
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(ReadOnlySharedMemoryMapping);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Class modeling a writable mapping of a shared memory region into the
|
||||||
|
// current process' address space. This is created by *SharedMemoryRegion
|
||||||
|
// instances.
|
||||||
|
class BASE_EXPORT WritableSharedMemoryMapping : public SharedMemoryMapping {
|
||||||
|
public:
|
||||||
|
// Default constructor initializes an invalid instance.
|
||||||
|
WritableSharedMemoryMapping();
|
||||||
|
|
||||||
|
// Move operations are allowed.
|
||||||
|
WritableSharedMemoryMapping(WritableSharedMemoryMapping&&) noexcept;
|
||||||
|
WritableSharedMemoryMapping& operator=(
|
||||||
|
WritableSharedMemoryMapping&&) noexcept;
|
||||||
|
|
||||||
|
// Returns the base address of the mapping. This is writable memory. This is
|
||||||
|
// page-aligned. This is nullptr for invalid instances.
|
||||||
|
void* memory() const { return raw_memory_ptr(); }
|
||||||
|
|
||||||
|
// Returns a pointer to a page-aligned T if the mapping is valid and large
|
||||||
|
// enough to contain a T, or nullptr otherwise.
|
||||||
|
template <typename T>
|
||||||
|
T* GetMemoryAs() const {
|
||||||
|
static_assert(std::is_trivially_copyable<T>::value,
|
||||||
|
"Copying non-trivially-copyable object across memory spaces "
|
||||||
|
"is dangerous");
|
||||||
|
if (!IsValid())
|
||||||
|
return nullptr;
|
||||||
|
if (sizeof(T) > size())
|
||||||
|
return nullptr;
|
||||||
|
return static_cast<T*>(raw_memory_ptr());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a span of T. The number of elements is autodeduced from the size of
|
||||||
|
// the shared memory mapping. The number of elements may be autodeduced as
|
||||||
|
// zero, i.e. the mapping is invalid or the size of the mapping isn't large
|
||||||
|
// enough to contain even one T: in that case, an empty span will be returned.
|
||||||
|
// The first element, if any, is guaranteed to be page-aligned.
|
||||||
|
template <typename T>
|
||||||
|
span<T> GetMemoryAsSpan() const {
|
||||||
|
static_assert(std::is_trivially_copyable<T>::value,
|
||||||
|
"Copying non-trivially-copyable object across memory spaces "
|
||||||
|
"is dangerous");
|
||||||
|
if (!IsValid())
|
||||||
|
return span<T>();
|
||||||
|
size_t count = size() / sizeof(T);
|
||||||
|
return GetMemoryAsSpan<T>(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a span of T with |count| elements if the mapping is valid and large
|
||||||
|
// enough to contain |count| elements, or an empty span otherwise. The first
|
||||||
|
// element, if any, is guaranteed to be page-aligned.
|
||||||
|
template <typename T>
|
||||||
|
span<T> GetMemoryAsSpan(size_t count) const {
|
||||||
|
static_assert(std::is_trivially_copyable<T>::value,
|
||||||
|
"Copying non-trivially-copyable object across memory spaces "
|
||||||
|
"is dangerous");
|
||||||
|
if (!IsValid())
|
||||||
|
return span<T>();
|
||||||
|
if (size() / sizeof(T) < count)
|
||||||
|
return span<T>();
|
||||||
|
return span<T>(static_cast<T*>(raw_memory_ptr()), count);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a BufferIterator of T.
|
||||||
|
template <typename T>
|
||||||
|
BufferIterator<T> GetMemoryAsBufferIterator() {
|
||||||
|
return BufferIterator<T>(GetMemoryAsSpan<T>());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend WritableSharedMemoryMapping MapAtForTesting(
|
||||||
|
subtle::PlatformSharedMemoryRegion* region,
|
||||||
|
off_t offset,
|
||||||
|
size_t size);
|
||||||
|
friend class ReadOnlySharedMemoryRegion;
|
||||||
|
friend class WritableSharedMemoryRegion;
|
||||||
|
friend class UnsafeSharedMemoryRegion;
|
||||||
|
WritableSharedMemoryMapping(void* address,
|
||||||
|
size_t size,
|
||||||
|
size_t mapped_size,
|
||||||
|
const UnguessableToken& guid);
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(WritableSharedMemoryMapping);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace base
|
||||||
|
|
||||||
|
#endif // BASE_MEMORY_SHARED_MEMORY_MAPPING_H_
|
@ -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 {
|
// class Controller {
|
||||||
// public:
|
// public:
|
||||||
// Controller() : weak_factory_(this) {}
|
|
||||||
// void SpawnWorker() { Worker::StartNew(weak_factory_.GetWeakPtr()); }
|
// void SpawnWorker() { Worker::StartNew(weak_factory_.GetWeakPtr()); }
|
||||||
// void WorkComplete(const Result& result) { ... }
|
// void WorkComplete(const Result& result) { ... }
|
||||||
// private:
|
// private:
|
||||||
// // Member variables should appear before the WeakPtrFactory, to ensure
|
// // Member variables should appear before the WeakPtrFactory, to ensure
|
||||||
// // that any WeakPtrs to Controller are invalidated before its members
|
// // that any WeakPtrs to Controller are invalidated before its members
|
||||||
// // variable's destructors are executed, rendering them invalid.
|
// // variable's destructors are executed, rendering them invalid.
|
||||||
// WeakPtrFactory<Controller> weak_factory_;
|
// WeakPtrFactory<Controller> weak_factory_{this};
|
||||||
// };
|
// };
|
||||||
//
|
//
|
||||||
// class Worker {
|
// class Worker {
|
||||||
@ -117,9 +116,9 @@ class BASE_EXPORT WeakReference {
|
|||||||
explicit WeakReference(const scoped_refptr<Flag>& flag);
|
explicit WeakReference(const scoped_refptr<Flag>& flag);
|
||||||
~WeakReference();
|
~WeakReference();
|
||||||
|
|
||||||
WeakReference(WeakReference&& other);
|
WeakReference(WeakReference&& other) noexcept;
|
||||||
WeakReference(const WeakReference& other);
|
WeakReference(const WeakReference& other);
|
||||||
WeakReference& operator=(WeakReference&& other) = default;
|
WeakReference& operator=(WeakReference&& other) noexcept = default;
|
||||||
WeakReference& operator=(const WeakReference& other) = default;
|
WeakReference& operator=(const WeakReference& other) = default;
|
||||||
|
|
||||||
bool IsValid() const;
|
bool IsValid() const;
|
||||||
@ -141,7 +140,7 @@ class BASE_EXPORT WeakReferenceOwner {
|
|||||||
void Invalidate();
|
void Invalidate();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
mutable scoped_refptr<WeakReference::Flag> flag_;
|
scoped_refptr<WeakReference::Flag> flag_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// This class simplifies the implementation of WeakPtr's type conversion
|
// This class simplifies the implementation of WeakPtr's type conversion
|
||||||
@ -154,9 +153,9 @@ class BASE_EXPORT WeakPtrBase {
|
|||||||
~WeakPtrBase();
|
~WeakPtrBase();
|
||||||
|
|
||||||
WeakPtrBase(const WeakPtrBase& other) = default;
|
WeakPtrBase(const WeakPtrBase& other) = default;
|
||||||
WeakPtrBase(WeakPtrBase&& other) = default;
|
WeakPtrBase(WeakPtrBase&& other) noexcept = default;
|
||||||
WeakPtrBase& operator=(const WeakPtrBase& other) = default;
|
WeakPtrBase& operator=(const WeakPtrBase& other) = default;
|
||||||
WeakPtrBase& operator=(WeakPtrBase&& other) = default;
|
WeakPtrBase& operator=(WeakPtrBase&& other) noexcept = default;
|
||||||
|
|
||||||
void reset() {
|
void reset() {
|
||||||
ref_ = internal::WeakReference();
|
ref_ = internal::WeakReference();
|
||||||
@ -237,7 +236,7 @@ class WeakPtr : public internal::WeakPtrBase {
|
|||||||
ptr_ = reinterpret_cast<uintptr_t>(t);
|
ptr_ = reinterpret_cast<uintptr_t>(t);
|
||||||
}
|
}
|
||||||
template <typename U>
|
template <typename U>
|
||||||
WeakPtr(WeakPtr<U>&& other) : WeakPtrBase(std::move(other)) {
|
WeakPtr(WeakPtr<U>&& other) noexcept : WeakPtrBase(std::move(other)) {
|
||||||
// Need to cast from U* to T* to do pointer adjustment in case of multiple
|
// Need to cast from U* to T* to do pointer adjustment in case of multiple
|
||||||
// inheritance. This also enforces the "U is a T" rule.
|
// inheritance. This also enforces the "U is a T" rule.
|
||||||
T* t = reinterpret_cast<U*>(other.ptr_);
|
T* t = reinterpret_cast<U*>(other.ptr_);
|
||||||
|
@ -192,12 +192,14 @@ constexpr ClampedNumeric<typename UnderlyingType<T>::type> MakeClampedNum(
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !BASE_NUMERICS_DISABLE_OSTREAM_OPERATORS
|
||||||
// Overload the ostream output operator to make logging work nicely.
|
// Overload the ostream output operator to make logging work nicely.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
std::ostream& operator<<(std::ostream& os, const ClampedNumeric<T>& value) {
|
std::ostream& operator<<(std::ostream& os, const ClampedNumeric<T>& value) {
|
||||||
os << static_cast<T>(value);
|
os << static_cast<T>(value);
|
||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// These implement the variadic wrapper for the math operations.
|
// These implement the variadic wrapper for the math operations.
|
||||||
template <template <typename, typename, typename> class M,
|
template <template <typename, typename, typename> class M,
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <ostream>
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
#include "base/numerics/safe_conversions_impl.h"
|
#include "base/numerics/safe_conversions_impl.h"
|
||||||
@ -20,6 +19,10 @@
|
|||||||
#define BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS (0)
|
#define BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if !BASE_NUMERICS_DISABLE_OSTREAM_OPERATORS
|
||||||
|
#include <ostream>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace base {
|
namespace base {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
@ -308,12 +311,14 @@ constexpr StrictNumeric<typename UnderlyingType<T>::type> MakeStrictNum(
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !BASE_NUMERICS_DISABLE_OSTREAM_OPERATORS
|
||||||
// Overload the ostream output operator to make logging work nicely.
|
// Overload the ostream output operator to make logging work nicely.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
std::ostream& operator<<(std::ostream& os, const StrictNumeric<T>& value) {
|
std::ostream& operator<<(std::ostream& os, const StrictNumeric<T>& value) {
|
||||||
os << static_cast<T>(value);
|
os << static_cast<T>(value);
|
||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#define BASE_NUMERIC_COMPARISON_OPERATORS(CLASS, NAME, OP) \
|
#define BASE_NUMERIC_COMPARISON_OPERATORS(CLASS, NAME, OP) \
|
||||||
template <typename L, typename R, \
|
template <typename L, typename R, \
|
||||||
|
@ -80,8 +80,9 @@ template <typename T>
|
|||||||
constexpr typename std::make_unsigned<T>::type SafeUnsignedAbs(T value) {
|
constexpr typename std::make_unsigned<T>::type SafeUnsignedAbs(T value) {
|
||||||
static_assert(std::is_integral<T>::value, "Type must be integral");
|
static_assert(std::is_integral<T>::value, "Type must be integral");
|
||||||
using UnsignedT = typename std::make_unsigned<T>::type;
|
using UnsignedT = typename std::make_unsigned<T>::type;
|
||||||
return IsValueNegative(value) ? 0 - static_cast<UnsignedT>(value)
|
return IsValueNegative(value)
|
||||||
: static_cast<UnsignedT>(value);
|
? static_cast<UnsignedT>(0u - static_cast<UnsignedT>(value))
|
||||||
|
: static_cast<UnsignedT>(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This allows us to switch paths on known compile-time constants.
|
// This allows us to switch paths on known compile-time constants.
|
||||||
|
@ -17,12 +17,15 @@
|
|||||||
|
|
||||||
#include "base/numerics/safe_conversions.h"
|
#include "base/numerics/safe_conversions.h"
|
||||||
|
|
||||||
|
#ifdef __asmjs__
|
||||||
|
// Optimized safe math instructions are incompatible with asmjs.
|
||||||
|
#define BASE_HAS_OPTIMIZED_SAFE_MATH (0)
|
||||||
// Where available use builtin math overflow support on Clang and GCC.
|
// Where available use builtin math overflow support on Clang and GCC.
|
||||||
#if !defined(__native_client__) && \
|
#elif !defined(__native_client__) && \
|
||||||
((defined(__clang__) && \
|
((defined(__clang__) && \
|
||||||
((__clang_major__ > 3) || \
|
((__clang_major__ > 3) || \
|
||||||
(__clang_major__ == 3 && __clang_minor__ >= 4))) || \
|
(__clang_major__ == 3 && __clang_minor__ >= 4))) || \
|
||||||
(defined(__GNUC__) && __GNUC__ >= 5))
|
(defined(__GNUC__) && __GNUC__ >= 5))
|
||||||
#include "base/numerics/safe_math_clang_gcc_impl.h"
|
#include "base/numerics/safe_math_clang_gcc_impl.h"
|
||||||
#define BASE_HAS_OPTIMIZED_SAFE_MATH (1)
|
#define BASE_HAS_OPTIMIZED_SAFE_MATH (1)
|
||||||
#else
|
#else
|
||||||
|
@ -5,12 +5,12 @@
|
|||||||
#ifndef BASE_OPTIONAL_H_
|
#ifndef BASE_OPTIONAL_H_
|
||||||
#define BASE_OPTIONAL_H_
|
#define BASE_OPTIONAL_H_
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
#include "base/template_util.h"
|
#include "base/template_util.h"
|
||||||
#include "base/thread_annotations.h"
|
|
||||||
|
|
||||||
namespace base {
|
namespace base {
|
||||||
|
|
||||||
@ -270,9 +270,7 @@ class OptionalBase {
|
|||||||
storage_.Init(std::forward<U>(value));
|
storage_.Init(std::forward<U>(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(lukasza): Figure out how to remove the NO_THREAD_SAFETY_ANALYSIS
|
void FreeIfNeeded() {
|
||||||
// annotation below. See https://crbug.com/881875#c1 for details.
|
|
||||||
void FreeIfNeeded() NO_THREAD_SAFETY_ANALYSIS {
|
|
||||||
if (!storage_.is_populated_)
|
if (!storage_.is_populated_)
|
||||||
return;
|
return;
|
||||||
storage_.value_.~T();
|
storage_.value_.~T();
|
||||||
@ -429,6 +427,28 @@ class OPTIONAL_DECLSPEC_EMPTY_BASES Optional
|
|||||||
std::is_copy_assignable<T>::value>,
|
std::is_copy_assignable<T>::value>,
|
||||||
public internal::MoveAssignable<std::is_move_constructible<T>::value &&
|
public internal::MoveAssignable<std::is_move_constructible<T>::value &&
|
||||||
std::is_move_assignable<T>::value> {
|
std::is_move_assignable<T>::value> {
|
||||||
|
private:
|
||||||
|
// Disable some versions of T that are ill-formed.
|
||||||
|
// See: https://timsong-cpp.github.io/cppwp/n4659/optional#syn-1
|
||||||
|
static_assert(
|
||||||
|
!std::is_same<internal::RemoveCvRefT<T>, in_place_t>::value,
|
||||||
|
"instantiation of base::Optional with in_place_t is ill-formed");
|
||||||
|
static_assert(!std::is_same<internal::RemoveCvRefT<T>, nullopt_t>::value,
|
||||||
|
"instantiation of base::Optional with nullopt_t is ill-formed");
|
||||||
|
static_assert(
|
||||||
|
!std::is_reference<T>::value,
|
||||||
|
"instantiation of base::Optional with a reference type is ill-formed");
|
||||||
|
// See: https://timsong-cpp.github.io/cppwp/n4659/optional#optional-3
|
||||||
|
static_assert(std::is_destructible<T>::value,
|
||||||
|
"instantiation of base::Optional with a non-destructible type "
|
||||||
|
"is ill-formed");
|
||||||
|
// Arrays are explicitly disallowed because for arrays of known bound
|
||||||
|
// is_destructible is of undefined value.
|
||||||
|
// See: https://en.cppreference.com/w/cpp/types/is_destructible
|
||||||
|
static_assert(
|
||||||
|
!std::is_array<T>::value,
|
||||||
|
"instantiation of base::Optional with an array type is ill-formed");
|
||||||
|
|
||||||
public:
|
public:
|
||||||
#undef OPTIONAL_DECLSPEC_EMPTY_BASES
|
#undef OPTIONAL_DECLSPEC_EMPTY_BASES
|
||||||
using value_type = T;
|
using value_type = T;
|
||||||
@ -570,32 +590,32 @@ class OPTIONAL_DECLSPEC_EMPTY_BASES Optional
|
|||||||
}
|
}
|
||||||
|
|
||||||
constexpr const T* operator->() const {
|
constexpr const T* operator->() const {
|
||||||
DCHECK(storage_.is_populated_);
|
CHECK(storage_.is_populated_);
|
||||||
return &storage_.value_;
|
return &storage_.value_;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr T* operator->() {
|
constexpr T* operator->() {
|
||||||
DCHECK(storage_.is_populated_);
|
CHECK(storage_.is_populated_);
|
||||||
return &storage_.value_;
|
return &storage_.value_;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr const T& operator*() const & {
|
constexpr const T& operator*() const & {
|
||||||
DCHECK(storage_.is_populated_);
|
CHECK(storage_.is_populated_);
|
||||||
return storage_.value_;
|
return storage_.value_;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr T& operator*() & {
|
constexpr T& operator*() & {
|
||||||
DCHECK(storage_.is_populated_);
|
CHECK(storage_.is_populated_);
|
||||||
return storage_.value_;
|
return storage_.value_;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr const T&& operator*() const && {
|
constexpr const T&& operator*() const && {
|
||||||
DCHECK(storage_.is_populated_);
|
CHECK(storage_.is_populated_);
|
||||||
return std::move(storage_.value_);
|
return std::move(storage_.value_);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr T&& operator*() && {
|
constexpr T&& operator*() && {
|
||||||
DCHECK(storage_.is_populated_);
|
CHECK(storage_.is_populated_);
|
||||||
return std::move(storage_.value_);
|
return std::move(storage_.value_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
128
security/sandbox/chromium/base/process/environment_internal.cc
Normal file
128
security/sandbox/chromium/base/process/environment_internal.cc
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
// Copyright 2019 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "base/process/environment_internal.h"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace base {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
#if defined(OS_POSIX) || defined(OS_FUCHSIA) || defined(OS_WIN)
|
||||||
|
// Parses a null-terminated input string of an environment block. The key is
|
||||||
|
// placed into the given string, and the total length of the line, including
|
||||||
|
// the terminating null, is returned.
|
||||||
|
size_t ParseEnvLine(const NativeEnvironmentString::value_type* input,
|
||||||
|
NativeEnvironmentString* key) {
|
||||||
|
// Skip to the equals or end of the string, this is the key.
|
||||||
|
size_t cur = 0;
|
||||||
|
while (input[cur] && input[cur] != '=')
|
||||||
|
cur++;
|
||||||
|
*key = NativeEnvironmentString(&input[0], cur);
|
||||||
|
|
||||||
|
// Now just skip to the end of the string.
|
||||||
|
while (input[cur])
|
||||||
|
cur++;
|
||||||
|
return cur + 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||||
|
|
||||||
|
std::unique_ptr<char* []> AlterEnvironment(const char* const* const env,
|
||||||
|
const EnvironmentMap& changes) {
|
||||||
|
std::string value_storage; // Holds concatenated null-terminated strings.
|
||||||
|
std::vector<size_t> result_indices; // Line indices into value_storage.
|
||||||
|
|
||||||
|
// First build up all of the unchanged environment strings. These are
|
||||||
|
// null-terminated of the form "key=value".
|
||||||
|
std::string key;
|
||||||
|
for (size_t i = 0; env[i]; i++) {
|
||||||
|
size_t line_length = ParseEnvLine(env[i], &key);
|
||||||
|
|
||||||
|
// Keep only values not specified in the change vector.
|
||||||
|
auto found_change = changes.find(key);
|
||||||
|
if (found_change == changes.end()) {
|
||||||
|
result_indices.push_back(value_storage.size());
|
||||||
|
value_storage.append(env[i], line_length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now append all modified and new values.
|
||||||
|
for (const auto& i : changes) {
|
||||||
|
if (!i.second.empty()) {
|
||||||
|
result_indices.push_back(value_storage.size());
|
||||||
|
value_storage.append(i.first);
|
||||||
|
value_storage.push_back('=');
|
||||||
|
value_storage.append(i.second);
|
||||||
|
value_storage.push_back(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t pointer_count_required =
|
||||||
|
result_indices.size() + 1 + // Null-terminated array of pointers.
|
||||||
|
(value_storage.size() + sizeof(char*) - 1) / sizeof(char*); // Buffer.
|
||||||
|
std::unique_ptr<char*[]> result(new char*[pointer_count_required]);
|
||||||
|
|
||||||
|
// The string storage goes after the array of pointers.
|
||||||
|
char* storage_data =
|
||||||
|
reinterpret_cast<char*>(&result.get()[result_indices.size() + 1]);
|
||||||
|
if (!value_storage.empty())
|
||||||
|
memcpy(storage_data, value_storage.data(), value_storage.size());
|
||||||
|
|
||||||
|
// Fill array of pointers at the beginning of the result.
|
||||||
|
for (size_t i = 0; i < result_indices.size(); i++)
|
||||||
|
result[i] = &storage_data[result_indices[i]];
|
||||||
|
result[result_indices.size()] = 0; // Null terminator.
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(OS_WIN)
|
||||||
|
|
||||||
|
NativeEnvironmentString AlterEnvironment(const wchar_t* env,
|
||||||
|
const EnvironmentMap& changes) {
|
||||||
|
NativeEnvironmentString result;
|
||||||
|
|
||||||
|
// First build up all of the unchanged environment strings.
|
||||||
|
const wchar_t* ptr = env;
|
||||||
|
while (*ptr) {
|
||||||
|
std::wstring key;
|
||||||
|
size_t line_length = ParseEnvLine(ptr, &key);
|
||||||
|
|
||||||
|
// Keep only values not specified in the change vector.
|
||||||
|
if (changes.find(key) == changes.end()) {
|
||||||
|
result.append(ptr, line_length);
|
||||||
|
}
|
||||||
|
ptr += line_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now append all modified and new values.
|
||||||
|
for (const auto& i : changes) {
|
||||||
|
// Windows environment blocks cannot handle keys or values with NULs.
|
||||||
|
CHECK_EQ(std::wstring::npos, i.first.find(L'\0'));
|
||||||
|
CHECK_EQ(std::wstring::npos, i.second.find(L'\0'));
|
||||||
|
if (!i.second.empty()) {
|
||||||
|
result += i.first;
|
||||||
|
result.push_back('=');
|
||||||
|
result += i.second;
|
||||||
|
result.push_back('\0');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the terminating NUL.
|
||||||
|
result.push_back('\0');
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // OS_POSIX || OS_FUCHSIA
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace base
|
@ -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 kDebuggerInactiveExitCode = 0xC0000354;
|
||||||
const DWORD kKeyboardInterruptExitCode = 0xC000013A;
|
const DWORD kKeyboardInterruptExitCode = 0xC000013A;
|
||||||
const DWORD kDebuggerTerminatedExitCode = 0x40010004;
|
const DWORD kDebuggerTerminatedExitCode = 0x40010004;
|
||||||
|
const DWORD kStatusInvalidImageHashExitCode = 0xC0000428;
|
||||||
|
|
||||||
// This exit code is used by the Windows task manager when it kills a
|
// This exit code is used by the Windows task manager when it kills a
|
||||||
// process. It's value is obviously not that unique, and it's
|
// process. It's value is obviously not that unique, and it's
|
||||||
@ -46,6 +47,7 @@ const DWORD kProcessKilledExitCode = 1;
|
|||||||
// exit code arguments to KillProcess*(), use platform/application
|
// exit code arguments to KillProcess*(), use platform/application
|
||||||
// specific values instead.
|
// specific values instead.
|
||||||
enum TerminationStatus {
|
enum TerminationStatus {
|
||||||
|
// clang-format off
|
||||||
TERMINATION_STATUS_NORMAL_TERMINATION, // zero exit status
|
TERMINATION_STATUS_NORMAL_TERMINATION, // zero exit status
|
||||||
TERMINATION_STATUS_ABNORMAL_TERMINATION, // non-zero exit status
|
TERMINATION_STATUS_ABNORMAL_TERMINATION, // non-zero exit status
|
||||||
TERMINATION_STATUS_PROCESS_WAS_KILLED, // e.g. SIGKILL or task manager kill
|
TERMINATION_STATUS_PROCESS_WAS_KILLED, // e.g. SIGKILL or task manager kill
|
||||||
@ -64,7 +66,12 @@ enum TerminationStatus {
|
|||||||
#endif
|
#endif
|
||||||
TERMINATION_STATUS_LAUNCH_FAILED, // child process never launched
|
TERMINATION_STATUS_LAUNCH_FAILED, // child process never launched
|
||||||
TERMINATION_STATUS_OOM, // Process died due to oom
|
TERMINATION_STATUS_OOM, // Process died due to oom
|
||||||
|
#if defined(OS_WIN)
|
||||||
|
// On Windows, the OS terminated process due to code integrity failure.
|
||||||
|
TERMINATION_STATUS_INTEGRITY_FAILURE,
|
||||||
|
#endif
|
||||||
TERMINATION_STATUS_MAX_ENUM
|
TERMINATION_STATUS_MAX_ENUM
|
||||||
|
// clang-format on
|
||||||
};
|
};
|
||||||
|
|
||||||
// Attempts to kill all the processes on the current machine that were launched
|
// Attempts to kill all the processes on the current machine that were launched
|
||||||
|
@ -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_
|
|
89
security/sandbox/chromium/base/process/memory.h
Normal file
89
security/sandbox/chromium/base/process/memory.h
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef BASE_PROCESS_MEMORY_H_
|
||||||
|
#define BASE_PROCESS_MEMORY_H_
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include "base/base_export.h"
|
||||||
|
#include "base/process/process_handle.h"
|
||||||
|
#include "build/build_config.h"
|
||||||
|
|
||||||
|
namespace base {
|
||||||
|
|
||||||
|
// Enables 'terminate on heap corruption' flag. Helps protect against heap
|
||||||
|
// overflow. Has no effect if the OS doesn't provide the necessary facility.
|
||||||
|
BASE_EXPORT void EnableTerminationOnHeapCorruption();
|
||||||
|
|
||||||
|
// Turns on process termination if memory runs out.
|
||||||
|
BASE_EXPORT void EnableTerminationOnOutOfMemory();
|
||||||
|
|
||||||
|
// Terminates process. Should be called only for out of memory errors.
|
||||||
|
// Crash reporting classifies such crashes as OOM.
|
||||||
|
BASE_EXPORT void TerminateBecauseOutOfMemory(size_t size);
|
||||||
|
|
||||||
|
#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_AIX)
|
||||||
|
BASE_EXPORT extern size_t g_oom_size;
|
||||||
|
|
||||||
|
// The maximum allowed value for the OOM score.
|
||||||
|
const int kMaxOomScore = 1000;
|
||||||
|
|
||||||
|
// This adjusts /proc/<pid>/oom_score_adj so the Linux OOM killer will
|
||||||
|
// prefer to kill certain process types over others. The range for the
|
||||||
|
// adjustment is [-1000, 1000], with [0, 1000] being user accessible.
|
||||||
|
// If the Linux system doesn't support the newer oom_score_adj range
|
||||||
|
// of [0, 1000], then we revert to using the older oom_adj, and
|
||||||
|
// translate the given value into [0, 15]. Some aliasing of values
|
||||||
|
// may occur in that case, of course.
|
||||||
|
BASE_EXPORT bool AdjustOOMScore(ProcessId process, int score);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
// Returns true if address-space was released. Some configurations reserve part
|
||||||
|
// of the process address-space for special allocations (e.g. WASM).
|
||||||
|
bool ReleaseAddressSpaceReservation();
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
#if defined(OS_WIN)
|
||||||
|
namespace win {
|
||||||
|
|
||||||
|
// Custom Windows exception code chosen to indicate an out of memory error.
|
||||||
|
// See https://msdn.microsoft.com/en-us/library/het71c37.aspx.
|
||||||
|
// "To make sure that you do not define a code that conflicts with an existing
|
||||||
|
// exception code" ... "The resulting error code should therefore have the
|
||||||
|
// highest four bits set to hexadecimal E."
|
||||||
|
// 0xe0000008 was chosen arbitrarily, as 0x00000008 is ERROR_NOT_ENOUGH_MEMORY.
|
||||||
|
const DWORD kOomExceptionCode = 0xe0000008;
|
||||||
|
|
||||||
|
} // namespace win
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// Handles out of memory, with the failed allocation |size|, or 0 when it is not
|
||||||
|
// known.
|
||||||
|
BASE_EXPORT void OnNoMemoryInternal(size_t size);
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
// Special allocator functions for callers that want to check for OOM.
|
||||||
|
// These will not abort if the allocation fails even if
|
||||||
|
// EnableTerminationOnOutOfMemory has been called.
|
||||||
|
// This can be useful for huge and/or unpredictable size memory allocations.
|
||||||
|
// Please only use this if you really handle the case when the allocation
|
||||||
|
// fails. Doing otherwise would risk security.
|
||||||
|
// These functions may still crash on OOM when running under memory tools,
|
||||||
|
// specifically ASan and other sanitizers.
|
||||||
|
// Return value tells whether the allocation succeeded. If it fails |result| is
|
||||||
|
// set to NULL, otherwise it holds the memory address.
|
||||||
|
BASE_EXPORT WARN_UNUSED_RESULT bool UncheckedMalloc(size_t size,
|
||||||
|
void** result);
|
||||||
|
BASE_EXPORT WARN_UNUSED_RESULT bool UncheckedCalloc(size_t num_items,
|
||||||
|
size_t size,
|
||||||
|
void** result);
|
||||||
|
|
||||||
|
} // namespace base
|
||||||
|
|
||||||
|
#endif // BASE_PROCESS_MEMORY_H_
|
@ -53,6 +53,46 @@ const ProcessId kNullProcessId = 0;
|
|||||||
#define CrPRIdPid "d"
|
#define CrPRIdPid "d"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
class UniqueProcId {
|
||||||
|
public:
|
||||||
|
explicit UniqueProcId(ProcessId value) : value_(value) {}
|
||||||
|
UniqueProcId(const UniqueProcId& other) = default;
|
||||||
|
UniqueProcId& operator=(const UniqueProcId& other) = default;
|
||||||
|
|
||||||
|
// Returns the process PID. WARNING: On some platforms, the pid may not be
|
||||||
|
// valid within the current process sandbox.
|
||||||
|
ProcessId GetUnsafeValue() const { return value_; }
|
||||||
|
|
||||||
|
bool operator==(const UniqueProcId& other) const {
|
||||||
|
return value_ == other.value_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const UniqueProcId& other) const {
|
||||||
|
return value_ != other.value_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<(const UniqueProcId& other) const {
|
||||||
|
return value_ < other.value_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<=(const UniqueProcId& other) const {
|
||||||
|
return value_ <= other.value_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator>(const UniqueProcId& other) const {
|
||||||
|
return value_ > other.value_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator>=(const UniqueProcId& other) const {
|
||||||
|
return value_ >= other.value_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ProcessId value_;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, const UniqueProcId& obj);
|
||||||
|
|
||||||
// Returns the id of the current process.
|
// Returns the id of the current process.
|
||||||
// Note that on some platforms, this is not guaranteed to be unique across
|
// Note that on some platforms, this is not guaranteed to be unique across
|
||||||
// processes (use GetUniqueIdForProcess if uniqueness is required).
|
// processes (use GetUniqueIdForProcess if uniqueness is required).
|
||||||
@ -60,9 +100,8 @@ BASE_EXPORT ProcessId GetCurrentProcId();
|
|||||||
|
|
||||||
// Returns a unique ID for the current process. The ID will be unique across all
|
// Returns a unique ID for the current process. The ID will be unique across all
|
||||||
// currently running processes within the chrome session, but IDs of terminated
|
// currently running processes within the chrome session, but IDs of terminated
|
||||||
// processes may be reused. This returns an opaque value that is different from
|
// processes may be reused.
|
||||||
// a process's PID.
|
BASE_EXPORT UniqueProcId GetUniqueIdForProcess();
|
||||||
BASE_EXPORT uint32_t GetUniqueIdForProcess();
|
|
||||||
|
|
||||||
#if defined(OS_LINUX)
|
#if defined(OS_LINUX)
|
||||||
// When a process is started in a different PID namespace from the browser
|
// When a process is started in a different PID namespace from the browser
|
||||||
|
@ -21,8 +21,14 @@ ProcessHandle GetCurrentProcessHandle() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ProcessId GetProcId(ProcessHandle process) {
|
ProcessId GetProcId(ProcessHandle process) {
|
||||||
|
if (process == base::kNullProcessHandle)
|
||||||
|
return 0;
|
||||||
// This returns 0 if we have insufficient rights to query the process handle.
|
// This returns 0 if we have insufficient rights to query the process handle.
|
||||||
return GetProcessId(process);
|
// Invalid handles or non-process handles will cause a hard failure.
|
||||||
|
ProcessId result = GetProcessId(process);
|
||||||
|
CHECK(result != 0 || GetLastError() != ERROR_INVALID_HANDLE)
|
||||||
|
<< "process handle = " << process;
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
ProcessId GetParentProcessId(ProcessHandle process) {
|
ProcessId GetParentProcessId(ProcessHandle process) {
|
||||||
|
@ -8,9 +8,12 @@
|
|||||||
#include "base/compiler_specific.h"
|
#include "base/compiler_specific.h"
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
#include "base/sequence_checker_impl.h"
|
#include "base/sequence_checker_impl.h"
|
||||||
|
#include "base/strings/string_piece.h"
|
||||||
|
#include "build/build_config.h"
|
||||||
|
|
||||||
// SequenceChecker is a helper class used to help verify that some methods of a
|
// SequenceChecker is a helper class used to help verify that some methods of a
|
||||||
// class are called sequentially (for thread-safety).
|
// class are called sequentially (for thread-safety). It supports thread safety
|
||||||
|
// annotations (see base/thread_annotations.h).
|
||||||
//
|
//
|
||||||
// Use the macros below instead of the SequenceChecker directly so that the
|
// Use the macros below instead of the SequenceChecker directly so that the
|
||||||
// unused member doesn't result in an extra byte (four when padded) per
|
// unused member doesn't result in an extra byte (four when padded) per
|
||||||
@ -42,20 +45,45 @@
|
|||||||
// void MyMethod() {
|
// void MyMethod() {
|
||||||
// DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
|
// DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
|
||||||
// ... (do stuff) ...
|
// ... (do stuff) ...
|
||||||
|
// MyOtherMethod();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// void MyOtherMethod()
|
||||||
|
// VALID_CONTEXT_REQUIRED(my_sequence_checker_) {
|
||||||
|
// foo_ = 42;
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// private:
|
// private:
|
||||||
|
// // GUARDED_BY_CONTEXT() enforces that this member is only
|
||||||
|
// // accessed from a scope that invokes DCHECK_CALLED_ON_VALID_SEQUENCE()
|
||||||
|
// // or from a function annotated with VALID_CONTEXT_REQUIRED(). A
|
||||||
|
// // DCHECK build will not compile if the member is accessed and these
|
||||||
|
// // conditions are not met.
|
||||||
|
// int foo_ GUARDED_BY_CONTEXT(my_sequence_checker_);
|
||||||
|
//
|
||||||
// SEQUENCE_CHECKER(my_sequence_checker_);
|
// SEQUENCE_CHECKER(my_sequence_checker_);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
#define SEQUENCE_CHECKER_INTERNAL_CONCAT2(a, b) a##b
|
||||||
|
#define SEQUENCE_CHECKER_INTERNAL_CONCAT(a, b) \
|
||||||
|
SEQUENCE_CHECKER_INTERNAL_CONCAT2(a, b)
|
||||||
|
#define SEQUENCE_CHECKER_INTERNAL_UID(prefix) \
|
||||||
|
SEQUENCE_CHECKER_INTERNAL_CONCAT(prefix, __LINE__)
|
||||||
|
|
||||||
#if DCHECK_IS_ON()
|
#if DCHECK_IS_ON()
|
||||||
#define SEQUENCE_CHECKER(name) base::SequenceChecker name
|
#define SEQUENCE_CHECKER(name) base::SequenceChecker name
|
||||||
#define DCHECK_CALLED_ON_VALID_SEQUENCE(name) \
|
#define DCHECK_CALLED_ON_VALID_SEQUENCE(name, ...) \
|
||||||
DCHECK((name).CalledOnValidSequence())
|
base::ScopedValidateSequenceChecker SEQUENCE_CHECKER_INTERNAL_UID( \
|
||||||
|
scoped_validate_sequence_checker_)(name, ##__VA_ARGS__);
|
||||||
#define DETACH_FROM_SEQUENCE(name) (name).DetachFromSequence()
|
#define DETACH_FROM_SEQUENCE(name) (name).DetachFromSequence()
|
||||||
#else // DCHECK_IS_ON()
|
#else // DCHECK_IS_ON()
|
||||||
|
#if __OBJC__ && defined(OS_IOS) && !HAS_FEATURE(objc_cxx_static_assert)
|
||||||
|
// TODO(thakis): Remove this branch once Xcode's clang has clang r356148.
|
||||||
#define SEQUENCE_CHECKER(name)
|
#define SEQUENCE_CHECKER(name)
|
||||||
#define DCHECK_CALLED_ON_VALID_SEQUENCE(name) EAT_STREAM_PARAMETERS
|
#else
|
||||||
|
#define SEQUENCE_CHECKER(name) static_assert(true, "")
|
||||||
|
#endif
|
||||||
|
#define DCHECK_CALLED_ON_VALID_SEQUENCE(name, ...) EAT_STREAM_PARAMETERS
|
||||||
#define DETACH_FROM_SEQUENCE(name)
|
#define DETACH_FROM_SEQUENCE(name)
|
||||||
#endif // DCHECK_IS_ON()
|
#endif // DCHECK_IS_ON()
|
||||||
|
|
||||||
@ -65,9 +93,18 @@ namespace base {
|
|||||||
//
|
//
|
||||||
// Note: You should almost always use the SequenceChecker class (through the
|
// Note: You should almost always use the SequenceChecker class (through the
|
||||||
// above macros) to get the right version for your build configuration.
|
// above macros) to get the right version for your build configuration.
|
||||||
class SequenceCheckerDoNothing {
|
// Note: This is only a check, not a "lock". It is marked "LOCKABLE" only in
|
||||||
|
// order to support thread_annotations.h.
|
||||||
|
class LOCKABLE SequenceCheckerDoNothing {
|
||||||
public:
|
public:
|
||||||
SequenceCheckerDoNothing() = default;
|
SequenceCheckerDoNothing() = default;
|
||||||
|
|
||||||
|
// Moving between matching sequences is allowed to help classes with
|
||||||
|
// SequenceCheckers that want a default move-construct/assign.
|
||||||
|
SequenceCheckerDoNothing(SequenceCheckerDoNothing&& other) = default;
|
||||||
|
SequenceCheckerDoNothing& operator=(SequenceCheckerDoNothing&& other) =
|
||||||
|
default;
|
||||||
|
|
||||||
bool CalledOnValidSequence() const WARN_UNUSED_RESULT { return true; }
|
bool CalledOnValidSequence() const WARN_UNUSED_RESULT { return true; }
|
||||||
void DetachFromSequence() {}
|
void DetachFromSequence() {}
|
||||||
|
|
||||||
@ -83,6 +120,24 @@ class SequenceChecker : public SequenceCheckerDoNothing {
|
|||||||
};
|
};
|
||||||
#endif // DCHECK_IS_ON()
|
#endif // DCHECK_IS_ON()
|
||||||
|
|
||||||
|
class SCOPED_LOCKABLE ScopedValidateSequenceChecker {
|
||||||
|
public:
|
||||||
|
explicit ScopedValidateSequenceChecker(const SequenceChecker& checker)
|
||||||
|
EXCLUSIVE_LOCK_FUNCTION(checker) {
|
||||||
|
DCHECK(checker.CalledOnValidSequence());
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit ScopedValidateSequenceChecker(const SequenceChecker& checker,
|
||||||
|
const StringPiece& msg)
|
||||||
|
EXCLUSIVE_LOCK_FUNCTION(checker) {
|
||||||
|
DCHECK(checker.CalledOnValidSequence()) << msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
~ScopedValidateSequenceChecker() UNLOCK_FUNCTION() {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace base
|
} // namespace base
|
||||||
|
|
||||||
#endif // BASE_SEQUENCE_CHECKER_H_
|
#endif // BASE_SEQUENCE_CHECKER_H_
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include "base/compiler_specific.h"
|
#include "base/compiler_specific.h"
|
||||||
#include "base/macros.h"
|
#include "base/macros.h"
|
||||||
#include "base/synchronization/lock.h"
|
#include "base/synchronization/lock.h"
|
||||||
|
#include "base/thread_annotations.h"
|
||||||
|
|
||||||
namespace base {
|
namespace base {
|
||||||
|
|
||||||
@ -20,11 +21,22 @@ namespace base {
|
|||||||
//
|
//
|
||||||
// Note: You should almost always use the SequenceChecker class to get the right
|
// Note: You should almost always use the SequenceChecker class to get the right
|
||||||
// version for your build configuration.
|
// version for your build configuration.
|
||||||
class BASE_EXPORT SequenceCheckerImpl {
|
// Note: This is only a check, not a "lock". It is marked "LOCKABLE" only in
|
||||||
|
// order to support thread_annotations.h.
|
||||||
|
class LOCKABLE BASE_EXPORT SequenceCheckerImpl {
|
||||||
public:
|
public:
|
||||||
SequenceCheckerImpl();
|
SequenceCheckerImpl();
|
||||||
~SequenceCheckerImpl();
|
~SequenceCheckerImpl();
|
||||||
|
|
||||||
|
// Allow move construct/assign. This must be called on |other|'s associated
|
||||||
|
// sequence and assignment can only be made into a SequenceCheckerImpl which
|
||||||
|
// is detached or already associated with the current sequence. This isn't
|
||||||
|
// thread-safe (|this| and |other| shouldn't be in use while this move is
|
||||||
|
// performed). If the assignment was legal, the resulting SequenceCheckerImpl
|
||||||
|
// will be bound to the current sequence and |other| will be detached.
|
||||||
|
SequenceCheckerImpl(SequenceCheckerImpl&& other);
|
||||||
|
SequenceCheckerImpl& operator=(SequenceCheckerImpl&& other);
|
||||||
|
|
||||||
// Returns true if called in sequence with previous calls to this method and
|
// Returns true if called in sequence with previous calls to this method and
|
||||||
// the constructor.
|
// the constructor.
|
||||||
bool CalledOnValidSequence() const WARN_UNUSED_RESULT;
|
bool CalledOnValidSequence() const WARN_UNUSED_RESULT;
|
||||||
@ -36,9 +48,12 @@ class BASE_EXPORT SequenceCheckerImpl {
|
|||||||
private:
|
private:
|
||||||
class Core;
|
class Core;
|
||||||
|
|
||||||
// Guards all variables below.
|
// Calls straight to ThreadLocalStorage::HasBeenDestroyed(). Exposed purely
|
||||||
|
// for 'friend' to work.
|
||||||
|
static bool HasThreadLocalStorageBeenDestroyed();
|
||||||
|
|
||||||
mutable Lock lock_;
|
mutable Lock lock_;
|
||||||
mutable std::unique_ptr<Core> core_;
|
mutable std::unique_ptr<Core> core_ GUARDED_BY(lock_);
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(SequenceCheckerImpl);
|
DISALLOW_COPY_AND_ASSIGN(SequenceCheckerImpl);
|
||||||
};
|
};
|
||||||
|
@ -152,6 +152,19 @@ class BASE_EXPORT SequencedTaskRunner : public TaskRunner {
|
|||||||
object.release());
|
object.release());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns true iff tasks posted to this TaskRunner are sequenced
|
||||||
|
// with this call.
|
||||||
|
//
|
||||||
|
// In particular:
|
||||||
|
// - Returns true if this is a SequencedTaskRunner to which the
|
||||||
|
// current task was posted.
|
||||||
|
// - Returns true if this is a SequencedTaskRunner bound to the
|
||||||
|
// same sequence as the SequencedTaskRunner to which the current
|
||||||
|
// task was posted.
|
||||||
|
// - Returns true if this is a SingleThreadTaskRunner bound to
|
||||||
|
// the current thread.
|
||||||
|
virtual bool RunsTasksInCurrentSequence() const = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
~SequencedTaskRunner() override = default;
|
~SequencedTaskRunner() override = default;
|
||||||
|
|
||||||
|
@ -17,12 +17,15 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
#include "base/optional.h"
|
#include "base/optional.h"
|
||||||
|
#include "base/template_util.h"
|
||||||
|
|
||||||
namespace base {
|
namespace base {
|
||||||
|
|
||||||
@ -39,6 +42,43 @@ void IterateAndEraseIf(Container& container, Predicate pred) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Iter>
|
||||||
|
constexpr bool IsRandomAccessIter =
|
||||||
|
std::is_same<typename std::iterator_traits<Iter>::iterator_category,
|
||||||
|
std::random_access_iterator_tag>::value;
|
||||||
|
|
||||||
|
// Utility type traits used for specializing base::Contains() below.
|
||||||
|
template <typename Container, typename Element, typename = void>
|
||||||
|
struct HasFindWithNpos : std::false_type {};
|
||||||
|
|
||||||
|
template <typename Container, typename Element>
|
||||||
|
struct HasFindWithNpos<
|
||||||
|
Container,
|
||||||
|
Element,
|
||||||
|
void_t<decltype(std::declval<const Container&>().find(
|
||||||
|
std::declval<const Element&>()) != Container::npos)>>
|
||||||
|
: std::true_type {};
|
||||||
|
|
||||||
|
template <typename Container, typename Element, typename = void>
|
||||||
|
struct HasFindWithEnd : std::false_type {};
|
||||||
|
|
||||||
|
template <typename Container, typename Element>
|
||||||
|
struct HasFindWithEnd<Container,
|
||||||
|
Element,
|
||||||
|
void_t<decltype(std::declval<const Container&>().find(
|
||||||
|
std::declval<const Element&>()) !=
|
||||||
|
std::declval<const Container&>().end())>>
|
||||||
|
: std::true_type {};
|
||||||
|
|
||||||
|
template <typename Container, typename Element, typename = void>
|
||||||
|
struct HasContains : std::false_type {};
|
||||||
|
|
||||||
|
template <typename Container, typename Element>
|
||||||
|
struct HasContains<Container,
|
||||||
|
Element,
|
||||||
|
void_t<decltype(std::declval<const Container&>().contains(
|
||||||
|
std::declval<const Element&>()))>> : std::true_type {};
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
// C++14 implementation of C++17's std::size():
|
// C++14 implementation of C++17's std::size():
|
||||||
@ -104,6 +144,30 @@ constexpr const T* data(std::initializer_list<T> il) noexcept {
|
|||||||
return il.begin();
|
return il.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// std::array::data() was not constexpr prior to C++17 [1].
|
||||||
|
// Hence these overloads are provided.
|
||||||
|
//
|
||||||
|
// [1] https://en.cppreference.com/w/cpp/container/array/data
|
||||||
|
template <typename T, size_t N>
|
||||||
|
constexpr T* data(std::array<T, N>& array) noexcept {
|
||||||
|
return !array.empty() ? &array[0] : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, size_t N>
|
||||||
|
constexpr const T* data(const std::array<T, N>& array) noexcept {
|
||||||
|
return !array.empty() ? &array[0] : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// C++14 implementation of C++17's std::as_const():
|
||||||
|
// https://en.cppreference.com/w/cpp/utility/as_const
|
||||||
|
template <typename T>
|
||||||
|
constexpr std::add_const_t<T>& as_const(T& t) noexcept {
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void as_const(const T&& t) = delete;
|
||||||
|
|
||||||
// Returns a const reference to the underlying container of a container adapter.
|
// Returns a const reference to the underlying container of a container adapter.
|
||||||
// Works for std::priority_queue, std::queue, and std::stack.
|
// Works for std::priority_queue, std::queue, and std::stack.
|
||||||
template <class A>
|
template <class A>
|
||||||
@ -134,39 +198,250 @@ STLCount(const Container& container, const T& val) {
|
|||||||
return std::count(container.begin(), container.end(), val);
|
return std::count(container.begin(), container.end(), val);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test to see if a set or map contains a particular key.
|
// General purpose implementation to check if |container| contains |value|.
|
||||||
// Returns true if the key is in the collection.
|
template <typename Container,
|
||||||
template <typename Collection, typename Key>
|
typename Value,
|
||||||
bool ContainsKey(const Collection& collection, const Key& key) {
|
std::enable_if_t<
|
||||||
return collection.find(key) != collection.end();
|
!internal::HasFindWithNpos<Container, Value>::value &&
|
||||||
|
!internal::HasFindWithEnd<Container, Value>::value &&
|
||||||
|
!internal::HasContains<Container, Value>::value>* = nullptr>
|
||||||
|
bool Contains(const Container& container, const Value& value) {
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
return std::find(begin(container), end(container), value) != end(container);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specialized Contains() implementation for when |container| has a find()
|
||||||
|
// member function and a static npos member, but no contains() member function.
|
||||||
|
template <typename Container,
|
||||||
|
typename Value,
|
||||||
|
std::enable_if_t<internal::HasFindWithNpos<Container, Value>::value &&
|
||||||
|
!internal::HasContains<Container, Value>::value>* =
|
||||||
|
nullptr>
|
||||||
|
bool Contains(const Container& container, const Value& value) {
|
||||||
|
return container.find(value) != Container::npos;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specialized Contains() implementation for when |container| has a find()
|
||||||
|
// and end() member function, but no contains() member function.
|
||||||
|
template <typename Container,
|
||||||
|
typename Value,
|
||||||
|
std::enable_if_t<internal::HasFindWithEnd<Container, Value>::value &&
|
||||||
|
!internal::HasContains<Container, Value>::value>* =
|
||||||
|
nullptr>
|
||||||
|
bool Contains(const Container& container, const Value& value) {
|
||||||
|
return container.find(value) != container.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specialized Contains() implementation for when |container| has a contains()
|
||||||
|
// member function.
|
||||||
|
template <
|
||||||
|
typename Container,
|
||||||
|
typename Value,
|
||||||
|
std::enable_if_t<internal::HasContains<Container, Value>::value>* = nullptr>
|
||||||
|
bool Contains(const Container& container, const Value& value) {
|
||||||
|
return container.contains(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// O(1) implementation of const casting an iterator for any sequence,
|
||||||
|
// associative or unordered associative container in the STL.
|
||||||
|
//
|
||||||
|
// Reference: https://stackoverflow.com/a/10669041
|
||||||
|
template <typename Container,
|
||||||
|
typename ConstIter,
|
||||||
|
std::enable_if_t<!internal::IsRandomAccessIter<ConstIter>>* = nullptr>
|
||||||
|
constexpr auto ConstCastIterator(Container& c, ConstIter it) {
|
||||||
|
return c.erase(it, it);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Explicit overload for std::forward_list where erase() is named erase_after().
|
||||||
|
template <typename T, typename Allocator>
|
||||||
|
constexpr auto ConstCastIterator(
|
||||||
|
std::forward_list<T, Allocator>& c,
|
||||||
|
typename std::forward_list<T, Allocator>::const_iterator it) {
|
||||||
|
// The erase_after(it, it) trick used below does not work for libstdc++ [1],
|
||||||
|
// thus we need a different way.
|
||||||
|
// TODO(crbug.com/972541): Remove this workaround once libstdc++ is fixed on all
|
||||||
|
// platforms.
|
||||||
|
//
|
||||||
|
// [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90857
|
||||||
|
#if defined(__GLIBCXX__)
|
||||||
|
return c.insert_after(it, {});
|
||||||
|
#else
|
||||||
|
return c.erase_after(it, it);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specialized O(1) const casting for random access iterators. This is
|
||||||
|
// necessary, because erase() is either not available (e.g. array-like
|
||||||
|
// containers), or has O(n) complexity (e.g. std::deque or std::vector).
|
||||||
|
template <typename Container,
|
||||||
|
typename ConstIter,
|
||||||
|
std::enable_if_t<internal::IsRandomAccessIter<ConstIter>>* = nullptr>
|
||||||
|
constexpr auto ConstCastIterator(Container& c, ConstIter it) {
|
||||||
|
using std::begin;
|
||||||
|
using std::cbegin;
|
||||||
|
return begin(c) + (it - cbegin(c));
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
template <typename Collection>
|
template <typename Map, typename Key, typename Value>
|
||||||
class HasKeyType {
|
std::pair<typename Map::iterator, bool> InsertOrAssignImpl(Map& map,
|
||||||
template <typename C>
|
Key&& key,
|
||||||
static std::true_type test(typename C::key_type*);
|
Value&& value) {
|
||||||
template <typename C>
|
auto lower = map.lower_bound(key);
|
||||||
static std::false_type test(...);
|
if (lower != map.end() && !map.key_comp()(key, lower->first)) {
|
||||||
|
// key already exists, perform assignment.
|
||||||
|
lower->second = std::forward<Value>(value);
|
||||||
|
return {lower, false};
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
// key did not yet exist, insert it.
|
||||||
static constexpr bool value = decltype(test<Collection>(nullptr))::value;
|
return {map.emplace_hint(lower, std::forward<Key>(key),
|
||||||
};
|
std::forward<Value>(value)),
|
||||||
|
true};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Map, typename Key, typename Value>
|
||||||
|
typename Map::iterator InsertOrAssignImpl(Map& map,
|
||||||
|
typename Map::const_iterator hint,
|
||||||
|
Key&& key,
|
||||||
|
Value&& value) {
|
||||||
|
auto&& key_comp = map.key_comp();
|
||||||
|
if ((hint == map.begin() || key_comp(std::prev(hint)->first, key))) {
|
||||||
|
if (hint == map.end() || key_comp(key, hint->first)) {
|
||||||
|
// *(hint - 1) < key < *hint => key did not exist and hint is correct.
|
||||||
|
return map.emplace_hint(hint, std::forward<Key>(key),
|
||||||
|
std::forward<Value>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!key_comp(hint->first, key)) {
|
||||||
|
// key == *hint => key already exists and hint is correct.
|
||||||
|
auto mutable_hint = ConstCastIterator(map, hint);
|
||||||
|
mutable_hint->second = std::forward<Value>(value);
|
||||||
|
return mutable_hint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// hint was not helpful, dispatch to hintless version.
|
||||||
|
return InsertOrAssignImpl(map, std::forward<Key>(key),
|
||||||
|
std::forward<Value>(value))
|
||||||
|
.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Map, typename Key, typename... Args>
|
||||||
|
std::pair<typename Map::iterator, bool> TryEmplaceImpl(Map& map,
|
||||||
|
Key&& key,
|
||||||
|
Args&&... args) {
|
||||||
|
auto lower = map.lower_bound(key);
|
||||||
|
if (lower != map.end() && !map.key_comp()(key, lower->first)) {
|
||||||
|
// key already exists, do nothing.
|
||||||
|
return {lower, false};
|
||||||
|
}
|
||||||
|
|
||||||
|
// key did not yet exist, insert it.
|
||||||
|
return {map.emplace_hint(lower, std::piecewise_construct,
|
||||||
|
std::forward_as_tuple(std::forward<Key>(key)),
|
||||||
|
std::forward_as_tuple(std::forward<Args>(args)...)),
|
||||||
|
true};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Map, typename Key, typename... Args>
|
||||||
|
typename Map::iterator TryEmplaceImpl(Map& map,
|
||||||
|
typename Map::const_iterator hint,
|
||||||
|
Key&& key,
|
||||||
|
Args&&... args) {
|
||||||
|
auto&& key_comp = map.key_comp();
|
||||||
|
if ((hint == map.begin() || key_comp(std::prev(hint)->first, key))) {
|
||||||
|
if (hint == map.end() || key_comp(key, hint->first)) {
|
||||||
|
// *(hint - 1) < key < *hint => key did not exist and hint is correct.
|
||||||
|
return map.emplace_hint(
|
||||||
|
hint, std::piecewise_construct,
|
||||||
|
std::forward_as_tuple(std::forward<Key>(key)),
|
||||||
|
std::forward_as_tuple(std::forward<Args>(args)...));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!key_comp(hint->first, key)) {
|
||||||
|
// key == *hint => no-op, return correct hint.
|
||||||
|
return ConstCastIterator(map, hint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// hint was not helpful, dispatch to hintless version.
|
||||||
|
return TryEmplaceImpl(map, std::forward<Key>(key),
|
||||||
|
std::forward<Args>(args)...)
|
||||||
|
.first;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
// Test to see if a collection like a vector contains a particular value.
|
// Implementation of C++17's std::map::insert_or_assign as a free function.
|
||||||
// Returns true if the value is in the collection.
|
template <typename Map, typename Value>
|
||||||
// Don't use this on collections such as sets or maps. This is enforced by
|
std::pair<typename Map::iterator, bool>
|
||||||
// disabling this method if the collection defines a key_type.
|
InsertOrAssign(Map& map, const typename Map::key_type& key, Value&& value) {
|
||||||
template <typename Collection,
|
return internal::InsertOrAssignImpl(map, key, std::forward<Value>(value));
|
||||||
typename Value,
|
}
|
||||||
typename std::enable_if<!internal::HasKeyType<Collection>::value,
|
|
||||||
int>::type = 0>
|
template <typename Map, typename Value>
|
||||||
bool ContainsValue(const Collection& collection, const Value& value) {
|
std::pair<typename Map::iterator, bool>
|
||||||
return std::find(std::begin(collection), std::end(collection), value) !=
|
InsertOrAssign(Map& map, typename Map::key_type&& key, Value&& value) {
|
||||||
std::end(collection);
|
return internal::InsertOrAssignImpl(map, std::move(key),
|
||||||
|
std::forward<Value>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implementation of C++17's std::map::insert_or_assign with hint as a free
|
||||||
|
// function.
|
||||||
|
template <typename Map, typename Value>
|
||||||
|
typename Map::iterator InsertOrAssign(Map& map,
|
||||||
|
typename Map::const_iterator hint,
|
||||||
|
const typename Map::key_type& key,
|
||||||
|
Value&& value) {
|
||||||
|
return internal::InsertOrAssignImpl(map, hint, key,
|
||||||
|
std::forward<Value>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Map, typename Value>
|
||||||
|
typename Map::iterator InsertOrAssign(Map& map,
|
||||||
|
typename Map::const_iterator hint,
|
||||||
|
typename Map::key_type&& key,
|
||||||
|
Value&& value) {
|
||||||
|
return internal::InsertOrAssignImpl(map, hint, std::move(key),
|
||||||
|
std::forward<Value>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implementation of C++17's std::map::try_emplace as a free function.
|
||||||
|
template <typename Map, typename... Args>
|
||||||
|
std::pair<typename Map::iterator, bool>
|
||||||
|
TryEmplace(Map& map, const typename Map::key_type& key, Args&&... args) {
|
||||||
|
return internal::TryEmplaceImpl(map, key, std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Map, typename... Args>
|
||||||
|
std::pair<typename Map::iterator, bool> TryEmplace(Map& map,
|
||||||
|
typename Map::key_type&& key,
|
||||||
|
Args&&... args) {
|
||||||
|
return internal::TryEmplaceImpl(map, std::move(key),
|
||||||
|
std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implementation of C++17's std::map::try_emplace with hint as a free
|
||||||
|
// function.
|
||||||
|
template <typename Map, typename... Args>
|
||||||
|
typename Map::iterator TryEmplace(Map& map,
|
||||||
|
typename Map::const_iterator hint,
|
||||||
|
const typename Map::key_type& key,
|
||||||
|
Args&&... args) {
|
||||||
|
return internal::TryEmplaceImpl(map, hint, key, std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Map, typename... Args>
|
||||||
|
typename Map::iterator TryEmplace(Map& map,
|
||||||
|
typename Map::const_iterator hint,
|
||||||
|
typename Map::key_type&& key,
|
||||||
|
Args&&... args) {
|
||||||
|
return internal::TryEmplaceImpl(map, hint, std::move(key),
|
||||||
|
std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if the container is sorted.
|
// Returns true if the container is sorted.
|
||||||
|
@ -13,10 +13,11 @@
|
|||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
|
#include "base/no_destructor.h"
|
||||||
#include "base/numerics/safe_math.h"
|
#include "base/numerics/safe_math.h"
|
||||||
#include "base/scoped_clear_last_error.h"
|
#include "base/strings/string_util.h"
|
||||||
#include "base/strings/utf_string_conversions.h"
|
#include "base/strings/utf_string_conversions.h"
|
||||||
#include "base/third_party/dmg_fp/dmg_fp.h"
|
#include "base/third_party/double_conversion/double-conversion/double-conversion.h"
|
||||||
|
|
||||||
namespace base {
|
namespace base {
|
||||||
|
|
||||||
@ -360,21 +361,29 @@ string16 NumberToString16(unsigned long long value) {
|
|||||||
return IntToStringT<string16, unsigned long long>::IntToString(value);
|
return IntToStringT<string16, unsigned long long>::IntToString(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const double_conversion::DoubleToStringConverter*
|
||||||
|
GetDoubleToStringConverter() {
|
||||||
|
static NoDestructor<double_conversion::DoubleToStringConverter> converter(
|
||||||
|
double_conversion::DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN,
|
||||||
|
nullptr, nullptr, 'e', -6, 12, 0, 0);
|
||||||
|
return converter.get();
|
||||||
|
}
|
||||||
|
|
||||||
std::string NumberToString(double value) {
|
std::string NumberToString(double value) {
|
||||||
// According to g_fmt.cc, it is sufficient to declare a buffer of size 32.
|
|
||||||
char buffer[32];
|
char buffer[32];
|
||||||
dmg_fp::g_fmt(buffer, value);
|
double_conversion::StringBuilder builder(buffer, sizeof(buffer));
|
||||||
return std::string(buffer);
|
GetDoubleToStringConverter()->ToShortest(value, &builder);
|
||||||
|
return std::string(buffer, builder.position());
|
||||||
}
|
}
|
||||||
|
|
||||||
base::string16 NumberToString16(double value) {
|
base::string16 NumberToString16(double value) {
|
||||||
// According to g_fmt.cc, it is sufficient to declare a buffer of size 32.
|
|
||||||
char buffer[32];
|
char buffer[32];
|
||||||
dmg_fp::g_fmt(buffer, value);
|
double_conversion::StringBuilder builder(buffer, sizeof(buffer));
|
||||||
|
GetDoubleToStringConverter()->ToShortest(value, &builder);
|
||||||
|
|
||||||
// The number will be ASCII. This creates the string using the "input
|
// The number will be ASCII. This creates the string using the "input
|
||||||
// iterator" variant which promotes from 8-bit to 16-bit via "=".
|
// iterator" variant which promotes from 8-bit to 16-bit via "=".
|
||||||
return base::string16(&buffer[0], &buffer[strlen(buffer)]);
|
return base::string16(&buffer[0], &buffer[builder.position()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StringToInt(StringPiece input, int* output) {
|
bool StringToInt(StringPiece input, int* output) {
|
||||||
@ -417,35 +426,37 @@ bool StringToSizeT(StringPiece16 input, size_t* output) {
|
|||||||
return String16ToIntImpl(input, output);
|
return String16ToIntImpl(input, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StringToDouble(const std::string& input, double* output) {
|
template <typename STRING, typename CHAR>
|
||||||
// Thread-safe? It is on at least Mac, Linux, and Windows.
|
bool StringToDoubleImpl(STRING input, const CHAR* data, double* output) {
|
||||||
internal::ScopedClearLastError clear_errno;
|
static NoDestructor<double_conversion::StringToDoubleConverter> converter(
|
||||||
|
double_conversion::StringToDoubleConverter::ALLOW_LEADING_SPACES |
|
||||||
|
double_conversion::StringToDoubleConverter::ALLOW_TRAILING_JUNK,
|
||||||
|
0.0, 0, nullptr, nullptr);
|
||||||
|
|
||||||
char* endptr = nullptr;
|
int processed_characters_count;
|
||||||
*output = dmg_fp::strtod(input.c_str(), &endptr);
|
*output = converter->StringToDouble(data, input.size(),
|
||||||
|
&processed_characters_count);
|
||||||
|
|
||||||
// Cases to return false:
|
// Cases to return false:
|
||||||
// - If errno is ERANGE, there was an overflow or underflow.
|
|
||||||
// - If the input string is empty, there was nothing to parse.
|
// - If the input string is empty, there was nothing to parse.
|
||||||
// - If endptr does not point to the end of the string, there are either
|
// - If the value saturated to HUGE_VAL.
|
||||||
// characters remaining in the string after a parsed number, or the string
|
// - If the entire string was not processed, there are either characters
|
||||||
// does not begin with a parseable number. endptr is compared to the
|
// remaining in the string after a parsed number, or the string does not
|
||||||
// expected end given the string's stated length to correctly catch cases
|
// begin with a parseable number.
|
||||||
// where the string contains embedded NUL characters.
|
|
||||||
// - If the first character is a space, there was leading whitespace
|
// - If the first character is a space, there was leading whitespace
|
||||||
return errno == 0 &&
|
return !input.empty() && *output != HUGE_VAL && *output != -HUGE_VAL &&
|
||||||
!input.empty() &&
|
static_cast<size_t>(processed_characters_count) == input.size() &&
|
||||||
input.c_str() + input.length() == endptr &&
|
!IsUnicodeWhitespace(input[0]);
|
||||||
!isspace(input[0]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: if you need to add String16ToDouble, first ask yourself if it's
|
bool StringToDouble(StringPiece input, double* output) {
|
||||||
// really necessary. If it is, probably the best implementation here is to
|
return StringToDoubleImpl(input, input.data(), output);
|
||||||
// convert to 8-bit and then use the 8-bit version.
|
}
|
||||||
|
|
||||||
// Note: if you need to add an iterator range version of StringToDouble, first
|
bool StringToDouble(StringPiece16 input, double* output) {
|
||||||
// ask yourself if it's really necessary. If it is, probably the best
|
return StringToDoubleImpl(
|
||||||
// implementation here is to instantiate a string and use the string version.
|
input, reinterpret_cast<const uint16_t*>(input.data()), output);
|
||||||
|
}
|
||||||
|
|
||||||
std::string HexEncode(const void* bytes, size_t size) {
|
std::string HexEncode(const void* bytes, size_t size) {
|
||||||
static const char kHexChars[] = "0123456789ABCDEF";
|
static const char kHexChars[] = "0123456789ABCDEF";
|
||||||
@ -461,6 +472,10 @@ std::string HexEncode(const void* bytes, size_t size) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string HexEncode(base::span<const uint8_t> bytes) {
|
||||||
|
return HexEncode(bytes.data(), bytes.size());
|
||||||
|
}
|
||||||
|
|
||||||
bool HexStringToInt(StringPiece input, int* output) {
|
bool HexStringToInt(StringPiece input, int* output) {
|
||||||
return IteratorRangeToNumber<HexIteratorRangeToIntTraits>::Invoke(
|
return IteratorRangeToNumber<HexIteratorRangeToIntTraits>::Invoke(
|
||||||
input.begin(), input.end(), output);
|
input.begin(), input.end(), output);
|
||||||
@ -481,7 +496,8 @@ bool HexStringToUInt64(StringPiece input, uint64_t* output) {
|
|||||||
input.begin(), input.end(), output);
|
input.begin(), input.end(), output);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HexStringToBytes(StringPiece input, std::vector<uint8_t>* output) {
|
template <typename Container>
|
||||||
|
static bool HexStringToByteContainer(StringPiece input, Container* output) {
|
||||||
DCHECK_EQ(output->size(), 0u);
|
DCHECK_EQ(output->size(), 0u);
|
||||||
size_t count = input.size();
|
size_t count = input.size();
|
||||||
if (count == 0 || (count % 2) != 0)
|
if (count == 0 || (count % 2) != 0)
|
||||||
@ -498,4 +514,32 @@ bool HexStringToBytes(StringPiece input, std::vector<uint8_t>* output) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool HexStringToBytes(StringPiece input, std::vector<uint8_t>* output) {
|
||||||
|
return HexStringToByteContainer(input, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HexStringToString(StringPiece input, std::string* output) {
|
||||||
|
return HexStringToByteContainer(input, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HexStringToSpan(StringPiece input, base::span<uint8_t> output) {
|
||||||
|
size_t count = input.size();
|
||||||
|
if (count == 0 || (count % 2) != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (count / 2 != output.size())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (uintptr_t i = 0; i < count / 2; ++i) {
|
||||||
|
uint8_t msb = 0; // most significant 4 bits
|
||||||
|
uint8_t lsb = 0; // least significant 4 bits
|
||||||
|
if (!CharToDigit<16>(input[i * 2], &msb) ||
|
||||||
|
!CharToDigit<16>(input[i * 2 + 1], &lsb)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
output[i] = (msb << 4) | lsb;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace base
|
} // namespace base
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "base/base_export.h"
|
#include "base/base_export.h"
|
||||||
|
#include "base/containers/span.h"
|
||||||
#include "base/strings/string16.h"
|
#include "base/strings/string16.h"
|
||||||
#include "base/strings/string_piece.h"
|
#include "base/strings/string_piece.h"
|
||||||
#include "build/build_config.h"
|
#include "build/build_config.h"
|
||||||
@ -56,29 +57,6 @@ BASE_EXPORT string16 NumberToString16(unsigned long long value);
|
|||||||
BASE_EXPORT std::string NumberToString(double value);
|
BASE_EXPORT std::string NumberToString(double value);
|
||||||
BASE_EXPORT string16 NumberToString16(double value);
|
BASE_EXPORT string16 NumberToString16(double value);
|
||||||
|
|
||||||
// Type-specific naming for backwards compatibility.
|
|
||||||
//
|
|
||||||
// TODO(brettw) these should be removed and callers converted to the overloaded
|
|
||||||
// "NumberToString" variant.
|
|
||||||
inline std::string IntToString(int value) {
|
|
||||||
return NumberToString(value);
|
|
||||||
}
|
|
||||||
inline string16 IntToString16(int value) {
|
|
||||||
return NumberToString16(value);
|
|
||||||
}
|
|
||||||
inline std::string UintToString(unsigned value) {
|
|
||||||
return NumberToString(value);
|
|
||||||
}
|
|
||||||
inline string16 UintToString16(unsigned value) {
|
|
||||||
return NumberToString16(value);
|
|
||||||
}
|
|
||||||
inline std::string Int64ToString(int64_t value) {
|
|
||||||
return NumberToString(value);
|
|
||||||
}
|
|
||||||
inline string16 Int64ToString16(int64_t value) {
|
|
||||||
return NumberToString16(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// String -> number conversions ------------------------------------------------
|
// String -> number conversions ------------------------------------------------
|
||||||
|
|
||||||
// Perform a best-effort conversion of the input string to a numeric type,
|
// Perform a best-effort conversion of the input string to a numeric type,
|
||||||
@ -120,7 +98,8 @@ BASE_EXPORT bool StringToSizeT(StringPiece16 input, size_t* output);
|
|||||||
// If your input is locale specific, use ICU to read the number.
|
// If your input is locale specific, use ICU to read the number.
|
||||||
// WARNING: Will write to |output| even when returning false.
|
// WARNING: Will write to |output| even when returning false.
|
||||||
// Read the comments here and above StringToInt() carefully.
|
// Read the comments here and above StringToInt() carefully.
|
||||||
BASE_EXPORT bool StringToDouble(const std::string& input, double* output);
|
BASE_EXPORT bool StringToDouble(StringPiece input, double* output);
|
||||||
|
BASE_EXPORT bool StringToDouble(StringPiece16 input, double* output);
|
||||||
|
|
||||||
// Hex encoding ----------------------------------------------------------------
|
// Hex encoding ----------------------------------------------------------------
|
||||||
|
|
||||||
@ -131,6 +110,7 @@ BASE_EXPORT bool StringToDouble(const std::string& input, double* output);
|
|||||||
// max size for |size| should be is
|
// max size for |size| should be is
|
||||||
// std::numeric_limits<size_t>::max() / 2
|
// std::numeric_limits<size_t>::max() / 2
|
||||||
BASE_EXPORT std::string HexEncode(const void* bytes, size_t size);
|
BASE_EXPORT std::string HexEncode(const void* bytes, size_t size);
|
||||||
|
BASE_EXPORT std::string HexEncode(base::span<const uint8_t> bytes);
|
||||||
|
|
||||||
// Best effort conversion, see StringToInt above for restrictions.
|
// Best effort conversion, see StringToInt above for restrictions.
|
||||||
// Will only successful parse hex values that will fit into |output|, i.e.
|
// Will only successful parse hex values that will fit into |output|, i.e.
|
||||||
@ -161,6 +141,17 @@ BASE_EXPORT bool HexStringToUInt64(StringPiece input, uint64_t* output);
|
|||||||
BASE_EXPORT bool HexStringToBytes(StringPiece input,
|
BASE_EXPORT bool HexStringToBytes(StringPiece input,
|
||||||
std::vector<uint8_t>* output);
|
std::vector<uint8_t>* output);
|
||||||
|
|
||||||
|
// Same as HexStringToBytes, but for an std::string.
|
||||||
|
BASE_EXPORT bool HexStringToString(StringPiece input, std::string* output);
|
||||||
|
|
||||||
|
// Decodes the hex string |input| into a presized |output|. The output buffer
|
||||||
|
// must be sized exactly to |input.size() / 2| or decoding will fail and no
|
||||||
|
// bytes will be written to |output|. Decoding an empty input is also
|
||||||
|
// considered a failure. When decoding fails due to encountering invalid input
|
||||||
|
// characters, |output| will have been filled with the decoded bytes up until
|
||||||
|
// the failure.
|
||||||
|
BASE_EXPORT bool HexStringToSpan(StringPiece input, base::span<uint8_t> output);
|
||||||
|
|
||||||
} // namespace base
|
} // namespace base
|
||||||
|
|
||||||
#endif // BASE_STRINGS_STRING_NUMBER_CONVERSIONS_H_
|
#endif // BASE_STRINGS_STRING_NUMBER_CONVERSIONS_H_
|
||||||
|
@ -52,22 +52,6 @@ std::ostream& operator<<(std::ostream& o, const StringPiece16& piece) {
|
|||||||
|
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
template<typename STR>
|
|
||||||
void CopyToStringT(const BasicStringPiece<STR>& self, STR* target) {
|
|
||||||
if (self.empty())
|
|
||||||
target->clear();
|
|
||||||
else
|
|
||||||
target->assign(self.data(), self.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
void CopyToString(const StringPiece& self, std::string* target) {
|
|
||||||
CopyToStringT(self, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CopyToString(const StringPiece16& self, string16* target) {
|
|
||||||
CopyToStringT(self, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename STR>
|
template<typename STR>
|
||||||
void AppendToStringT(const BasicStringPiece<STR>& self, STR* target) {
|
void AppendToStringT(const BasicStringPiece<STR>& self, STR* target) {
|
||||||
if (!self.empty())
|
if (!self.empty())
|
||||||
@ -219,8 +203,11 @@ size_t find_first_of(const StringPiece& self,
|
|||||||
size_t find_first_of(const StringPiece16& self,
|
size_t find_first_of(const StringPiece16& self,
|
||||||
const StringPiece16& s,
|
const StringPiece16& s,
|
||||||
size_t pos) {
|
size_t pos) {
|
||||||
|
// Use the faster std::find() if searching for a single character.
|
||||||
StringPiece16::const_iterator found =
|
StringPiece16::const_iterator found =
|
||||||
std::find_first_of(self.begin() + pos, self.end(), s.begin(), s.end());
|
s.size() == 1 ? std::find(self.begin() + pos, self.end(), s[0])
|
||||||
|
: std::find_first_of(self.begin() + pos, self.end(),
|
||||||
|
s.begin(), s.end());
|
||||||
if (found == self.end())
|
if (found == self.end())
|
||||||
return StringPiece16::npos;
|
return StringPiece16::npos;
|
||||||
return found - self.begin();
|
return found - self.begin();
|
||||||
@ -435,16 +422,5 @@ StringPiece16 substr(const StringPiece16& self,
|
|||||||
return substrT(self, pos, n);
|
return substrT(self, pos, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DCHECK_IS_ON()
|
|
||||||
void AssertIteratorsInOrder(std::string::const_iterator begin,
|
|
||||||
std::string::const_iterator end) {
|
|
||||||
DCHECK(begin <= end) << "StringPiece iterators swapped or invalid.";
|
|
||||||
}
|
|
||||||
void AssertIteratorsInOrder(string16::const_iterator begin,
|
|
||||||
string16::const_iterator end) {
|
|
||||||
DCHECK(begin <= end) << "StringPiece iterators swapped or invalid.";
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace base
|
} // namespace base
|
||||||
|
@ -47,9 +47,6 @@ namespace base {
|
|||||||
// template internal to the .cc file.
|
// template internal to the .cc file.
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
BASE_EXPORT void CopyToString(const StringPiece& self, std::string* target);
|
|
||||||
BASE_EXPORT void CopyToString(const StringPiece16& self, string16* target);
|
|
||||||
|
|
||||||
BASE_EXPORT void AppendToString(const StringPiece& self, std::string* target);
|
BASE_EXPORT void AppendToString(const StringPiece& self, std::string* target);
|
||||||
BASE_EXPORT void AppendToString(const StringPiece16& self, string16* target);
|
BASE_EXPORT void AppendToString(const StringPiece16& self, string16* target);
|
||||||
|
|
||||||
@ -141,21 +138,12 @@ BASE_EXPORT StringPiece16 substr(const StringPiece16& self,
|
|||||||
size_t pos,
|
size_t pos,
|
||||||
size_t n);
|
size_t n);
|
||||||
|
|
||||||
#if DCHECK_IS_ON()
|
|
||||||
// Asserts that begin <= end to catch some errors with iterator usage.
|
|
||||||
BASE_EXPORT void AssertIteratorsInOrder(std::string::const_iterator begin,
|
|
||||||
std::string::const_iterator end);
|
|
||||||
BASE_EXPORT void AssertIteratorsInOrder(string16::const_iterator begin,
|
|
||||||
string16::const_iterator end);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
// BasicStringPiece ------------------------------------------------------------
|
// BasicStringPiece ------------------------------------------------------------
|
||||||
|
|
||||||
// Defines the types, methods, operators, and data members common to both
|
// Defines the types, methods, operators, and data members common to both
|
||||||
// StringPiece and StringPiece16. Do not refer to this class directly, but
|
// StringPiece and StringPiece16.
|
||||||
// rather to BasicStringPiece, StringPiece, or StringPiece16.
|
|
||||||
//
|
//
|
||||||
// This is templatized by string class type rather than character type, so
|
// This is templatized by string class type rather than character type, so
|
||||||
// BasicStringPiece<std::string> or BasicStringPiece<base::string16>.
|
// BasicStringPiece<std::string> or BasicStringPiece<base::string16>.
|
||||||
@ -190,11 +178,7 @@ template <typename STRING_TYPE> class BasicStringPiece {
|
|||||||
: ptr_(offset), length_(len) {}
|
: ptr_(offset), length_(len) {}
|
||||||
BasicStringPiece(const typename STRING_TYPE::const_iterator& begin,
|
BasicStringPiece(const typename STRING_TYPE::const_iterator& begin,
|
||||||
const typename STRING_TYPE::const_iterator& end) {
|
const typename STRING_TYPE::const_iterator& end) {
|
||||||
#if DCHECK_IS_ON()
|
DCHECK(begin <= end) << "StringPiece iterators swapped or invalid.";
|
||||||
// This assertion is done out-of-line to avoid bringing in logging.h and
|
|
||||||
// instantiating logging macros for every instantiation.
|
|
||||||
internal::AssertIteratorsInOrder(begin, end);
|
|
||||||
#endif
|
|
||||||
length_ = static_cast<size_t>(std::distance(begin, end));
|
length_ = static_cast<size_t>(std::distance(begin, end));
|
||||||
|
|
||||||
// The length test before assignment is to avoid dereferencing an iterator
|
// The length test before assignment is to avoid dereferencing an iterator
|
||||||
@ -211,19 +195,6 @@ template <typename STRING_TYPE> class BasicStringPiece {
|
|||||||
constexpr size_type length() const noexcept { return length_; }
|
constexpr size_type length() const noexcept { return length_; }
|
||||||
bool empty() const { return length_ == 0; }
|
bool empty() const { return length_ == 0; }
|
||||||
|
|
||||||
void clear() {
|
|
||||||
ptr_ = NULL;
|
|
||||||
length_ = 0;
|
|
||||||
}
|
|
||||||
void set(const value_type* data, size_type len) {
|
|
||||||
ptr_ = data;
|
|
||||||
length_ = len;
|
|
||||||
}
|
|
||||||
void set(const value_type* str) {
|
|
||||||
ptr_ = str;
|
|
||||||
length_ = str ? STRING_TYPE::traits_type::length(str) : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr value_type operator[](size_type i) const {
|
constexpr value_type operator[](size_type i) const {
|
||||||
CHECK(i < length_);
|
CHECK(i < length_);
|
||||||
return ptr_[i];
|
return ptr_[i];
|
||||||
@ -280,12 +251,6 @@ template <typename STRING_TYPE> class BasicStringPiece {
|
|||||||
size_type max_size() const { return length_; }
|
size_type max_size() const { return length_; }
|
||||||
size_type capacity() const { return length_; }
|
size_type capacity() const { return length_; }
|
||||||
|
|
||||||
// Sets the value of the given string target type to be the current string.
|
|
||||||
// This saves a temporary over doing |a = b.as_string()|
|
|
||||||
void CopyToString(STRING_TYPE* target) const {
|
|
||||||
internal::CopyToString(*this, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AppendToString(STRING_TYPE* target) const {
|
void AppendToString(STRING_TYPE* target) const {
|
||||||
internal::AppendToString(*this, target);
|
internal::AppendToString(*this, target);
|
||||||
}
|
}
|
||||||
|
@ -14,27 +14,15 @@ namespace base {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// PieceToOutputType converts a StringPiece as needed to a given output type,
|
|
||||||
// which is either the same type of StringPiece (a NOP) or the corresponding
|
|
||||||
// non-piece string type.
|
|
||||||
//
|
|
||||||
// The default converter is a NOP, it works when the OutputType is the
|
|
||||||
// correct StringPiece.
|
|
||||||
template<typename Str, typename OutputType>
|
|
||||||
OutputType PieceToOutputType(BasicStringPiece<Str> piece) {
|
|
||||||
return piece;
|
|
||||||
}
|
|
||||||
template<> // Convert StringPiece to std::string
|
|
||||||
std::string PieceToOutputType<std::string, std::string>(StringPiece piece) {
|
|
||||||
return piece.as_string();
|
|
||||||
}
|
|
||||||
template<> // Convert StringPiece16 to string16.
|
|
||||||
string16 PieceToOutputType<string16, string16>(StringPiece16 piece) {
|
|
||||||
return piece.as_string();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns either the ASCII or UTF-16 whitespace.
|
// Returns either the ASCII or UTF-16 whitespace.
|
||||||
template<typename Str> BasicStringPiece<Str> WhitespaceForType();
|
template<typename Str> BasicStringPiece<Str> WhitespaceForType();
|
||||||
|
#if defined(OS_WIN) && defined(BASE_STRING16_IS_STD_U16STRING)
|
||||||
|
template <>
|
||||||
|
WStringPiece WhitespaceForType<std::wstring>() {
|
||||||
|
return kWhitespaceWide;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
template<> StringPiece16 WhitespaceForType<string16>() {
|
template<> StringPiece16 WhitespaceForType<string16>() {
|
||||||
return kWhitespaceUTF16;
|
return kWhitespaceUTF16;
|
||||||
}
|
}
|
||||||
@ -42,37 +30,12 @@ template<> StringPiece WhitespaceForType<std::string>() {
|
|||||||
return kWhitespaceASCII;
|
return kWhitespaceASCII;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optimize the single-character case to call find() on the string instead,
|
|
||||||
// since this is the common case and can be made faster. This could have been
|
|
||||||
// done with template specialization too, but would have been less clear.
|
|
||||||
//
|
|
||||||
// There is no corresponding FindFirstNotOf because StringPiece already
|
|
||||||
// implements these different versions that do the optimized searching.
|
|
||||||
size_t FindFirstOf(StringPiece piece, char c, size_t pos) {
|
|
||||||
return piece.find(c, pos);
|
|
||||||
}
|
|
||||||
size_t FindFirstOf(StringPiece16 piece, char16 c, size_t pos) {
|
|
||||||
return piece.find(c, pos);
|
|
||||||
}
|
|
||||||
size_t FindFirstOf(StringPiece piece, StringPiece one_of, size_t pos) {
|
|
||||||
return piece.find_first_of(one_of, pos);
|
|
||||||
}
|
|
||||||
size_t FindFirstOf(StringPiece16 piece, StringPiece16 one_of, size_t pos) {
|
|
||||||
return piece.find_first_of(one_of, pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
// General string splitter template. Can take 8- or 16-bit input, can produce
|
// General string splitter template. Can take 8- or 16-bit input, can produce
|
||||||
// the corresponding string or StringPiece output, and can take single- or
|
// the corresponding string or StringPiece output.
|
||||||
// multiple-character delimiters.
|
template <typename OutputStringType, typename Str>
|
||||||
//
|
|
||||||
// DelimiterType is either a character (Str::value_type) or a string piece of
|
|
||||||
// multiple characters (BasicStringPiece<Str>). StringPiece has a version of
|
|
||||||
// find for both of these cases, and the single-character version is the most
|
|
||||||
// common and can be implemented faster, which is why this is a template.
|
|
||||||
template<typename Str, typename OutputStringType, typename DelimiterType>
|
|
||||||
static std::vector<OutputStringType> SplitStringT(
|
static std::vector<OutputStringType> SplitStringT(
|
||||||
BasicStringPiece<Str> str,
|
BasicStringPiece<Str> str,
|
||||||
DelimiterType delimiter,
|
BasicStringPiece<Str> delimiter,
|
||||||
WhitespaceHandling whitespace,
|
WhitespaceHandling whitespace,
|
||||||
SplitResult result_type) {
|
SplitResult result_type) {
|
||||||
std::vector<OutputStringType> result;
|
std::vector<OutputStringType> result;
|
||||||
@ -81,7 +44,7 @@ static std::vector<OutputStringType> SplitStringT(
|
|||||||
|
|
||||||
size_t start = 0;
|
size_t start = 0;
|
||||||
while (start != Str::npos) {
|
while (start != Str::npos) {
|
||||||
size_t end = FindFirstOf(str, delimiter, start);
|
size_t end = str.find_first_of(delimiter, start);
|
||||||
|
|
||||||
BasicStringPiece<Str> piece;
|
BasicStringPiece<Str> piece;
|
||||||
if (end == Str::npos) {
|
if (end == Str::npos) {
|
||||||
@ -96,7 +59,7 @@ static std::vector<OutputStringType> SplitStringT(
|
|||||||
piece = TrimString(piece, WhitespaceForType<Str>(), TRIM_ALL);
|
piece = TrimString(piece, WhitespaceForType<Str>(), TRIM_ALL);
|
||||||
|
|
||||||
if (result_type == SPLIT_WANT_ALL || !piece.empty())
|
if (result_type == SPLIT_WANT_ALL || !piece.empty())
|
||||||
result.push_back(PieceToOutputType<Str, OutputStringType>(piece));
|
result.emplace_back(piece);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -115,7 +78,7 @@ bool AppendStringKeyValue(StringPiece input,
|
|||||||
DVLOG(1) << "cannot find delimiter in: " << input;
|
DVLOG(1) << "cannot find delimiter in: " << input;
|
||||||
return false; // No delimiter.
|
return false; // No delimiter.
|
||||||
}
|
}
|
||||||
input.substr(0, end_key_pos).CopyToString(&result_pair.first);
|
result_pair.first = std::string(input.substr(0, end_key_pos));
|
||||||
|
|
||||||
// Find the value string.
|
// Find the value string.
|
||||||
StringPiece remains = input.substr(end_key_pos, input.size() - end_key_pos);
|
StringPiece remains = input.substr(end_key_pos, input.size() - end_key_pos);
|
||||||
@ -124,22 +87,23 @@ bool AppendStringKeyValue(StringPiece input,
|
|||||||
DVLOG(1) << "cannot parse value from input: " << input;
|
DVLOG(1) << "cannot parse value from input: " << input;
|
||||||
return false; // No value.
|
return false; // No value.
|
||||||
}
|
}
|
||||||
remains.substr(begin_value_pos, remains.size() - begin_value_pos)
|
|
||||||
.CopyToString(&result_pair.second);
|
result_pair.second = std::string(
|
||||||
|
remains.substr(begin_value_pos, remains.size() - begin_value_pos));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Str, typename OutputStringType>
|
template <typename OutputStringType, typename Str>
|
||||||
void SplitStringUsingSubstrT(BasicStringPiece<Str> input,
|
std::vector<OutputStringType> SplitStringUsingSubstrT(
|
||||||
BasicStringPiece<Str> delimiter,
|
BasicStringPiece<Str> input,
|
||||||
WhitespaceHandling whitespace,
|
BasicStringPiece<Str> delimiter,
|
||||||
SplitResult result_type,
|
WhitespaceHandling whitespace,
|
||||||
std::vector<OutputStringType>* result) {
|
SplitResult result_type) {
|
||||||
using Piece = BasicStringPiece<Str>;
|
using Piece = BasicStringPiece<Str>;
|
||||||
using size_type = typename Piece::size_type;
|
using size_type = typename Piece::size_type;
|
||||||
|
|
||||||
result->clear();
|
std::vector<OutputStringType> result;
|
||||||
for (size_type begin_index = 0, end_index = 0; end_index != Piece::npos;
|
for (size_type begin_index = 0, end_index = 0; end_index != Piece::npos;
|
||||||
begin_index = end_index + delimiter.size()) {
|
begin_index = end_index + delimiter.size()) {
|
||||||
end_index = input.find(delimiter, begin_index);
|
end_index = input.find(delimiter, begin_index);
|
||||||
@ -151,8 +115,10 @@ void SplitStringUsingSubstrT(BasicStringPiece<Str> input,
|
|||||||
term = TrimString(term, WhitespaceForType<Str>(), TRIM_ALL);
|
term = TrimString(term, WhitespaceForType<Str>(), TRIM_ALL);
|
||||||
|
|
||||||
if (result_type == SPLIT_WANT_ALL || !term.empty())
|
if (result_type == SPLIT_WANT_ALL || !term.empty())
|
||||||
result->push_back(PieceToOutputType<Str, OutputStringType>(term));
|
result.emplace_back(term);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
@ -161,48 +127,29 @@ std::vector<std::string> SplitString(StringPiece input,
|
|||||||
StringPiece separators,
|
StringPiece separators,
|
||||||
WhitespaceHandling whitespace,
|
WhitespaceHandling whitespace,
|
||||||
SplitResult result_type) {
|
SplitResult result_type) {
|
||||||
if (separators.size() == 1) {
|
return SplitStringT<std::string>(input, separators, whitespace, result_type);
|
||||||
return SplitStringT<std::string, std::string, char>(
|
|
||||||
input, separators[0], whitespace, result_type);
|
|
||||||
}
|
|
||||||
return SplitStringT<std::string, std::string, StringPiece>(
|
|
||||||
input, separators, whitespace, result_type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<string16> SplitString(StringPiece16 input,
|
std::vector<string16> SplitString(StringPiece16 input,
|
||||||
StringPiece16 separators,
|
StringPiece16 separators,
|
||||||
WhitespaceHandling whitespace,
|
WhitespaceHandling whitespace,
|
||||||
SplitResult result_type) {
|
SplitResult result_type) {
|
||||||
if (separators.size() == 1) {
|
return SplitStringT<string16>(input, separators, whitespace, result_type);
|
||||||
return SplitStringT<string16, string16, char16>(
|
|
||||||
input, separators[0], whitespace, result_type);
|
|
||||||
}
|
|
||||||
return SplitStringT<string16, string16, StringPiece16>(
|
|
||||||
input, separators, whitespace, result_type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<StringPiece> SplitStringPiece(StringPiece input,
|
std::vector<StringPiece> SplitStringPiece(StringPiece input,
|
||||||
StringPiece separators,
|
StringPiece separators,
|
||||||
WhitespaceHandling whitespace,
|
WhitespaceHandling whitespace,
|
||||||
SplitResult result_type) {
|
SplitResult result_type) {
|
||||||
if (separators.size() == 1) {
|
return SplitStringT<StringPiece>(input, separators, whitespace, result_type);
|
||||||
return SplitStringT<std::string, StringPiece, char>(
|
|
||||||
input, separators[0], whitespace, result_type);
|
|
||||||
}
|
|
||||||
return SplitStringT<std::string, StringPiece, StringPiece>(
|
|
||||||
input, separators, whitespace, result_type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<StringPiece16> SplitStringPiece(StringPiece16 input,
|
std::vector<StringPiece16> SplitStringPiece(StringPiece16 input,
|
||||||
StringPiece16 separators,
|
StringPiece16 separators,
|
||||||
WhitespaceHandling whitespace,
|
WhitespaceHandling whitespace,
|
||||||
SplitResult result_type) {
|
SplitResult result_type) {
|
||||||
if (separators.size() == 1) {
|
return SplitStringT<StringPiece16>(input, separators, whitespace,
|
||||||
return SplitStringT<string16, StringPiece16, char16>(
|
result_type);
|
||||||
input, separators[0], whitespace, result_type);
|
|
||||||
}
|
|
||||||
return SplitStringT<string16, StringPiece16, StringPiece16>(
|
|
||||||
input, separators, whitespace, result_type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SplitStringIntoKeyValuePairs(StringPiece input,
|
bool SplitStringIntoKeyValuePairs(StringPiece input,
|
||||||
@ -240,18 +187,16 @@ std::vector<string16> SplitStringUsingSubstr(StringPiece16 input,
|
|||||||
StringPiece16 delimiter,
|
StringPiece16 delimiter,
|
||||||
WhitespaceHandling whitespace,
|
WhitespaceHandling whitespace,
|
||||||
SplitResult result_type) {
|
SplitResult result_type) {
|
||||||
std::vector<string16> result;
|
return SplitStringUsingSubstrT<string16>(input, delimiter, whitespace,
|
||||||
SplitStringUsingSubstrT(input, delimiter, whitespace, result_type, &result);
|
result_type);
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> SplitStringUsingSubstr(StringPiece input,
|
std::vector<std::string> SplitStringUsingSubstr(StringPiece input,
|
||||||
StringPiece delimiter,
|
StringPiece delimiter,
|
||||||
WhitespaceHandling whitespace,
|
WhitespaceHandling whitespace,
|
||||||
SplitResult result_type) {
|
SplitResult result_type) {
|
||||||
std::vector<std::string> result;
|
return SplitStringUsingSubstrT<std::string>(input, delimiter, whitespace,
|
||||||
SplitStringUsingSubstrT(input, delimiter, whitespace, result_type, &result);
|
result_type);
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<StringPiece16> SplitStringPieceUsingSubstr(
|
std::vector<StringPiece16> SplitStringPieceUsingSubstr(
|
||||||
@ -260,8 +205,8 @@ std::vector<StringPiece16> SplitStringPieceUsingSubstr(
|
|||||||
WhitespaceHandling whitespace,
|
WhitespaceHandling whitespace,
|
||||||
SplitResult result_type) {
|
SplitResult result_type) {
|
||||||
std::vector<StringPiece16> result;
|
std::vector<StringPiece16> result;
|
||||||
SplitStringUsingSubstrT(input, delimiter, whitespace, result_type, &result);
|
return SplitStringUsingSubstrT<StringPiece16>(input, delimiter, whitespace,
|
||||||
return result;
|
result_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<StringPiece> SplitStringPieceUsingSubstr(
|
std::vector<StringPiece> SplitStringPieceUsingSubstr(
|
||||||
@ -269,9 +214,41 @@ std::vector<StringPiece> SplitStringPieceUsingSubstr(
|
|||||||
StringPiece delimiter,
|
StringPiece delimiter,
|
||||||
WhitespaceHandling whitespace,
|
WhitespaceHandling whitespace,
|
||||||
SplitResult result_type) {
|
SplitResult result_type) {
|
||||||
std::vector<StringPiece> result;
|
return SplitStringUsingSubstrT<StringPiece>(input, delimiter, whitespace,
|
||||||
SplitStringUsingSubstrT(input, delimiter, whitespace, result_type, &result);
|
result_type);
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(OS_WIN) && defined(BASE_STRING16_IS_STD_U16STRING)
|
||||||
|
std::vector<std::wstring> SplitString(WStringPiece input,
|
||||||
|
WStringPiece separators,
|
||||||
|
WhitespaceHandling whitespace,
|
||||||
|
SplitResult result_type) {
|
||||||
|
return SplitStringT<std::wstring>(input, separators, whitespace, result_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<WStringPiece> SplitStringPiece(WStringPiece input,
|
||||||
|
WStringPiece separators,
|
||||||
|
WhitespaceHandling whitespace,
|
||||||
|
SplitResult result_type) {
|
||||||
|
return SplitStringT<WStringPiece>(input, separators, whitespace, result_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::wstring> SplitStringUsingSubstr(WStringPiece input,
|
||||||
|
WStringPiece delimiter,
|
||||||
|
WhitespaceHandling whitespace,
|
||||||
|
SplitResult result_type) {
|
||||||
|
return SplitStringUsingSubstrT<std::wstring>(input, delimiter, whitespace,
|
||||||
|
result_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<WStringPiece> SplitStringPieceUsingSubstr(
|
||||||
|
WStringPiece input,
|
||||||
|
WStringPiece delimiter,
|
||||||
|
WhitespaceHandling whitespace,
|
||||||
|
SplitResult result_type) {
|
||||||
|
return SplitStringUsingSubstrT<WStringPiece>(input, delimiter, whitespace,
|
||||||
|
result_type);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace base
|
} // namespace base
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include "base/base_export.h"
|
#include "base/base_export.h"
|
||||||
#include "base/strings/string16.h"
|
#include "base/strings/string16.h"
|
||||||
#include "base/strings/string_piece.h"
|
#include "base/strings/string_piece.h"
|
||||||
|
#include "build/build_config.h"
|
||||||
|
|
||||||
namespace base {
|
namespace base {
|
||||||
|
|
||||||
@ -39,26 +40,31 @@ enum SplitResult {
|
|||||||
// Split the given string on ANY of the given separators, returning copies of
|
// Split the given string on ANY of the given separators, returning copies of
|
||||||
// the result.
|
// the result.
|
||||||
//
|
//
|
||||||
|
// Note this is inverse of JoinString() defined in string_util.h.
|
||||||
|
//
|
||||||
// To split on either commas or semicolons, keeping all whitespace:
|
// To split on either commas or semicolons, keeping all whitespace:
|
||||||
//
|
//
|
||||||
// std::vector<std::string> tokens = base::SplitString(
|
// std::vector<std::string> tokens = base::SplitString(
|
||||||
// input, ",;", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
|
// input, ", WARN_UNUSED_RESULT;", base::KEEP_WHITESPACE,
|
||||||
BASE_EXPORT std::vector<std::string> SplitString(
|
// base::SPLIT_WANT_ALL) WARN_UNUSED_RESULT;
|
||||||
StringPiece input,
|
BASE_EXPORT std::vector<std::string> SplitString(StringPiece input,
|
||||||
StringPiece separators,
|
StringPiece separators,
|
||||||
WhitespaceHandling whitespace,
|
WhitespaceHandling whitespace,
|
||||||
SplitResult result_type);
|
SplitResult result_type)
|
||||||
BASE_EXPORT std::vector<string16> SplitString(
|
WARN_UNUSED_RESULT;
|
||||||
StringPiece16 input,
|
BASE_EXPORT std::vector<string16> SplitString(StringPiece16 input,
|
||||||
StringPiece16 separators,
|
StringPiece16 separators,
|
||||||
WhitespaceHandling whitespace,
|
WhitespaceHandling whitespace,
|
||||||
SplitResult result_type);
|
SplitResult result_type)
|
||||||
|
WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
// Like SplitString above except it returns a vector of StringPieces which
|
// Like SplitString above except it returns a vector of StringPieces which
|
||||||
// reference the original buffer without copying. Although you have to be
|
// reference the original buffer without copying. Although you have to be
|
||||||
// careful to keep the original string unmodified, this provides an efficient
|
// careful to keep the original string unmodified, this provides an efficient
|
||||||
// way to iterate through tokens in a string.
|
// way to iterate through tokens in a string.
|
||||||
//
|
//
|
||||||
|
// Note this is inverse of JoinString() defined in string_util.h.
|
||||||
|
//
|
||||||
// To iterate through all whitespace-separated tokens in an input string:
|
// To iterate through all whitespace-separated tokens in an input string:
|
||||||
//
|
//
|
||||||
// for (const auto& cur :
|
// for (const auto& cur :
|
||||||
@ -70,12 +76,12 @@ BASE_EXPORT std::vector<StringPiece> SplitStringPiece(
|
|||||||
StringPiece input,
|
StringPiece input,
|
||||||
StringPiece separators,
|
StringPiece separators,
|
||||||
WhitespaceHandling whitespace,
|
WhitespaceHandling whitespace,
|
||||||
SplitResult result_type);
|
SplitResult result_type) WARN_UNUSED_RESULT;
|
||||||
BASE_EXPORT std::vector<StringPiece16> SplitStringPiece(
|
BASE_EXPORT std::vector<StringPiece16> SplitStringPiece(
|
||||||
StringPiece16 input,
|
StringPiece16 input,
|
||||||
StringPiece16 separators,
|
StringPiece16 separators,
|
||||||
WhitespaceHandling whitespace,
|
WhitespaceHandling whitespace,
|
||||||
SplitResult result_type);
|
SplitResult result_type) WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
using StringPairs = std::vector<std::pair<std::string, std::string>>;
|
using StringPairs = std::vector<std::pair<std::string, std::string>>;
|
||||||
|
|
||||||
@ -102,12 +108,12 @@ BASE_EXPORT std::vector<string16> SplitStringUsingSubstr(
|
|||||||
StringPiece16 input,
|
StringPiece16 input,
|
||||||
StringPiece16 delimiter,
|
StringPiece16 delimiter,
|
||||||
WhitespaceHandling whitespace,
|
WhitespaceHandling whitespace,
|
||||||
SplitResult result_type);
|
SplitResult result_type) WARN_UNUSED_RESULT;
|
||||||
BASE_EXPORT std::vector<std::string> SplitStringUsingSubstr(
|
BASE_EXPORT std::vector<std::string> SplitStringUsingSubstr(
|
||||||
StringPiece input,
|
StringPiece input,
|
||||||
StringPiece delimiter,
|
StringPiece delimiter,
|
||||||
WhitespaceHandling whitespace,
|
WhitespaceHandling whitespace,
|
||||||
SplitResult result_type);
|
SplitResult result_type) WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
// Like SplitStringUsingSubstr above except it returns a vector of StringPieces
|
// Like SplitStringUsingSubstr above except it returns a vector of StringPieces
|
||||||
// which reference the original buffer without copying. Although you have to be
|
// which reference the original buffer without copying. Although you have to be
|
||||||
@ -125,12 +131,38 @@ BASE_EXPORT std::vector<StringPiece16> SplitStringPieceUsingSubstr(
|
|||||||
StringPiece16 input,
|
StringPiece16 input,
|
||||||
StringPiece16 delimiter,
|
StringPiece16 delimiter,
|
||||||
WhitespaceHandling whitespace,
|
WhitespaceHandling whitespace,
|
||||||
SplitResult result_type);
|
SplitResult result_type) WARN_UNUSED_RESULT;
|
||||||
BASE_EXPORT std::vector<StringPiece> SplitStringPieceUsingSubstr(
|
BASE_EXPORT std::vector<StringPiece> SplitStringPieceUsingSubstr(
|
||||||
StringPiece input,
|
StringPiece input,
|
||||||
StringPiece delimiter,
|
StringPiece delimiter,
|
||||||
WhitespaceHandling whitespace,
|
WhitespaceHandling whitespace,
|
||||||
SplitResult result_type);
|
SplitResult result_type) WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
|
#if defined(OS_WIN) && defined(BASE_STRING16_IS_STD_U16STRING)
|
||||||
|
BASE_EXPORT std::vector<std::wstring> SplitString(WStringPiece input,
|
||||||
|
WStringPiece separators,
|
||||||
|
WhitespaceHandling whitespace,
|
||||||
|
SplitResult result_type)
|
||||||
|
WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
|
BASE_EXPORT std::vector<WStringPiece> SplitStringPiece(
|
||||||
|
WStringPiece input,
|
||||||
|
WStringPiece separators,
|
||||||
|
WhitespaceHandling whitespace,
|
||||||
|
SplitResult result_type) WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
|
BASE_EXPORT std::vector<std::wstring> SplitStringUsingSubstr(
|
||||||
|
WStringPiece input,
|
||||||
|
WStringPiece delimiter,
|
||||||
|
WhitespaceHandling whitespace,
|
||||||
|
SplitResult result_type) WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
|
BASE_EXPORT std::vector<WStringPiece> SplitStringPieceUsingSubstr(
|
||||||
|
WStringPiece input,
|
||||||
|
WStringPiece delimiter,
|
||||||
|
WhitespaceHandling whitespace,
|
||||||
|
SplitResult result_type) WARN_UNUSED_RESULT;
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace base
|
} // namespace base
|
||||||
|
|
||||||
|
@ -237,17 +237,16 @@ bool ReplaceCharsT(const StringType& input,
|
|||||||
|
|
||||||
bool ReplaceChars(const string16& input,
|
bool ReplaceChars(const string16& input,
|
||||||
StringPiece16 replace_chars,
|
StringPiece16 replace_chars,
|
||||||
const string16& replace_with,
|
StringPiece16 replace_with,
|
||||||
string16* output) {
|
string16* output) {
|
||||||
return ReplaceCharsT(input, replace_chars, StringPiece16(replace_with),
|
return ReplaceCharsT(input, replace_chars, replace_with, output);
|
||||||
output);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ReplaceChars(const std::string& input,
|
bool ReplaceChars(const std::string& input,
|
||||||
StringPiece replace_chars,
|
StringPiece replace_chars,
|
||||||
const std::string& replace_with,
|
StringPiece replace_with,
|
||||||
std::string* output) {
|
std::string* output) {
|
||||||
return ReplaceCharsT(input, replace_chars, StringPiece(replace_with), output);
|
return ReplaceCharsT(input, replace_chars, replace_with, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RemoveChars(const string16& input,
|
bool RemoveChars(const string16& input,
|
||||||
@ -262,8 +261,8 @@ bool RemoveChars(const std::string& input,
|
|||||||
return ReplaceCharsT(input, remove_chars, StringPiece(), output);
|
return ReplaceCharsT(input, remove_chars, StringPiece(), output);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Str>
|
template <typename Str>
|
||||||
TrimPositions TrimStringT(const Str& input,
|
TrimPositions TrimStringT(BasicStringPiece<Str> input,
|
||||||
BasicStringPiece<Str> trim_chars,
|
BasicStringPiece<Str> trim_chars,
|
||||||
TrimPositions positions,
|
TrimPositions positions,
|
||||||
Str* output) {
|
Str* output) {
|
||||||
@ -271,40 +270,40 @@ TrimPositions TrimStringT(const Str& input,
|
|||||||
// a StringPiece version of input to be able to call find* on it with the
|
// a StringPiece version of input to be able to call find* on it with the
|
||||||
// StringPiece version of trim_chars (normally the trim_chars will be a
|
// StringPiece version of trim_chars (normally the trim_chars will be a
|
||||||
// constant so avoid making a copy).
|
// constant so avoid making a copy).
|
||||||
BasicStringPiece<Str> input_piece(input);
|
|
||||||
const size_t last_char = input.length() - 1;
|
const size_t last_char = input.length() - 1;
|
||||||
const size_t first_good_char = (positions & TRIM_LEADING) ?
|
const size_t first_good_char =
|
||||||
input_piece.find_first_not_of(trim_chars) : 0;
|
(positions & TRIM_LEADING) ? input.find_first_not_of(trim_chars) : 0;
|
||||||
const size_t last_good_char = (positions & TRIM_TRAILING) ?
|
const size_t last_good_char = (positions & TRIM_TRAILING)
|
||||||
input_piece.find_last_not_of(trim_chars) : last_char;
|
? input.find_last_not_of(trim_chars)
|
||||||
|
: last_char;
|
||||||
|
|
||||||
// When the string was all trimmed, report that we stripped off characters
|
// When the string was all trimmed, report that we stripped off characters
|
||||||
// from whichever position the caller was interested in. For empty input, we
|
// from whichever position the caller was interested in. For empty input, we
|
||||||
// stripped no characters, but we still need to clear |output|.
|
// stripped no characters, but we still need to clear |output|.
|
||||||
if (input.empty() ||
|
if (input.empty() || first_good_char == Str::npos ||
|
||||||
(first_good_char == Str::npos) || (last_good_char == Str::npos)) {
|
last_good_char == Str::npos) {
|
||||||
bool input_was_empty = input.empty(); // in case output == &input
|
bool input_was_empty = input.empty(); // in case output == &input
|
||||||
output->clear();
|
output->clear();
|
||||||
return input_was_empty ? TRIM_NONE : positions;
|
return input_was_empty ? TRIM_NONE : positions;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trim.
|
// Trim.
|
||||||
*output =
|
output->assign(input.data() + first_good_char,
|
||||||
input.substr(first_good_char, last_good_char - first_good_char + 1);
|
last_good_char - first_good_char + 1);
|
||||||
|
|
||||||
// Return where we trimmed from.
|
// Return where we trimmed from.
|
||||||
return static_cast<TrimPositions>(
|
return static_cast<TrimPositions>(
|
||||||
((first_good_char == 0) ? TRIM_NONE : TRIM_LEADING) |
|
(first_good_char == 0 ? TRIM_NONE : TRIM_LEADING) |
|
||||||
((last_good_char == last_char) ? TRIM_NONE : TRIM_TRAILING));
|
(last_good_char == last_char ? TRIM_NONE : TRIM_TRAILING));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TrimString(const string16& input,
|
bool TrimString(StringPiece16 input,
|
||||||
StringPiece16 trim_chars,
|
StringPiece16 trim_chars,
|
||||||
string16* output) {
|
string16* output) {
|
||||||
return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
|
return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TrimString(const std::string& input,
|
bool TrimString(StringPiece input,
|
||||||
StringPiece trim_chars,
|
StringPiece trim_chars,
|
||||||
std::string* output) {
|
std::string* output) {
|
||||||
return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
|
return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
|
||||||
@ -370,7 +369,7 @@ void TruncateUTF8ToByteSize(const std::string& input,
|
|||||||
output->clear();
|
output->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
TrimPositions TrimWhitespace(const string16& input,
|
TrimPositions TrimWhitespace(StringPiece16 input,
|
||||||
TrimPositions positions,
|
TrimPositions positions,
|
||||||
string16* output) {
|
string16* output) {
|
||||||
return TrimStringT(input, StringPiece16(kWhitespaceUTF16), positions, output);
|
return TrimStringT(input, StringPiece16(kWhitespaceUTF16), positions, output);
|
||||||
@ -381,7 +380,7 @@ StringPiece16 TrimWhitespace(StringPiece16 input,
|
|||||||
return TrimStringPieceT(input, StringPiece16(kWhitespaceUTF16), positions);
|
return TrimStringPieceT(input, StringPiece16(kWhitespaceUTF16), positions);
|
||||||
}
|
}
|
||||||
|
|
||||||
TrimPositions TrimWhitespaceASCII(const std::string& input,
|
TrimPositions TrimWhitespaceASCII(StringPiece input,
|
||||||
TrimPositions positions,
|
TrimPositions positions,
|
||||||
std::string* output) {
|
std::string* output) {
|
||||||
return TrimStringT(input, StringPiece(kWhitespaceASCII), positions, output);
|
return TrimStringT(input, StringPiece(kWhitespaceASCII), positions, output);
|
||||||
@ -506,20 +505,29 @@ bool IsStringASCII(WStringPiece str) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool IsStringUTF8(StringPiece str) {
|
template <bool (*Validator)(uint32_t)>
|
||||||
const char *src = str.data();
|
inline static bool DoIsStringUTF8(StringPiece str) {
|
||||||
|
const char* src = str.data();
|
||||||
int32_t src_len = static_cast<int32_t>(str.length());
|
int32_t src_len = static_cast<int32_t>(str.length());
|
||||||
int32_t char_index = 0;
|
int32_t char_index = 0;
|
||||||
|
|
||||||
while (char_index < src_len) {
|
while (char_index < src_len) {
|
||||||
int32_t code_point;
|
int32_t code_point;
|
||||||
CBU8_NEXT(src, char_index, src_len, code_point);
|
CBU8_NEXT(src, char_index, src_len, code_point);
|
||||||
if (!IsValidCharacter(code_point))
|
if (!Validator(code_point))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsStringUTF8(StringPiece str) {
|
||||||
|
return DoIsStringUTF8<IsValidCharacter>(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsStringUTF8AllowingNoncharacters(StringPiece str) {
|
||||||
|
return DoIsStringUTF8<IsValidCodepoint>(str);
|
||||||
|
}
|
||||||
|
|
||||||
// Implementation note: Normally this function will be called with a hardcoded
|
// Implementation note: Normally this function will be called with a hardcoded
|
||||||
// constant for the lowercase_ascii parameter. Constructing a StringPiece from
|
// constant for the lowercase_ascii parameter. Constructing a StringPiece from
|
||||||
// a C constant requires running strlen, so the result will be two passes
|
// a C constant requires running strlen, so the result will be two passes
|
||||||
@ -913,7 +921,7 @@ void ReplaceSubstringsAfterOffset(std::string* str,
|
|||||||
template <class string_type>
|
template <class string_type>
|
||||||
inline typename string_type::value_type* WriteIntoT(string_type* str,
|
inline typename string_type::value_type* WriteIntoT(string_type* str,
|
||||||
size_t length_with_null) {
|
size_t length_with_null) {
|
||||||
DCHECK_GT(length_with_null, 1u);
|
DCHECK_GE(length_with_null, 1u);
|
||||||
str->reserve(length_with_null);
|
str->reserve(length_with_null);
|
||||||
str->resize(length_with_null - 1);
|
str->resize(length_with_null - 1);
|
||||||
return &((*str)[0]);
|
return &((*str)[0]);
|
||||||
@ -1085,6 +1093,36 @@ string16 ReplaceStringPlaceholders(const string16& format_string,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(OS_WIN) && defined(BASE_STRING16_IS_STD_U16STRING)
|
||||||
|
|
||||||
|
TrimPositions TrimWhitespace(WStringPiece input,
|
||||||
|
TrimPositions positions,
|
||||||
|
std::wstring* output) {
|
||||||
|
return TrimStringT(input, WStringPiece(kWhitespaceWide), positions, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
WStringPiece TrimWhitespace(WStringPiece input, TrimPositions positions) {
|
||||||
|
return TrimStringPieceT(input, WStringPiece(kWhitespaceWide), positions);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TrimString(WStringPiece input,
|
||||||
|
WStringPiece trim_chars,
|
||||||
|
std::wstring* output) {
|
||||||
|
return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
WStringPiece TrimString(WStringPiece input,
|
||||||
|
WStringPiece trim_chars,
|
||||||
|
TrimPositions positions) {
|
||||||
|
return TrimStringPieceT(input, trim_chars, positions);
|
||||||
|
}
|
||||||
|
|
||||||
|
wchar_t* WriteInto(std::wstring* str, size_t length_with_null) {
|
||||||
|
return WriteIntoT(str, length_with_null);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
// The following code is compatible with the OpenBSD lcpy interface. See:
|
// The following code is compatible with the OpenBSD lcpy interface. See:
|
||||||
// http://www.gratisoft.us/todd/papers/strlcpy.html
|
// http://www.gratisoft.us/todd/papers/strlcpy.html
|
||||||
// ftp://ftp.openbsd.org/pub/OpenBSD/src/lib/libc/string/{wcs,str}lcpy.c
|
// ftp://ftp.openbsd.org/pub/OpenBSD/src/lib/libc/string/{wcs,str}lcpy.c
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "base/base_export.h"
|
#include "base/base_export.h"
|
||||||
#include "base/bit_cast.h"
|
|
||||||
#include "base/compiler_specific.h"
|
#include "base/compiler_specific.h"
|
||||||
#include "base/stl_util.h"
|
#include "base/stl_util.h"
|
||||||
#include "base/strings/string16.h"
|
#include "base/strings/string16.h"
|
||||||
@ -161,6 +160,7 @@ BASE_EXPORT const string16& EmptyString16();
|
|||||||
// by HTML5, and don't include control characters.
|
// by HTML5, and don't include control characters.
|
||||||
BASE_EXPORT extern const wchar_t kWhitespaceWide[]; // Includes Unicode.
|
BASE_EXPORT extern const wchar_t kWhitespaceWide[]; // Includes Unicode.
|
||||||
BASE_EXPORT extern const char16 kWhitespaceUTF16[]; // Includes Unicode.
|
BASE_EXPORT extern const char16 kWhitespaceUTF16[]; // Includes Unicode.
|
||||||
|
BASE_EXPORT extern const char16 kWhitespaceNoCrLfUTF16[]; // Unicode w/o CR/LF.
|
||||||
BASE_EXPORT extern const char kWhitespaceASCII[];
|
BASE_EXPORT extern const char kWhitespaceASCII[];
|
||||||
BASE_EXPORT extern const char16 kWhitespaceASCIIAs16[]; // No unicode.
|
BASE_EXPORT extern const char16 kWhitespaceASCIIAs16[]; // No unicode.
|
||||||
|
|
||||||
@ -184,11 +184,11 @@ BASE_EXPORT bool RemoveChars(const std::string& input,
|
|||||||
// NOTE: Safe to use the same variable for both |input| and |output|.
|
// NOTE: Safe to use the same variable for both |input| and |output|.
|
||||||
BASE_EXPORT bool ReplaceChars(const string16& input,
|
BASE_EXPORT bool ReplaceChars(const string16& input,
|
||||||
StringPiece16 replace_chars,
|
StringPiece16 replace_chars,
|
||||||
const string16& replace_with,
|
StringPiece16 replace_with,
|
||||||
string16* output);
|
string16* output);
|
||||||
BASE_EXPORT bool ReplaceChars(const std::string& input,
|
BASE_EXPORT bool ReplaceChars(const std::string& input,
|
||||||
StringPiece replace_chars,
|
StringPiece replace_chars,
|
||||||
const std::string& replace_with,
|
StringPiece replace_with,
|
||||||
std::string* output);
|
std::string* output);
|
||||||
|
|
||||||
enum TrimPositions {
|
enum TrimPositions {
|
||||||
@ -204,10 +204,10 @@ enum TrimPositions {
|
|||||||
//
|
//
|
||||||
// It is safe to use the same variable for both |input| and |output| (this is
|
// It is safe to use the same variable for both |input| and |output| (this is
|
||||||
// the normal usage to trim in-place).
|
// the normal usage to trim in-place).
|
||||||
BASE_EXPORT bool TrimString(const string16& input,
|
BASE_EXPORT bool TrimString(StringPiece16 input,
|
||||||
StringPiece16 trim_chars,
|
StringPiece16 trim_chars,
|
||||||
string16* output);
|
string16* output);
|
||||||
BASE_EXPORT bool TrimString(const std::string& input,
|
BASE_EXPORT bool TrimString(StringPiece input,
|
||||||
StringPiece trim_chars,
|
StringPiece trim_chars,
|
||||||
std::string* output);
|
std::string* output);
|
||||||
|
|
||||||
@ -229,37 +229,63 @@ BASE_EXPORT void TruncateUTF8ToByteSize(const std::string& input,
|
|||||||
#if defined(WCHAR_T_IS_UTF16)
|
#if defined(WCHAR_T_IS_UTF16)
|
||||||
// Utility functions to access the underlying string buffer as a wide char
|
// Utility functions to access the underlying string buffer as a wide char
|
||||||
// pointer.
|
// pointer.
|
||||||
|
//
|
||||||
|
// Note: These functions violate strict aliasing when char16 and wchar_t are
|
||||||
|
// unrelated types. We thus pass -fno-strict-aliasing to the compiler on
|
||||||
|
// non-Windows platforms [1], and rely on it being off in Clang's CL mode [2].
|
||||||
|
//
|
||||||
|
// [1] https://crrev.com/b9a0976622/build/config/compiler/BUILD.gn#244
|
||||||
|
// [2]
|
||||||
|
// https://github.com/llvm/llvm-project/blob/1e28a66/clang/lib/Driver/ToolChains/Clang.cpp#L3949
|
||||||
inline wchar_t* as_writable_wcstr(char16* str) {
|
inline wchar_t* as_writable_wcstr(char16* str) {
|
||||||
return bit_cast<wchar_t*>(str);
|
return reinterpret_cast<wchar_t*>(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline wchar_t* as_writable_wcstr(string16& str) {
|
inline wchar_t* as_writable_wcstr(string16& str) {
|
||||||
return bit_cast<wchar_t*>(data(str));
|
return reinterpret_cast<wchar_t*>(data(str));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const wchar_t* as_wcstr(const char16* str) {
|
inline const wchar_t* as_wcstr(const char16* str) {
|
||||||
return bit_cast<const wchar_t*>(str);
|
return reinterpret_cast<const wchar_t*>(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const wchar_t* as_wcstr(StringPiece16 str) {
|
inline const wchar_t* as_wcstr(StringPiece16 str) {
|
||||||
return bit_cast<const wchar_t*>(str.data());
|
return reinterpret_cast<const wchar_t*>(str.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Utility functions to access the underlying string buffer as a char16 pointer.
|
// Utility functions to access the underlying string buffer as a char16 pointer.
|
||||||
inline char16* as_writable_u16cstr(wchar_t* str) {
|
inline char16* as_writable_u16cstr(wchar_t* str) {
|
||||||
return bit_cast<char16*>(str);
|
return reinterpret_cast<char16*>(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline char16* as_writable_u16cstr(std::wstring& str) {
|
inline char16* as_writable_u16cstr(std::wstring& str) {
|
||||||
return bit_cast<char16*>(data(str));
|
return reinterpret_cast<char16*>(data(str));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const char16* as_u16cstr(const wchar_t* str) {
|
inline const char16* as_u16cstr(const wchar_t* str) {
|
||||||
return bit_cast<const char16*>(str);
|
return reinterpret_cast<const char16*>(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const char16* as_u16cstr(WStringPiece str) {
|
inline const char16* as_u16cstr(WStringPiece str) {
|
||||||
return bit_cast<const char16*>(str.data());
|
return reinterpret_cast<const char16*>(str.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utility functions to convert between base::WStringPiece and
|
||||||
|
// base::StringPiece16.
|
||||||
|
inline WStringPiece AsWStringPiece(StringPiece16 str) {
|
||||||
|
return WStringPiece(as_wcstr(str.data()), str.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline StringPiece16 AsStringPiece16(WStringPiece str) {
|
||||||
|
return StringPiece16(as_u16cstr(str.data()), str.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::wstring AsWString(StringPiece16 str) {
|
||||||
|
return std::wstring(as_wcstr(str.data()), str.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline string16 AsString16(WStringPiece str) {
|
||||||
|
return string16(as_u16cstr(str.data()), str.size());
|
||||||
}
|
}
|
||||||
#endif // defined(WCHAR_T_IS_UTF16)
|
#endif // defined(WCHAR_T_IS_UTF16)
|
||||||
|
|
||||||
@ -270,12 +296,12 @@ inline const char16* as_u16cstr(WStringPiece str) {
|
|||||||
//
|
//
|
||||||
// The std::string versions return where whitespace was found.
|
// The std::string versions return where whitespace was found.
|
||||||
// NOTE: Safe to use the same variable for both input and output.
|
// NOTE: Safe to use the same variable for both input and output.
|
||||||
BASE_EXPORT TrimPositions TrimWhitespace(const string16& input,
|
BASE_EXPORT TrimPositions TrimWhitespace(StringPiece16 input,
|
||||||
TrimPositions positions,
|
TrimPositions positions,
|
||||||
string16* output);
|
string16* output);
|
||||||
BASE_EXPORT StringPiece16 TrimWhitespace(StringPiece16 input,
|
BASE_EXPORT StringPiece16 TrimWhitespace(StringPiece16 input,
|
||||||
TrimPositions positions);
|
TrimPositions positions);
|
||||||
BASE_EXPORT TrimPositions TrimWhitespaceASCII(const std::string& input,
|
BASE_EXPORT TrimPositions TrimWhitespaceASCII(StringPiece input,
|
||||||
TrimPositions positions,
|
TrimPositions positions,
|
||||||
std::string* output);
|
std::string* output);
|
||||||
BASE_EXPORT StringPiece TrimWhitespaceASCII(StringPiece input,
|
BASE_EXPORT StringPiece TrimWhitespaceASCII(StringPiece input,
|
||||||
@ -302,21 +328,23 @@ BASE_EXPORT bool ContainsOnlyChars(StringPiece input, StringPiece characters);
|
|||||||
BASE_EXPORT bool ContainsOnlyChars(StringPiece16 input,
|
BASE_EXPORT bool ContainsOnlyChars(StringPiece16 input,
|
||||||
StringPiece16 characters);
|
StringPiece16 characters);
|
||||||
|
|
||||||
// Returns true if the specified string matches the criteria. How can a wide
|
// Returns true if |str| is structurally valid UTF-8 and also doesn't
|
||||||
// string be 8-bit or UTF8? It contains only characters that are < 256 (in the
|
// contain any non-character code point (e.g. U+10FFFE). Prohibiting
|
||||||
// first case) or characters that use only 8-bits and whose 8-bit
|
// non-characters increases the likelihood of detecting non-UTF-8 in
|
||||||
// representation looks like a UTF-8 string (the second case).
|
// real-world text, for callers which do not need to accept
|
||||||
//
|
// non-characters in strings.
|
||||||
// Note that IsStringUTF8 checks not only if the input is structurally
|
|
||||||
// valid but also if it doesn't contain any non-character codepoint
|
|
||||||
// (e.g. U+FFFE). It's done on purpose because all the existing callers want
|
|
||||||
// to have the maximum 'discriminating' power from other encodings. If
|
|
||||||
// there's a use case for just checking the structural validity, we have to
|
|
||||||
// add a new function for that.
|
|
||||||
//
|
|
||||||
// IsStringASCII assumes the input is likely all ASCII, and does not leave early
|
|
||||||
// if it is not the case.
|
|
||||||
BASE_EXPORT bool IsStringUTF8(StringPiece str);
|
BASE_EXPORT bool IsStringUTF8(StringPiece str);
|
||||||
|
|
||||||
|
// Returns true if |str| contains valid UTF-8, allowing non-character
|
||||||
|
// code points.
|
||||||
|
BASE_EXPORT bool IsStringUTF8AllowingNoncharacters(StringPiece str);
|
||||||
|
|
||||||
|
// Returns true if |str| contains only valid ASCII character values.
|
||||||
|
// Note 1: IsStringASCII executes in time determined solely by the
|
||||||
|
// length of the string, not by its contents, so it is robust against
|
||||||
|
// timing attacks for all strings of equal length.
|
||||||
|
// Note 2: IsStringASCII assumes the input is likely all ASCII, and
|
||||||
|
// does not leave early if it is not the case.
|
||||||
BASE_EXPORT bool IsStringASCII(StringPiece str);
|
BASE_EXPORT bool IsStringASCII(StringPiece str);
|
||||||
BASE_EXPORT bool IsStringASCII(StringPiece16 str);
|
BASE_EXPORT bool IsStringASCII(StringPiece16 str);
|
||||||
#if defined(WCHAR_T_IS_UTF32)
|
#if defined(WCHAR_T_IS_UTF32)
|
||||||
@ -383,6 +411,10 @@ template <typename Char>
|
|||||||
inline bool IsAsciiDigit(Char c) {
|
inline bool IsAsciiDigit(Char c) {
|
||||||
return c >= '0' && c <= '9';
|
return c >= '0' && c <= '9';
|
||||||
}
|
}
|
||||||
|
template <typename Char>
|
||||||
|
inline bool IsAsciiPrintable(Char c) {
|
||||||
|
return c >= ' ' && c <= '~';
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
inline bool IsHexDigit(Char c) {
|
inline bool IsHexDigit(Char c) {
|
||||||
@ -445,10 +477,6 @@ BASE_EXPORT void ReplaceSubstringsAfterOffset(
|
|||||||
// convenient in that is can be used inline in the call, and fast in that it
|
// convenient in that is can be used inline in the call, and fast in that it
|
||||||
// avoids copying the results of the call from a char* into a string.
|
// avoids copying the results of the call from a char* into a string.
|
||||||
//
|
//
|
||||||
// |length_with_null| must be at least 2, since otherwise the underlying string
|
|
||||||
// would have size 0, and trying to access &((*str)[0]) in that case can result
|
|
||||||
// in a number of problems.
|
|
||||||
//
|
|
||||||
// Internally, this takes linear time because the resize() call 0-fills the
|
// Internally, this takes linear time because the resize() call 0-fills the
|
||||||
// underlying array for potentially all
|
// underlying array for potentially all
|
||||||
// (|length_with_null - 1| * sizeof(string_type::value_type)) bytes. Ideally we
|
// (|length_with_null - 1| * sizeof(string_type::value_type)) bytes. Ideally we
|
||||||
@ -460,9 +488,11 @@ BASE_EXPORT void ReplaceSubstringsAfterOffset(
|
|||||||
BASE_EXPORT char* WriteInto(std::string* str, size_t length_with_null);
|
BASE_EXPORT char* WriteInto(std::string* str, size_t length_with_null);
|
||||||
BASE_EXPORT char16* WriteInto(string16* str, size_t length_with_null);
|
BASE_EXPORT char16* WriteInto(string16* str, size_t length_with_null);
|
||||||
|
|
||||||
// Does the opposite of SplitString()/SplitStringPiece(). Joins a vector or list
|
// Joins a vector or list of strings into a single string, inserting |separator|
|
||||||
// of strings into a single string, inserting |separator| (which may be empty)
|
// (which may be empty) in between all elements.
|
||||||
// in between all elements.
|
//
|
||||||
|
// Note this is inverse of SplitString()/SplitStringPiece() defined in
|
||||||
|
// string_split.h.
|
||||||
//
|
//
|
||||||
// If possible, callers should build a vector of StringPieces and use the
|
// If possible, callers should build a vector of StringPieces and use the
|
||||||
// StringPiece variant, so that they do not create unnecessary copies of
|
// StringPiece variant, so that they do not create unnecessary copies of
|
||||||
@ -506,6 +536,25 @@ BASE_EXPORT string16 ReplaceStringPlaceholders(const string16& format_string,
|
|||||||
const string16& a,
|
const string16& a,
|
||||||
size_t* offset);
|
size_t* offset);
|
||||||
|
|
||||||
|
#if defined(OS_WIN) && defined(BASE_STRING16_IS_STD_U16STRING)
|
||||||
|
BASE_EXPORT TrimPositions TrimWhitespace(WStringPiece input,
|
||||||
|
TrimPositions positions,
|
||||||
|
std::wstring* output);
|
||||||
|
|
||||||
|
BASE_EXPORT WStringPiece TrimWhitespace(WStringPiece input,
|
||||||
|
TrimPositions positions);
|
||||||
|
|
||||||
|
BASE_EXPORT bool TrimString(WStringPiece input,
|
||||||
|
WStringPiece trim_chars,
|
||||||
|
std::wstring* output);
|
||||||
|
|
||||||
|
BASE_EXPORT WStringPiece TrimString(WStringPiece input,
|
||||||
|
WStringPiece trim_chars,
|
||||||
|
TrimPositions positions);
|
||||||
|
|
||||||
|
BASE_EXPORT wchar_t* WriteInto(std::wstring* str, size_t length_with_null);
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace base
|
} // namespace base
|
||||||
|
|
||||||
#if defined(OS_WIN)
|
#if defined(OS_WIN)
|
||||||
|
@ -6,61 +6,48 @@
|
|||||||
|
|
||||||
namespace base {
|
namespace base {
|
||||||
|
|
||||||
#define WHITESPACE_UNICODE \
|
#define WHITESPACE_ASCII_NO_CR_LF \
|
||||||
0x0009, /* CHARACTER TABULATION */ \
|
0x09, /* CHARACTER TABULATION */ \
|
||||||
0x000A, /* LINE FEED (LF) */ \
|
0x0B, /* LINE TABULATION */ \
|
||||||
0x000B, /* LINE TABULATION */ \
|
0x0C, /* FORM FEED (FF) */ \
|
||||||
0x000C, /* FORM FEED (FF) */ \
|
0x20 /* SPACE */
|
||||||
0x000D, /* CARRIAGE RETURN (CR) */ \
|
|
||||||
0x0020, /* SPACE */ \
|
|
||||||
0x0085, /* NEXT LINE (NEL) */ \
|
|
||||||
0x00A0, /* NO-BREAK SPACE */ \
|
|
||||||
0x1680, /* OGHAM SPACE MARK */ \
|
|
||||||
0x2000, /* EN QUAD */ \
|
|
||||||
0x2001, /* EM QUAD */ \
|
|
||||||
0x2002, /* EN SPACE */ \
|
|
||||||
0x2003, /* EM SPACE */ \
|
|
||||||
0x2004, /* THREE-PER-EM SPACE */ \
|
|
||||||
0x2005, /* FOUR-PER-EM SPACE */ \
|
|
||||||
0x2006, /* SIX-PER-EM SPACE */ \
|
|
||||||
0x2007, /* FIGURE SPACE */ \
|
|
||||||
0x2008, /* PUNCTUATION SPACE */ \
|
|
||||||
0x2009, /* THIN SPACE */ \
|
|
||||||
0x200A, /* HAIR SPACE */ \
|
|
||||||
0x2028, /* LINE SEPARATOR */ \
|
|
||||||
0x2029, /* PARAGRAPH SEPARATOR */ \
|
|
||||||
0x202F, /* NARROW NO-BREAK SPACE */ \
|
|
||||||
0x205F, /* MEDIUM MATHEMATICAL SPACE */ \
|
|
||||||
0x3000, /* IDEOGRAPHIC SPACE */ \
|
|
||||||
0
|
|
||||||
|
|
||||||
const wchar_t kWhitespaceWide[] = {
|
#define WHITESPACE_ASCII \
|
||||||
WHITESPACE_UNICODE
|
WHITESPACE_ASCII_NO_CR_LF, /* Comment to make clang-format linebreak */ \
|
||||||
};
|
0x0A, /* LINE FEED (LF) */ \
|
||||||
|
0x0D /* CARRIAGE RETURN (CR) */
|
||||||
|
|
||||||
const char16 kWhitespaceUTF16[] = {
|
#define WHITESPACE_UNICODE_NON_ASCII \
|
||||||
WHITESPACE_UNICODE
|
0x0085, /* NEXT LINE (NEL) */ \
|
||||||
};
|
0x00A0, /* NO-BREAK SPACE */ \
|
||||||
|
0x1680, /* OGHAM SPACE MARK */ \
|
||||||
|
0x2000, /* EN QUAD */ \
|
||||||
|
0x2001, /* EM QUAD */ \
|
||||||
|
0x2002, /* EN SPACE */ \
|
||||||
|
0x2003, /* EM SPACE */ \
|
||||||
|
0x2004, /* THREE-PER-EM SPACE */ \
|
||||||
|
0x2005, /* FOUR-PER-EM SPACE */ \
|
||||||
|
0x2006, /* SIX-PER-EM SPACE */ \
|
||||||
|
0x2007, /* FIGURE SPACE */ \
|
||||||
|
0x2008, /* PUNCTUATION SPACE */ \
|
||||||
|
0x2009, /* THIN SPACE */ \
|
||||||
|
0x200A, /* HAIR SPACE */ \
|
||||||
|
0x2028, /* LINE SEPARATOR */ \
|
||||||
|
0x2029, /* PARAGRAPH SEPARATOR */ \
|
||||||
|
0x202F, /* NARROW NO-BREAK SPACE */ \
|
||||||
|
0x205F, /* MEDIUM MATHEMATICAL SPACE */ \
|
||||||
|
0x3000 /* IDEOGRAPHIC SPACE */
|
||||||
|
|
||||||
const char kWhitespaceASCII[] = {
|
#define WHITESPACE_UNICODE_NO_CR_LF \
|
||||||
0x09, // CHARACTER TABULATION
|
WHITESPACE_ASCII_NO_CR_LF, WHITESPACE_UNICODE_NON_ASCII
|
||||||
0x0A, // LINE FEED (LF)
|
|
||||||
0x0B, // LINE TABULATION
|
|
||||||
0x0C, // FORM FEED (FF)
|
|
||||||
0x0D, // CARRIAGE RETURN (CR)
|
|
||||||
0x20, // SPACE
|
|
||||||
0
|
|
||||||
};
|
|
||||||
|
|
||||||
const char16 kWhitespaceASCIIAs16[] = {
|
#define WHITESPACE_UNICODE WHITESPACE_ASCII, WHITESPACE_UNICODE_NON_ASCII
|
||||||
0x09, // CHARACTER TABULATION
|
|
||||||
0x0A, // LINE FEED (LF)
|
const wchar_t kWhitespaceWide[] = {WHITESPACE_UNICODE, 0};
|
||||||
0x0B, // LINE TABULATION
|
const char16 kWhitespaceUTF16[] = {WHITESPACE_UNICODE, 0};
|
||||||
0x0C, // FORM FEED (FF)
|
const char16 kWhitespaceNoCrLfUTF16[] = {WHITESPACE_UNICODE_NO_CR_LF, 0};
|
||||||
0x0D, // CARRIAGE RETURN (CR)
|
const char kWhitespaceASCII[] = {WHITESPACE_ASCII, 0};
|
||||||
0x20, // SPACE
|
const char16 kWhitespaceASCIIAs16[] = {WHITESPACE_ASCII, 0};
|
||||||
0
|
|
||||||
};
|
|
||||||
|
|
||||||
const char kUtf8ByteOrderMark[] = "\xEF\xBB\xBF";
|
const char kUtf8ByteOrderMark[] = "\xEF\xBB\xBF";
|
||||||
|
|
||||||
|
@ -39,18 +39,25 @@ inline int vsnprintfT(wchar_t* buffer,
|
|||||||
va_list argptr) {
|
va_list argptr) {
|
||||||
return base::vswprintf(buffer, buf_size, format, argptr);
|
return base::vswprintf(buffer, buf_size, format, argptr);
|
||||||
}
|
}
|
||||||
|
inline int vsnprintfT(char16_t* buffer,
|
||||||
|
size_t buf_size,
|
||||||
|
const char16_t* format,
|
||||||
|
va_list argptr) {
|
||||||
|
return base::vswprintf(reinterpret_cast<wchar_t*>(buffer), buf_size,
|
||||||
|
reinterpret_cast<const wchar_t*>(format), argptr);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Templatized backend for StringPrintF/StringAppendF. This does not finalize
|
// Templatized backend for StringPrintF/StringAppendF. This does not finalize
|
||||||
// the va_list, the caller is expected to do that.
|
// the va_list, the caller is expected to do that.
|
||||||
template <class StringType>
|
template <class CharT>
|
||||||
static void StringAppendVT(StringType* dst,
|
static void StringAppendVT(std::basic_string<CharT>* dst,
|
||||||
const typename StringType::value_type* format,
|
const CharT* format,
|
||||||
va_list ap) {
|
va_list ap) {
|
||||||
// First try with a small fixed size buffer.
|
// First try with a small fixed size buffer.
|
||||||
// This buffer size should be kept in sync with StringUtilTest.GrowBoundary
|
// This buffer size should be kept in sync with StringUtilTest.GrowBoundary
|
||||||
// and StringUtilTest.StringPrintfBounds.
|
// and StringUtilTest.StringPrintfBounds.
|
||||||
typename StringType::value_type stack_buf[1024];
|
CharT stack_buf[1024];
|
||||||
|
|
||||||
va_list ap_copy;
|
va_list ap_copy;
|
||||||
va_copy(ap_copy, ap);
|
va_copy(ap_copy, ap);
|
||||||
@ -93,7 +100,7 @@ static void StringAppendVT(StringType* dst,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<typename StringType::value_type> mem_buf(mem_length);
|
std::vector<CharT> mem_buf(mem_length);
|
||||||
|
|
||||||
// NOTE: You can only use a va_list once. Since we're in a while loop, we
|
// NOTE: You can only use a va_list once. Since we're in a while loop, we
|
||||||
// need to make a new copy each time so we don't use up the original.
|
// need to make a new copy each time so we don't use up the original.
|
||||||
@ -129,6 +136,15 @@ std::wstring StringPrintf(const wchar_t* format, ...) {
|
|||||||
va_end(ap);
|
va_end(ap);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::u16string StringPrintf(const char16_t* format, ...) {
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, format);
|
||||||
|
std::u16string result;
|
||||||
|
StringAppendV(&result, format, ap);
|
||||||
|
va_end(ap);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
std::string StringPrintV(const char* format, va_list ap) {
|
std::string StringPrintV(const char* format, va_list ap) {
|
||||||
@ -156,6 +172,17 @@ const std::wstring& SStringPrintf(std::wstring* dst,
|
|||||||
va_end(ap);
|
va_end(ap);
|
||||||
return *dst;
|
return *dst;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::u16string& SStringPrintf(std::u16string* dst,
|
||||||
|
const char16_t* format,
|
||||||
|
...) {
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, format);
|
||||||
|
dst->clear();
|
||||||
|
StringAppendV(dst, format, ap);
|
||||||
|
va_end(ap);
|
||||||
|
return *dst;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void StringAppendF(std::string* dst, const char* format, ...) {
|
void StringAppendF(std::string* dst, const char* format, ...) {
|
||||||
@ -172,6 +199,13 @@ void StringAppendF(std::wstring* dst, const wchar_t* format, ...) {
|
|||||||
StringAppendV(dst, format, ap);
|
StringAppendV(dst, format, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void StringAppendF(std::u16string* dst, const char16_t* format, ...) {
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, format);
|
||||||
|
StringAppendV(dst, format, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void StringAppendV(std::string* dst, const char* format, va_list ap) {
|
void StringAppendV(std::string* dst, const char* format, va_list ap) {
|
||||||
@ -182,6 +216,10 @@ void StringAppendV(std::string* dst, const char* format, va_list ap) {
|
|||||||
void StringAppendV(std::wstring* dst, const wchar_t* format, va_list ap) {
|
void StringAppendV(std::wstring* dst, const wchar_t* format, va_list ap) {
|
||||||
StringAppendVT(dst, format, ap);
|
StringAppendVT(dst, format, ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void StringAppendV(std::u16string* dst, const char16_t* format, va_list ap) {
|
||||||
|
StringAppendVT(dst, format, ap);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} // namespace base
|
} // namespace base
|
||||||
|
@ -19,8 +19,14 @@ namespace base {
|
|||||||
BASE_EXPORT std::string StringPrintf(const char* format, ...)
|
BASE_EXPORT std::string StringPrintf(const char* format, ...)
|
||||||
PRINTF_FORMAT(1, 2) WARN_UNUSED_RESULT;
|
PRINTF_FORMAT(1, 2) WARN_UNUSED_RESULT;
|
||||||
#if defined(OS_WIN)
|
#if defined(OS_WIN)
|
||||||
|
// Note: Unfortunately compile time checking of the format string for UTF-16
|
||||||
|
// strings is not supported by any compiler, thus these functions should be used
|
||||||
|
// carefully and sparingly. Also applies to SStringPrintf and StringAppendV
|
||||||
|
// below.
|
||||||
BASE_EXPORT std::wstring StringPrintf(const wchar_t* format, ...)
|
BASE_EXPORT std::wstring StringPrintf(const wchar_t* format, ...)
|
||||||
WPRINTF_FORMAT(1, 2) WARN_UNUSED_RESULT;
|
WPRINTF_FORMAT(1, 2) WARN_UNUSED_RESULT;
|
||||||
|
BASE_EXPORT std::u16string StringPrintf(const char16_t* format, ...)
|
||||||
|
WPRINTF_FORMAT(1, 2) WARN_UNUSED_RESULT;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Return a C++ string given vprintf-like input.
|
// Return a C++ string given vprintf-like input.
|
||||||
@ -35,6 +41,9 @@ BASE_EXPORT const std::string& SStringPrintf(std::string* dst,
|
|||||||
BASE_EXPORT const std::wstring& SStringPrintf(std::wstring* dst,
|
BASE_EXPORT const std::wstring& SStringPrintf(std::wstring* dst,
|
||||||
const wchar_t* format,
|
const wchar_t* format,
|
||||||
...) WPRINTF_FORMAT(2, 3);
|
...) WPRINTF_FORMAT(2, 3);
|
||||||
|
BASE_EXPORT const std::u16string& SStringPrintf(std::u16string* dst,
|
||||||
|
const char16_t* format,
|
||||||
|
...) WPRINTF_FORMAT(2, 3);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Append result to a supplied string.
|
// Append result to a supplied string.
|
||||||
@ -43,6 +52,8 @@ BASE_EXPORT void StringAppendF(std::string* dst, const char* format, ...)
|
|||||||
#if defined(OS_WIN)
|
#if defined(OS_WIN)
|
||||||
BASE_EXPORT void StringAppendF(std::wstring* dst, const wchar_t* format, ...)
|
BASE_EXPORT void StringAppendF(std::wstring* dst, const wchar_t* format, ...)
|
||||||
WPRINTF_FORMAT(2, 3);
|
WPRINTF_FORMAT(2, 3);
|
||||||
|
BASE_EXPORT void StringAppendF(std::u16string* dst, const char16_t* format, ...)
|
||||||
|
WPRINTF_FORMAT(2, 3);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Lower-level routine that takes a va_list and appends to a specified
|
// Lower-level routine that takes a va_list and appends to a specified
|
||||||
@ -53,6 +64,9 @@ BASE_EXPORT void StringAppendV(std::string* dst, const char* format, va_list ap)
|
|||||||
BASE_EXPORT void StringAppendV(std::wstring* dst,
|
BASE_EXPORT void StringAppendV(std::wstring* dst,
|
||||||
const wchar_t* format,
|
const wchar_t* format,
|
||||||
va_list ap) WPRINTF_FORMAT(2, 0);
|
va_list ap) WPRINTF_FORMAT(2, 0);
|
||||||
|
BASE_EXPORT void StringAppendV(std::u16string* dst,
|
||||||
|
const char16_t* format,
|
||||||
|
va_list ap) WPRINTF_FORMAT(2, 0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} // namespace base
|
} // namespace base
|
||||||
|
@ -17,16 +17,19 @@
|
|||||||
namespace base {
|
namespace base {
|
||||||
|
|
||||||
inline bool IsValidCodepoint(uint32_t code_point) {
|
inline bool IsValidCodepoint(uint32_t code_point) {
|
||||||
// Excludes the surrogate code points ([0xD800, 0xDFFF]) and
|
// Excludes code points that are not Unicode scalar values, i.e.
|
||||||
// codepoints larger than 0x10FFFF (the highest codepoint allowed).
|
// surrogate code points ([0xD800, 0xDFFF]). Additionally, excludes
|
||||||
// Non-characters and unassigned codepoints are allowed.
|
// code points larger than 0x10FFFF (the highest codepoint allowed).
|
||||||
|
// Non-characters and unassigned code points are allowed.
|
||||||
|
// https://unicode.org/glossary/#unicode_scalar_value
|
||||||
return code_point < 0xD800u ||
|
return code_point < 0xD800u ||
|
||||||
(code_point >= 0xE000u && code_point <= 0x10FFFFu);
|
(code_point >= 0xE000u && code_point <= 0x10FFFFu);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool IsValidCharacter(uint32_t code_point) {
|
inline bool IsValidCharacter(uint32_t code_point) {
|
||||||
// Excludes non-characters (U+FDD0..U+FDEF, and all codepoints ending in
|
// Excludes non-characters (U+FDD0..U+FDEF, and all code points
|
||||||
// 0xFFFE or 0xFFFF) from the set of valid code points.
|
// ending in 0xFFFE or 0xFFFF) from the set of valid code points.
|
||||||
|
// https://unicode.org/faq/private_use.html#nonchar1
|
||||||
return code_point < 0xD800u || (code_point >= 0xE000u &&
|
return code_point < 0xD800u || (code_point >= 0xE000u &&
|
||||||
code_point < 0xFDD0u) || (code_point > 0xFDEFu &&
|
code_point < 0xFDD0u) || (code_point > 0xFDEFu &&
|
||||||
code_point <= 0x10FFFFu && (code_point & 0xFFFEu) != 0xFFFEu);
|
code_point <= 0x10FFFFu && (code_point & 0xFFFEu) != 0xFFFEu);
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
#include "base/bit_cast.h"
|
|
||||||
#include "base/strings/string_piece.h"
|
#include "base/strings/string_piece.h"
|
||||||
#include "base/strings/string_util.h"
|
#include "base/strings/string_util.h"
|
||||||
#include "base/strings/utf_string_conversion_utils.h"
|
#include "base/strings/utf_string_conversion_utils.h"
|
||||||
|
@ -23,31 +23,31 @@ namespace base {
|
|||||||
// possible.
|
// possible.
|
||||||
BASE_EXPORT bool WideToUTF8(const wchar_t* src, size_t src_len,
|
BASE_EXPORT bool WideToUTF8(const wchar_t* src, size_t src_len,
|
||||||
std::string* output);
|
std::string* output);
|
||||||
BASE_EXPORT std::string WideToUTF8(WStringPiece wide);
|
BASE_EXPORT std::string WideToUTF8(WStringPiece wide) WARN_UNUSED_RESULT;
|
||||||
BASE_EXPORT bool UTF8ToWide(const char* src, size_t src_len,
|
BASE_EXPORT bool UTF8ToWide(const char* src, size_t src_len,
|
||||||
std::wstring* output);
|
std::wstring* output);
|
||||||
BASE_EXPORT std::wstring UTF8ToWide(StringPiece utf8);
|
BASE_EXPORT std::wstring UTF8ToWide(StringPiece utf8) WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
BASE_EXPORT bool WideToUTF16(const wchar_t* src, size_t src_len,
|
BASE_EXPORT bool WideToUTF16(const wchar_t* src, size_t src_len,
|
||||||
string16* output);
|
string16* output);
|
||||||
BASE_EXPORT string16 WideToUTF16(WStringPiece wide);
|
BASE_EXPORT string16 WideToUTF16(WStringPiece wide) WARN_UNUSED_RESULT;
|
||||||
BASE_EXPORT bool UTF16ToWide(const char16* src, size_t src_len,
|
BASE_EXPORT bool UTF16ToWide(const char16* src, size_t src_len,
|
||||||
std::wstring* output);
|
std::wstring* output);
|
||||||
BASE_EXPORT std::wstring UTF16ToWide(StringPiece16 utf16);
|
BASE_EXPORT std::wstring UTF16ToWide(StringPiece16 utf16) WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
BASE_EXPORT bool UTF8ToUTF16(const char* src, size_t src_len, string16* output);
|
BASE_EXPORT bool UTF8ToUTF16(const char* src, size_t src_len, string16* output);
|
||||||
BASE_EXPORT string16 UTF8ToUTF16(StringPiece utf8);
|
BASE_EXPORT string16 UTF8ToUTF16(StringPiece utf8) WARN_UNUSED_RESULT;
|
||||||
BASE_EXPORT bool UTF16ToUTF8(const char16* src, size_t src_len,
|
BASE_EXPORT bool UTF16ToUTF8(const char16* src, size_t src_len,
|
||||||
std::string* output);
|
std::string* output);
|
||||||
BASE_EXPORT std::string UTF16ToUTF8(StringPiece16 utf16);
|
BASE_EXPORT std::string UTF16ToUTF8(StringPiece16 utf16) WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
// This converts an ASCII string, typically a hardcoded constant, to a UTF16
|
// This converts an ASCII string, typically a hardcoded constant, to a UTF16
|
||||||
// string.
|
// string.
|
||||||
BASE_EXPORT string16 ASCIIToUTF16(StringPiece ascii);
|
BASE_EXPORT string16 ASCIIToUTF16(StringPiece ascii) WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
// Converts to 7-bit ASCII by truncating. The result must be known to be ASCII
|
// Converts to 7-bit ASCII by truncating. The result must be known to be ASCII
|
||||||
// beforehand.
|
// beforehand.
|
||||||
BASE_EXPORT std::string UTF16ToASCII(StringPiece16 utf16);
|
BASE_EXPORT std::string UTF16ToASCII(StringPiece16 utf16) WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
} // namespace base
|
} // namespace base
|
||||||
|
|
||||||
|
@ -58,9 +58,6 @@
|
|||||||
// thread that has Wait()ed the longest is selected. The default policy
|
// thread that has Wait()ed the longest is selected. The default policy
|
||||||
// may improve performance, as the selected thread may have a greater chance of
|
// may improve performance, as the selected thread may have a greater chance of
|
||||||
// having some of its stack data in various CPU caches.
|
// having some of its stack data in various CPU caches.
|
||||||
//
|
|
||||||
// For a discussion of the many very subtle implementation details, see the FAQ
|
|
||||||
// at the end of condition_variable_win.cc.
|
|
||||||
|
|
||||||
#ifndef BASE_SYNCHRONIZATION_CONDITION_VARIABLE_H_
|
#ifndef BASE_SYNCHRONIZATION_CONDITION_VARIABLE_H_
|
||||||
#define BASE_SYNCHRONIZATION_CONDITION_VARIABLE_H_
|
#define BASE_SYNCHRONIZATION_CONDITION_VARIABLE_H_
|
||||||
|
@ -67,7 +67,7 @@ void ConditionVariable::Wait() {
|
|||||||
Optional<internal::ScopedBlockingCallWithBaseSyncPrimitives>
|
Optional<internal::ScopedBlockingCallWithBaseSyncPrimitives>
|
||||||
scoped_blocking_call;
|
scoped_blocking_call;
|
||||||
if (waiting_is_blocking_)
|
if (waiting_is_blocking_)
|
||||||
scoped_blocking_call.emplace(BlockingType::MAY_BLOCK);
|
scoped_blocking_call.emplace(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||||
|
|
||||||
#if DCHECK_IS_ON()
|
#if DCHECK_IS_ON()
|
||||||
user_lock_->CheckHeldAndUnmark();
|
user_lock_->CheckHeldAndUnmark();
|
||||||
@ -83,7 +83,7 @@ void ConditionVariable::TimedWait(const TimeDelta& max_time) {
|
|||||||
Optional<internal::ScopedBlockingCallWithBaseSyncPrimitives>
|
Optional<internal::ScopedBlockingCallWithBaseSyncPrimitives>
|
||||||
scoped_blocking_call;
|
scoped_blocking_call;
|
||||||
if (waiting_is_blocking_)
|
if (waiting_is_blocking_)
|
||||||
scoped_blocking_call.emplace(BlockingType::MAY_BLOCK);
|
scoped_blocking_call.emplace(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||||
|
|
||||||
int64_t usecs = max_time.InMicroseconds();
|
int64_t usecs = max_time.InMicroseconds();
|
||||||
struct timespec relative_time;
|
struct timespec relative_time;
|
||||||
|
@ -26,8 +26,8 @@ class LOCKABLE BASE_EXPORT Lock {
|
|||||||
~Lock() {}
|
~Lock() {}
|
||||||
|
|
||||||
// TODO(lukasza): https://crbug.com/831825: Add EXCLUSIVE_LOCK_FUNCTION
|
// TODO(lukasza): https://crbug.com/831825: Add EXCLUSIVE_LOCK_FUNCTION
|
||||||
// annotation to Acquire method and similar annotations to Release, Try and
|
// annotation to Acquire method and similar annotations to Release and Try
|
||||||
// AssertAcquired methods (here and in the #else branch).
|
// methods (here and in the #else branch).
|
||||||
void Acquire() { lock_.Lock(); }
|
void Acquire() { lock_.Lock(); }
|
||||||
void Release() { lock_.Unlock(); }
|
void Release() { lock_.Unlock(); }
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ class LOCKABLE BASE_EXPORT Lock {
|
|||||||
bool Try() { return lock_.Try(); }
|
bool Try() { return lock_.Try(); }
|
||||||
|
|
||||||
// Null implementation if not debug.
|
// Null implementation if not debug.
|
||||||
void AssertAcquired() const {}
|
void AssertAcquired() const ASSERT_EXCLUSIVE_LOCK() {}
|
||||||
#else
|
#else
|
||||||
Lock();
|
Lock();
|
||||||
~Lock();
|
~Lock();
|
||||||
@ -63,7 +63,7 @@ class LOCKABLE BASE_EXPORT Lock {
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssertAcquired() const;
|
void AssertAcquired() const ASSERT_EXCLUSIVE_LOCK();
|
||||||
#endif // DCHECK_IS_ON()
|
#endif // DCHECK_IS_ON()
|
||||||
|
|
||||||
// Whether Lock mitigates priority inversion when used from different thread
|
// Whether Lock mitigates priority inversion when used from different thread
|
||||||
@ -116,6 +116,18 @@ using AutoLock = internal::BasicAutoLock<Lock>;
|
|||||||
// constructor, and re-Acquire() it in the destructor.
|
// constructor, and re-Acquire() it in the destructor.
|
||||||
using AutoUnlock = internal::BasicAutoUnlock<Lock>;
|
using AutoUnlock = internal::BasicAutoUnlock<Lock>;
|
||||||
|
|
||||||
|
// Like AutoLock but is a no-op when the provided Lock* is null. Inspired from
|
||||||
|
// absl::MutexLockMaybe. Use this instead of base::Optional<base::AutoLock> to
|
||||||
|
// get around -Wthread-safety-analysis warnings for conditional locking.
|
||||||
|
using AutoLockMaybe = internal::BasicAutoLockMaybe<Lock>;
|
||||||
|
|
||||||
|
// Like AutoLock but permits Release() of its mutex before destruction.
|
||||||
|
// Release() may be called at most once. Inspired from
|
||||||
|
// absl::ReleasableMutexLock. Use this instead of base::Optional<base::AutoLock>
|
||||||
|
// to get around -Wthread-safety-analysis warnings for AutoLocks that are
|
||||||
|
// explicitly released early (prefer proper scoping to this).
|
||||||
|
using ReleasableAutoLock = internal::BasicReleasableAutoLock<Lock>;
|
||||||
|
|
||||||
} // namespace base
|
} // namespace base
|
||||||
|
|
||||||
#endif // BASE_SYNCHRONIZATION_LOCK_H_
|
#endif // BASE_SYNCHRONIZATION_LOCK_H_
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user