Migrate system/core/base to system/libbase.
Add Symlink to ensure hardcoded references do not break build. BUG: 148941208 test: TH Change-Id: Ia2b69de1af6e07be2aab3ba4fe1493de80269c40 Merged-In: I1134f1e9e968b9273748e2483bea8d25e5c9e994
This commit is contained in:
parent
984aa655df
commit
28d0f1a3af
|
@ -1 +0,0 @@
|
|||
../.clang-format-2
|
241
base/Android.bp
241
base/Android.bp
|
@ -1,241 +0,0 @@
|
|||
//
|
||||
// Copyright (C) 2015 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
cc_defaults {
|
||||
name: "libbase_cflags_defaults",
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Werror",
|
||||
"-Wextra",
|
||||
],
|
||||
target: {
|
||||
android: {
|
||||
cflags: [
|
||||
"-D_FILE_OFFSET_BITS=64",
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cc_library_headers {
|
||||
name: "libbase_headers",
|
||||
vendor_available: true,
|
||||
ramdisk_available: true,
|
||||
recovery_available: true,
|
||||
host_supported: true,
|
||||
native_bridge_supported: true,
|
||||
export_include_dirs: ["include"],
|
||||
|
||||
target: {
|
||||
linux_bionic: {
|
||||
enabled: true,
|
||||
},
|
||||
windows: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
apex_available: [
|
||||
"//apex_available:anyapex",
|
||||
"//apex_available:platform",
|
||||
],
|
||||
min_sdk_version: "29",
|
||||
}
|
||||
|
||||
cc_defaults {
|
||||
name: "libbase_defaults",
|
||||
defaults: ["libbase_cflags_defaults"],
|
||||
srcs: [
|
||||
"chrono_utils.cpp",
|
||||
"cmsg.cpp",
|
||||
"file.cpp",
|
||||
"liblog_symbols.cpp",
|
||||
"logging.cpp",
|
||||
"mapped_file.cpp",
|
||||
"parsebool.cpp",
|
||||
"parsenetaddress.cpp",
|
||||
"process.cpp",
|
||||
"properties.cpp",
|
||||
"stringprintf.cpp",
|
||||
"strings.cpp",
|
||||
"threads.cpp",
|
||||
"test_utils.cpp",
|
||||
],
|
||||
|
||||
cppflags: ["-Wexit-time-destructors"],
|
||||
shared_libs: ["liblog"],
|
||||
target: {
|
||||
android: {
|
||||
sanitize: {
|
||||
misc_undefined: ["integer"],
|
||||
},
|
||||
|
||||
},
|
||||
linux: {
|
||||
srcs: [
|
||||
"errors_unix.cpp",
|
||||
],
|
||||
},
|
||||
darwin: {
|
||||
srcs: [
|
||||
"errors_unix.cpp",
|
||||
],
|
||||
},
|
||||
linux_bionic: {
|
||||
enabled: true,
|
||||
},
|
||||
windows: {
|
||||
srcs: [
|
||||
"errors_windows.cpp",
|
||||
"utf8.cpp",
|
||||
],
|
||||
exclude_srcs: [
|
||||
"cmsg.cpp",
|
||||
],
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cc_library {
|
||||
name: "libbase",
|
||||
defaults: ["libbase_defaults"],
|
||||
vendor_available: true,
|
||||
ramdisk_available: true,
|
||||
recovery_available: true,
|
||||
host_supported: true,
|
||||
native_bridge_supported: true,
|
||||
vndk: {
|
||||
enabled: true,
|
||||
support_system_process: true,
|
||||
},
|
||||
header_libs: [
|
||||
"libbase_headers",
|
||||
],
|
||||
export_header_lib_headers: ["libbase_headers"],
|
||||
static_libs: ["fmtlib"],
|
||||
whole_static_libs: ["fmtlib"],
|
||||
export_static_lib_headers: ["fmtlib"],
|
||||
apex_available: [
|
||||
"//apex_available:anyapex",
|
||||
"//apex_available:platform",
|
||||
],
|
||||
min_sdk_version: "29",
|
||||
}
|
||||
|
||||
cc_library_static {
|
||||
name: "libbase_ndk",
|
||||
defaults: ["libbase_defaults"],
|
||||
sdk_version: "current",
|
||||
stl: "c++_static",
|
||||
export_include_dirs: ["include"],
|
||||
static_libs: ["fmtlib_ndk"],
|
||||
whole_static_libs: ["fmtlib_ndk"],
|
||||
export_static_lib_headers: ["fmtlib_ndk"],
|
||||
}
|
||||
|
||||
// Tests
|
||||
// ------------------------------------------------------------------------------
|
||||
cc_test {
|
||||
name: "libbase_test",
|
||||
defaults: ["libbase_cflags_defaults"],
|
||||
host_supported: true,
|
||||
srcs: [
|
||||
"cmsg_test.cpp",
|
||||
"endian_test.cpp",
|
||||
"errors_test.cpp",
|
||||
"expected_test.cpp",
|
||||
"file_test.cpp",
|
||||
"logging_splitters_test.cpp",
|
||||
"logging_test.cpp",
|
||||
"macros_test.cpp",
|
||||
"mapped_file_test.cpp",
|
||||
"no_destructor_test.cpp",
|
||||
"parsedouble_test.cpp",
|
||||
"parsebool_test.cpp",
|
||||
"parseint_test.cpp",
|
||||
"parsenetaddress_test.cpp",
|
||||
"process_test.cpp",
|
||||
"properties_test.cpp",
|
||||
"result_test.cpp",
|
||||
"scopeguard_test.cpp",
|
||||
"stringprintf_test.cpp",
|
||||
"strings_test.cpp",
|
||||
"test_main.cpp",
|
||||
"test_utils_test.cpp",
|
||||
],
|
||||
target: {
|
||||
android: {
|
||||
sanitize: {
|
||||
misc_undefined: ["integer"],
|
||||
},
|
||||
},
|
||||
linux: {
|
||||
srcs: ["chrono_utils_test.cpp"],
|
||||
},
|
||||
windows: {
|
||||
srcs: ["utf8_test.cpp"],
|
||||
cflags: ["-Wno-unused-parameter"],
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
local_include_dirs: ["."],
|
||||
shared_libs: ["libbase"],
|
||||
compile_multilib: "both",
|
||||
multilib: {
|
||||
lib32: {
|
||||
suffix: "32",
|
||||
},
|
||||
lib64: {
|
||||
suffix: "64",
|
||||
},
|
||||
},
|
||||
test_suites: ["device-tests"],
|
||||
}
|
||||
|
||||
cc_test {
|
||||
name: "libbase_tidy_test",
|
||||
defaults: ["libbase_cflags_defaults"],
|
||||
host_supported: true,
|
||||
|
||||
tidy: true,
|
||||
tidy_checks_as_errors: ["bugprone-use-after-move"],
|
||||
|
||||
srcs: [
|
||||
"tidy/unique_fd_test.cpp",
|
||||
"tidy/unique_fd_test2.cpp",
|
||||
],
|
||||
|
||||
shared_libs: ["libbase"],
|
||||
test_suites: ["device_tests"],
|
||||
}
|
||||
|
||||
cc_benchmark {
|
||||
name: "libbase_benchmark",
|
||||
defaults: ["libbase_cflags_defaults"],
|
||||
|
||||
srcs: ["format_benchmark.cpp"],
|
||||
shared_libs: ["libbase"],
|
||||
|
||||
compile_multilib: "both",
|
||||
multilib: {
|
||||
lib32: {
|
||||
suffix: "32",
|
||||
},
|
||||
lib64: {
|
||||
suffix: "64",
|
||||
},
|
||||
},
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
set noparent
|
||||
filter=-build/header_guard,-build/include,-build/c++11,-whitespace/operators
|
|
@ -1,3 +0,0 @@
|
|||
enh@google.com
|
||||
jmgao@google.com
|
||||
tomcherry@google.com
|
|
@ -1,42 +0,0 @@
|
|||
# libbase
|
||||
|
||||
## Who is this library for?
|
||||
|
||||
This library is a collection of convenience functions to make common tasks
|
||||
easier and less error-prone.
|
||||
|
||||
In this context, "error-prone" covers both "hard to do correctly" and
|
||||
"hard to do with good performance", but as a general purpose library,
|
||||
libbase's primary focus is on making it easier to do things easily and
|
||||
correctly when a compromise has to be made between "simplest API" on the
|
||||
one hand and "fastest implementation" on the other. Though obviously
|
||||
the ideal is to have both.
|
||||
|
||||
## Should my routine be added?
|
||||
|
||||
The intention is to cover the 80% use cases, not be all things to all users.
|
||||
|
||||
If you have a routine that's really useful in your project,
|
||||
congratulations. But that doesn't mean it should be here rather than
|
||||
just in your project.
|
||||
|
||||
The question for libbase is "should everyone be doing this?"/"does this
|
||||
make everyone's code cleaner/safer?". Historically we've considered the
|
||||
bar for inclusion to be "are there at least three *unrelated* projects
|
||||
that would be cleaned up by doing so".
|
||||
|
||||
If your routine is actually something from a future C++ standard (that
|
||||
isn't yet in libc++), or it's widely used in another library, that helps
|
||||
show that there's precedent. Being able to say "so-and-so has used this
|
||||
API for n years" is a good way to reduce concerns about API choices.
|
||||
|
||||
## Any other restrictions?
|
||||
|
||||
Unlike most Android code, code in libbase has to build for Mac and
|
||||
Windows too.
|
||||
|
||||
Code here is also expected to have good test coverage.
|
||||
|
||||
By its nature, it's difficult to change libbase API. It's often best
|
||||
to start using your routine just in your project, and let it "graduate"
|
||||
after you're certain that the API is solid.
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "android-base/chrono_utils.h"
|
||||
|
||||
#include <time.h>
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
boot_clock::time_point boot_clock::now() {
|
||||
#ifdef __linux__
|
||||
timespec ts;
|
||||
clock_gettime(CLOCK_BOOTTIME, &ts);
|
||||
return boot_clock::time_point(std::chrono::seconds(ts.tv_sec) +
|
||||
std::chrono::nanoseconds(ts.tv_nsec));
|
||||
#else
|
||||
// Darwin and Windows do not support clock_gettime.
|
||||
return boot_clock::time_point();
|
||||
#endif // __linux__
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Timer& t) {
|
||||
os << t.duration().count() << "ms";
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "android-base/chrono_utils.h"
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
std::chrono::seconds GetBootTimeSeconds() {
|
||||
struct timespec now;
|
||||
clock_gettime(CLOCK_BOOTTIME, &now);
|
||||
|
||||
auto now_tp = boot_clock::time_point(std::chrono::seconds(now.tv_sec) +
|
||||
std::chrono::nanoseconds(now.tv_nsec));
|
||||
return std::chrono::duration_cast<std::chrono::seconds>(now_tp.time_since_epoch());
|
||||
}
|
||||
|
||||
// Tests (at least) the seconds accuracy of the boot_clock::now() method.
|
||||
TEST(ChronoUtilsTest, BootClockNowSeconds) {
|
||||
auto now = GetBootTimeSeconds();
|
||||
auto boot_seconds =
|
||||
std::chrono::duration_cast<std::chrono::seconds>(boot_clock::now().time_since_epoch());
|
||||
EXPECT_EQ(now, boot_seconds);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void ExpectAboutEqual(T expected, T actual) {
|
||||
auto expected_upper_bound = expected * 1.05f;
|
||||
auto expected_lower_bound = expected * .95;
|
||||
EXPECT_GT(expected_upper_bound, actual);
|
||||
EXPECT_LT(expected_lower_bound, actual);
|
||||
}
|
||||
|
||||
TEST(ChronoUtilsTest, TimerDurationIsSane) {
|
||||
auto start = boot_clock::now();
|
||||
Timer t;
|
||||
std::this_thread::sleep_for(50ms);
|
||||
auto stop = boot_clock::now();
|
||||
auto stop_timer = t.duration();
|
||||
|
||||
auto expected = std::chrono::duration_cast<std::chrono::milliseconds>(stop - start);
|
||||
ExpectAboutEqual(expected, stop_timer);
|
||||
}
|
||||
|
||||
TEST(ChronoUtilsTest, TimerOstream) {
|
||||
Timer t;
|
||||
std::this_thread::sleep_for(50ms);
|
||||
auto stop_timer = t.duration().count();
|
||||
std::stringstream os;
|
||||
os << t;
|
||||
decltype(stop_timer) stop_timer_from_stream;
|
||||
os >> stop_timer_from_stream;
|
||||
EXPECT_NE(0, stop_timer);
|
||||
ExpectAboutEqual(stop_timer, stop_timer_from_stream);
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
173
base/cmsg.cpp
173
base/cmsg.cpp
|
@ -1,173 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <android-base/cmsg.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/user.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
ssize_t SendFileDescriptorVector(borrowed_fd sockfd, const void* data, size_t len,
|
||||
const std::vector<int>& fds) {
|
||||
size_t cmsg_space = CMSG_SPACE(sizeof(int) * fds.size());
|
||||
size_t cmsg_len = CMSG_LEN(sizeof(int) * fds.size());
|
||||
if (cmsg_space >= PAGE_SIZE) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
alignas(struct cmsghdr) char cmsg_buf[cmsg_space];
|
||||
iovec iov = {.iov_base = const_cast<void*>(data), .iov_len = len};
|
||||
msghdr msg = {
|
||||
.msg_name = nullptr,
|
||||
.msg_namelen = 0,
|
||||
.msg_iov = &iov,
|
||||
.msg_iovlen = 1,
|
||||
.msg_control = cmsg_buf,
|
||||
// We can't cast to the actual type of the field, because it's different across platforms.
|
||||
.msg_controllen = static_cast<unsigned int>(cmsg_space),
|
||||
.msg_flags = 0,
|
||||
};
|
||||
|
||||
struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
cmsg->cmsg_len = cmsg_len;
|
||||
|
||||
int* cmsg_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
|
||||
for (size_t i = 0; i < fds.size(); ++i) {
|
||||
cmsg_fds[i] = fds[i];
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
int flags = MSG_NOSIGNAL;
|
||||
#else
|
||||
int flags = 0;
|
||||
#endif
|
||||
|
||||
return TEMP_FAILURE_RETRY(sendmsg(sockfd.get(), &msg, flags));
|
||||
}
|
||||
|
||||
ssize_t ReceiveFileDescriptorVector(borrowed_fd sockfd, void* data, size_t len, size_t max_fds,
|
||||
std::vector<unique_fd>* fds) {
|
||||
fds->clear();
|
||||
|
||||
size_t cmsg_space = CMSG_SPACE(sizeof(int) * max_fds);
|
||||
if (cmsg_space >= PAGE_SIZE) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
alignas(struct cmsghdr) char cmsg_buf[cmsg_space];
|
||||
iovec iov = {.iov_base = const_cast<void*>(data), .iov_len = len};
|
||||
msghdr msg = {
|
||||
.msg_name = nullptr,
|
||||
.msg_namelen = 0,
|
||||
.msg_iov = &iov,
|
||||
.msg_iovlen = 1,
|
||||
.msg_control = cmsg_buf,
|
||||
// We can't cast to the actual type of the field, because it's different across platforms.
|
||||
.msg_controllen = static_cast<unsigned int>(cmsg_space),
|
||||
.msg_flags = 0,
|
||||
};
|
||||
|
||||
int flags = MSG_TRUNC | MSG_CTRUNC;
|
||||
#if defined(__linux__)
|
||||
flags |= MSG_CMSG_CLOEXEC | MSG_NOSIGNAL;
|
||||
#endif
|
||||
|
||||
ssize_t rc = TEMP_FAILURE_RETRY(recvmsg(sockfd.get(), &msg, flags));
|
||||
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int error = 0;
|
||||
if ((msg.msg_flags & MSG_TRUNC)) {
|
||||
LOG(ERROR) << "message was truncated when receiving file descriptors";
|
||||
error = EMSGSIZE;
|
||||
} else if ((msg.msg_flags & MSG_CTRUNC)) {
|
||||
LOG(ERROR) << "control message was truncated when receiving file descriptors";
|
||||
error = EMSGSIZE;
|
||||
}
|
||||
|
||||
std::vector<unique_fd> received_fds;
|
||||
struct cmsghdr* cmsg;
|
||||
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != nullptr; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
|
||||
if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
|
||||
LOG(ERROR) << "received unexpected cmsg: [" << cmsg->cmsg_level << ", " << cmsg->cmsg_type
|
||||
<< "]";
|
||||
error = EBADMSG;
|
||||
continue;
|
||||
}
|
||||
|
||||
// There isn't a macro that does the inverse of CMSG_LEN, so hack around it ourselves, with
|
||||
// some asserts to ensure that CMSG_LEN behaves as we expect.
|
||||
#if defined(__linux__)
|
||||
#define CMSG_ASSERT static_assert
|
||||
#else
|
||||
// CMSG_LEN is somehow not constexpr on darwin.
|
||||
#define CMSG_ASSERT CHECK
|
||||
#endif
|
||||
CMSG_ASSERT(CMSG_LEN(0) + 1 * sizeof(int) == CMSG_LEN(1 * sizeof(int)));
|
||||
CMSG_ASSERT(CMSG_LEN(0) + 2 * sizeof(int) == CMSG_LEN(2 * sizeof(int)));
|
||||
CMSG_ASSERT(CMSG_LEN(0) + 3 * sizeof(int) == CMSG_LEN(3 * sizeof(int)));
|
||||
CMSG_ASSERT(CMSG_LEN(0) + 4 * sizeof(int) == CMSG_LEN(4 * sizeof(int)));
|
||||
|
||||
if (cmsg->cmsg_len % sizeof(int) != 0) {
|
||||
LOG(FATAL) << "cmsg_len(" << cmsg->cmsg_len << ") not aligned to sizeof(int)";
|
||||
} else if (cmsg->cmsg_len <= CMSG_LEN(0)) {
|
||||
LOG(FATAL) << "cmsg_len(" << cmsg->cmsg_len << ") not long enough to hold any data";
|
||||
}
|
||||
|
||||
int* cmsg_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
|
||||
size_t cmsg_fdcount = static_cast<size_t>(cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
|
||||
for (size_t i = 0; i < cmsg_fdcount; ++i) {
|
||||
#if !defined(__linux__)
|
||||
// Linux uses MSG_CMSG_CLOEXEC instead of doing this manually.
|
||||
fcntl(cmsg_fds[i], F_SETFD, FD_CLOEXEC);
|
||||
#endif
|
||||
received_fds.emplace_back(cmsg_fds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (error != 0) {
|
||||
errno = error;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (received_fds.size() > max_fds) {
|
||||
LOG(ERROR) << "received too many file descriptors, expected " << fds->size() << ", received "
|
||||
<< received_fds.size();
|
||||
errno = EMSGSIZE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
*fds = std::move(received_fds);
|
||||
return rc;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,202 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <android-base/cmsg.h>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#if !defined(_WIN32)
|
||||
|
||||
using android::base::ReceiveFileDescriptors;
|
||||
using android::base::SendFileDescriptors;
|
||||
using android::base::unique_fd;
|
||||
|
||||
static ino_t GetInode(int fd) {
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) != 0) {
|
||||
PLOG(FATAL) << "fstat failed";
|
||||
}
|
||||
|
||||
return st.st_ino;
|
||||
}
|
||||
|
||||
struct CmsgTest : ::testing::TestWithParam<bool> {
|
||||
bool Seqpacket() { return GetParam(); }
|
||||
|
||||
void SetUp() override {
|
||||
ASSERT_TRUE(
|
||||
android::base::Socketpair(Seqpacket() ? SOCK_SEQPACKET : SOCK_STREAM, &send, &recv));
|
||||
int dup1 = dup(tmp1.fd);
|
||||
ASSERT_NE(-1, dup1);
|
||||
int dup2 = dup(tmp2.fd);
|
||||
ASSERT_NE(-1, dup2);
|
||||
|
||||
fd1.reset(dup1);
|
||||
fd2.reset(dup2);
|
||||
|
||||
ino1 = GetInode(dup1);
|
||||
ino2 = GetInode(dup2);
|
||||
}
|
||||
|
||||
unique_fd send;
|
||||
unique_fd recv;
|
||||
|
||||
TemporaryFile tmp1;
|
||||
TemporaryFile tmp2;
|
||||
|
||||
unique_fd fd1;
|
||||
unique_fd fd2;
|
||||
|
||||
ino_t ino1;
|
||||
ino_t ino2;
|
||||
};
|
||||
|
||||
TEST_P(CmsgTest, smoke) {
|
||||
ASSERT_EQ(1, SendFileDescriptors(send.get(), "x", 1, fd1.get()));
|
||||
|
||||
char buf[2];
|
||||
unique_fd received;
|
||||
ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 2, &received));
|
||||
ASSERT_EQ('x', buf[0]);
|
||||
ASSERT_NE(-1, received.get());
|
||||
|
||||
ASSERT_EQ(ino1, GetInode(received.get()));
|
||||
}
|
||||
|
||||
TEST_P(CmsgTest, msg_trunc) {
|
||||
ASSERT_EQ(2, SendFileDescriptors(send.get(), "ab", 2, fd1.get(), fd2.get()));
|
||||
|
||||
char buf[2];
|
||||
unique_fd received1, received2;
|
||||
|
||||
ssize_t rc = ReceiveFileDescriptors(recv.get(), buf, 1, &received1, &received2);
|
||||
if (Seqpacket()) {
|
||||
ASSERT_EQ(-1, rc);
|
||||
ASSERT_EQ(EMSGSIZE, errno);
|
||||
ASSERT_EQ(-1, received1.get());
|
||||
ASSERT_EQ(-1, received2.get());
|
||||
} else {
|
||||
ASSERT_EQ(1, rc);
|
||||
ASSERT_NE(-1, received1.get());
|
||||
ASSERT_NE(-1, received2.get());
|
||||
ASSERT_EQ(ino1, GetInode(received1.get()));
|
||||
ASSERT_EQ(ino2, GetInode(received2.get()));
|
||||
ASSERT_EQ(1, read(recv.get(), buf, 2));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(CmsgTest, msg_ctrunc) {
|
||||
ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get(), fd2.get()));
|
||||
|
||||
char buf[2];
|
||||
unique_fd received;
|
||||
ASSERT_EQ(-1, ReceiveFileDescriptors(recv.get(), buf, 1, &received));
|
||||
ASSERT_EQ(EMSGSIZE, errno);
|
||||
ASSERT_EQ(-1, received.get());
|
||||
}
|
||||
|
||||
TEST_P(CmsgTest, peek) {
|
||||
ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get()));
|
||||
|
||||
char buf[2];
|
||||
ASSERT_EQ(1, ::recv(recv.get(), buf, sizeof(buf), MSG_PEEK));
|
||||
ASSERT_EQ('a', buf[0]);
|
||||
|
||||
unique_fd received;
|
||||
ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received));
|
||||
ASSERT_EQ(ino1, GetInode(received.get()));
|
||||
}
|
||||
|
||||
TEST_P(CmsgTest, stream_fd_association) {
|
||||
if (Seqpacket()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// fds are associated with the first byte of the write.
|
||||
ASSERT_EQ(1, TEMP_FAILURE_RETRY(write(send.get(), "a", 1)));
|
||||
ASSERT_EQ(2, SendFileDescriptors(send.get(), "bc", 2, fd1.get()));
|
||||
ASSERT_EQ(1, SendFileDescriptors(send.get(), "d", 1, fd2.get()));
|
||||
char buf[2];
|
||||
ASSERT_EQ(2, TEMP_FAILURE_RETRY(read(recv.get(), buf, 2)));
|
||||
ASSERT_EQ(0, memcmp(buf, "ab", 2));
|
||||
|
||||
std::vector<unique_fd> received1;
|
||||
ssize_t rc = ReceiveFileDescriptorVector(recv.get(), buf, 1, 1, &received1);
|
||||
ASSERT_EQ(1, rc);
|
||||
ASSERT_EQ('c', buf[0]);
|
||||
ASSERT_TRUE(received1.empty());
|
||||
|
||||
unique_fd received2;
|
||||
rc = ReceiveFileDescriptors(recv.get(), buf, 1, &received2);
|
||||
ASSERT_EQ(1, rc);
|
||||
ASSERT_EQ('d', buf[0]);
|
||||
ASSERT_EQ(ino2, GetInode(received2.get()));
|
||||
}
|
||||
|
||||
TEST_P(CmsgTest, multiple_fd_ordering) {
|
||||
ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get(), fd2.get()));
|
||||
|
||||
char buf[2];
|
||||
unique_fd received1, received2;
|
||||
ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received1, &received2));
|
||||
|
||||
ASSERT_NE(-1, received1.get());
|
||||
ASSERT_NE(-1, received2.get());
|
||||
|
||||
ASSERT_EQ(ino1, GetInode(received1.get()));
|
||||
ASSERT_EQ(ino2, GetInode(received2.get()));
|
||||
}
|
||||
|
||||
TEST_P(CmsgTest, separate_fd_ordering) {
|
||||
ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get()));
|
||||
ASSERT_EQ(1, SendFileDescriptors(send.get(), "b", 1, fd2.get()));
|
||||
|
||||
char buf[2];
|
||||
unique_fd received1, received2;
|
||||
ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received1));
|
||||
ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received2));
|
||||
|
||||
ASSERT_NE(-1, received1.get());
|
||||
ASSERT_NE(-1, received2.get());
|
||||
|
||||
ASSERT_EQ(ino1, GetInode(received1.get()));
|
||||
ASSERT_EQ(ino2, GetInode(received2.get()));
|
||||
}
|
||||
|
||||
TEST_P(CmsgTest, separate_fds_no_coalescing) {
|
||||
unique_fd sent1(dup(tmp1.fd));
|
||||
unique_fd sent2(dup(tmp2.fd));
|
||||
|
||||
ASSERT_EQ(1, SendFileDescriptors(send.get(), "", 1, fd1.get()));
|
||||
ASSERT_EQ(1, SendFileDescriptors(send.get(), "", 1, fd2.get()));
|
||||
|
||||
char buf[2];
|
||||
std::vector<unique_fd> received;
|
||||
ASSERT_EQ(1, ReceiveFileDescriptorVector(recv.get(), buf, 2, 2, &received));
|
||||
ASSERT_EQ(1U, received.size());
|
||||
ASSERT_EQ(ino1, GetInode(received[0].get()));
|
||||
|
||||
ASSERT_EQ(1, ReceiveFileDescriptorVector(recv.get(), buf, 2, 2, &received));
|
||||
ASSERT_EQ(1U, received.size());
|
||||
ASSERT_EQ(ino2, GetInode(received[0].get()));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(CmsgTest, CmsgTest, testing::Bool());
|
||||
|
||||
#endif
|
|
@ -1,69 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "android-base/endian.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
TEST(endian, constants) {
|
||||
ASSERT_TRUE(__LITTLE_ENDIAN == LITTLE_ENDIAN);
|
||||
ASSERT_TRUE(__BIG_ENDIAN == BIG_ENDIAN);
|
||||
ASSERT_TRUE(__BYTE_ORDER == BYTE_ORDER);
|
||||
|
||||
ASSERT_EQ(__LITTLE_ENDIAN, __BYTE_ORDER);
|
||||
}
|
||||
|
||||
TEST(endian, smoke) {
|
||||
static constexpr uint16_t le16 = 0x1234;
|
||||
static constexpr uint32_t le32 = 0x12345678;
|
||||
static constexpr uint64_t le64 = 0x123456789abcdef0;
|
||||
|
||||
static constexpr uint16_t be16 = 0x3412;
|
||||
static constexpr uint32_t be32 = 0x78563412;
|
||||
static constexpr uint64_t be64 = 0xf0debc9a78563412;
|
||||
|
||||
ASSERT_EQ(be16, htons(le16));
|
||||
ASSERT_EQ(be32, htonl(le32));
|
||||
ASSERT_EQ(be64, htonq(le64));
|
||||
|
||||
ASSERT_EQ(le16, ntohs(be16));
|
||||
ASSERT_EQ(le32, ntohl(be32));
|
||||
ASSERT_EQ(le64, ntohq(be64));
|
||||
|
||||
ASSERT_EQ(be16, htobe16(le16));
|
||||
ASSERT_EQ(be32, htobe32(le32));
|
||||
ASSERT_EQ(be64, htobe64(le64));
|
||||
|
||||
ASSERT_EQ(le16, betoh16(be16));
|
||||
ASSERT_EQ(le32, betoh32(be32));
|
||||
ASSERT_EQ(le64, betoh64(be64));
|
||||
|
||||
ASSERT_EQ(le16, htole16(le16));
|
||||
ASSERT_EQ(le32, htole32(le32));
|
||||
ASSERT_EQ(le64, htole64(le64));
|
||||
|
||||
ASSERT_EQ(le16, letoh16(le16));
|
||||
ASSERT_EQ(le32, letoh32(le32));
|
||||
ASSERT_EQ(le64, letoh64(le64));
|
||||
|
||||
ASSERT_EQ(le16, be16toh(be16));
|
||||
ASSERT_EQ(le32, be32toh(be32));
|
||||
ASSERT_EQ(le64, be64toh(be64));
|
||||
|
||||
ASSERT_EQ(le16, le16toh(le16));
|
||||
ASSERT_EQ(le32, le32toh(le32));
|
||||
ASSERT_EQ(le64, le64toh(le64));
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "android-base/errors.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
// Error strings aren't consistent enough across systems to test the output,
|
||||
// just make sure we can compile correctly and nothing crashes even if we send
|
||||
// it possibly bogus error codes.
|
||||
TEST(ErrorsTest, TestSystemErrorString) {
|
||||
SystemErrorCodeToString(-1);
|
||||
SystemErrorCodeToString(0);
|
||||
SystemErrorCodeToString(1);
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,30 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "android-base/errors.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
std::string SystemErrorCodeToString(int error_code) {
|
||||
return strerror(error_code);
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,68 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "android-base/errors.h"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "android-base/stringprintf.h"
|
||||
#include "android-base/strings.h"
|
||||
#include "android-base/utf8.h"
|
||||
|
||||
// A Windows error code is a DWORD. It's simpler to use an int error code for
|
||||
// both Unix and Windows if possible, but if this fails we'll need a different
|
||||
// function signature for each.
|
||||
static_assert(sizeof(int) >= sizeof(DWORD),
|
||||
"Windows system error codes are too large to fit in an int.");
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
static constexpr DWORD kErrorMessageBufferSize = 256;
|
||||
|
||||
std::string SystemErrorCodeToString(int int_error_code) {
|
||||
WCHAR msgbuf[kErrorMessageBufferSize];
|
||||
DWORD error_code = int_error_code;
|
||||
DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
|
||||
DWORD len = FormatMessageW(flags, nullptr, error_code, 0, msgbuf,
|
||||
kErrorMessageBufferSize, nullptr);
|
||||
if (len == 0) {
|
||||
return android::base::StringPrintf(
|
||||
"Error %lu while retrieving message for error %lu", GetLastError(),
|
||||
error_code);
|
||||
}
|
||||
|
||||
// Convert UTF-16 to UTF-8.
|
||||
std::string msg;
|
||||
if (!android::base::WideToUTF8(msgbuf, &msg)) {
|
||||
return android::base::StringPrintf(
|
||||
"Error %lu while converting message for error %lu from UTF-16 to UTF-8",
|
||||
GetLastError(), error_code);
|
||||
}
|
||||
|
||||
// Messages returned by the system end with line breaks.
|
||||
msg = android::base::Trim(msg);
|
||||
|
||||
// There are many Windows error messages compared to POSIX, so include the
|
||||
// numeric error code for easier, quicker, accurate identification. Use
|
||||
// decimal instead of hex because there are decimal ranges like 10000-11999
|
||||
// for Winsock.
|
||||
android::base::StringAppendF(&msg, " (%lu)", error_code);
|
||||
return msg;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,876 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "android-base/expected.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using android::base::expected;
|
||||
using android::base::unexpected;
|
||||
|
||||
typedef expected<int, int> exp_int;
|
||||
typedef expected<double, double> exp_double;
|
||||
typedef expected<std::string, std::string> exp_string;
|
||||
typedef expected<std::pair<std::string, int>, int> exp_pair;
|
||||
typedef expected<void, int> exp_void;
|
||||
|
||||
struct T {
|
||||
int a;
|
||||
int b;
|
||||
T() = default;
|
||||
T(int a, int b) noexcept : a(a), b(b) {}
|
||||
};
|
||||
bool operator==(const T& x, const T& y) {
|
||||
return x.a == y.a && x.b == y.b;
|
||||
}
|
||||
bool operator!=(const T& x, const T& y) {
|
||||
return x.a != y.a || x.b != y.b;
|
||||
}
|
||||
|
||||
struct E {
|
||||
std::string message;
|
||||
int cause;
|
||||
E(const std::string& message, int cause) : message(message), cause(cause) {}
|
||||
};
|
||||
|
||||
typedef expected<T,E> exp_complex;
|
||||
|
||||
TEST(Expected, testDefaultConstructible) {
|
||||
exp_int e;
|
||||
EXPECT_TRUE(e.has_value());
|
||||
EXPECT_EQ(0, e.value());
|
||||
|
||||
exp_complex e2;
|
||||
EXPECT_TRUE(e2.has_value());
|
||||
EXPECT_EQ(T(0,0), e2.value());
|
||||
|
||||
exp_void e3;
|
||||
EXPECT_TRUE(e3.has_value());
|
||||
}
|
||||
|
||||
TEST(Expected, testCopyConstructible) {
|
||||
exp_int e;
|
||||
exp_int e2 = e;
|
||||
|
||||
EXPECT_TRUE(e.has_value());
|
||||
EXPECT_TRUE(e2.has_value());
|
||||
EXPECT_EQ(0, e.value());
|
||||
EXPECT_EQ(0, e2.value());
|
||||
|
||||
exp_void e3;
|
||||
exp_void e4 = e3;
|
||||
EXPECT_TRUE(e3.has_value());
|
||||
EXPECT_TRUE(e4.has_value());
|
||||
}
|
||||
|
||||
TEST(Expected, testMoveConstructible) {
|
||||
exp_int e;
|
||||
exp_int e2 = std::move(e);
|
||||
|
||||
EXPECT_TRUE(e.has_value());
|
||||
EXPECT_TRUE(e2.has_value());
|
||||
EXPECT_EQ(0, e.value());
|
||||
EXPECT_EQ(0, e2.value());
|
||||
|
||||
exp_string e3(std::string("hello"));
|
||||
exp_string e4 = std::move(e3);
|
||||
|
||||
EXPECT_TRUE(e3.has_value());
|
||||
EXPECT_TRUE(e4.has_value());
|
||||
EXPECT_EQ("", e3.value()); // e3 is moved
|
||||
EXPECT_EQ("hello", e4.value());
|
||||
|
||||
exp_void e5;
|
||||
exp_void e6 = std::move(e5);
|
||||
EXPECT_TRUE(e5.has_value());
|
||||
EXPECT_TRUE(e6.has_value());
|
||||
}
|
||||
|
||||
TEST(Expected, testCopyConstructibleFromConvertibleType) {
|
||||
exp_double e = 3.3f;
|
||||
exp_int e2 = e;
|
||||
|
||||
EXPECT_TRUE(e.has_value());
|
||||
EXPECT_TRUE(e2.has_value());
|
||||
EXPECT_EQ(3.3f, e.value());
|
||||
EXPECT_EQ(3, e2.value());
|
||||
}
|
||||
|
||||
TEST(Expected, testMoveConstructibleFromConvertibleType) {
|
||||
exp_double e = 3.3f;
|
||||
exp_int e2 = std::move(e);
|
||||
|
||||
EXPECT_TRUE(e.has_value());
|
||||
EXPECT_TRUE(e2.has_value());
|
||||
EXPECT_EQ(3.3f, e.value());
|
||||
EXPECT_EQ(3, e2.value());
|
||||
}
|
||||
|
||||
TEST(Expected, testConstructibleFromValue) {
|
||||
exp_int e = 3;
|
||||
exp_double e2 = 5.5f;
|
||||
exp_string e3 = std::string("hello");
|
||||
exp_complex e4 = T(10, 20);
|
||||
exp_void e5 = {};
|
||||
|
||||
EXPECT_TRUE(e.has_value());
|
||||
EXPECT_TRUE(e2.has_value());
|
||||
EXPECT_TRUE(e3.has_value());
|
||||
EXPECT_TRUE(e4.has_value());
|
||||
EXPECT_TRUE(e5.has_value());
|
||||
EXPECT_EQ(3, e.value());
|
||||
EXPECT_EQ(5.5f, e2.value());
|
||||
EXPECT_EQ("hello", e3.value());
|
||||
EXPECT_EQ(T(10,20), e4.value());
|
||||
}
|
||||
|
||||
TEST(Expected, testConstructibleFromMovedValue) {
|
||||
std::string hello = "hello";
|
||||
exp_string e = std::move(hello);
|
||||
|
||||
EXPECT_TRUE(e.has_value());
|
||||
EXPECT_EQ("hello", e.value());
|
||||
EXPECT_EQ("", hello);
|
||||
}
|
||||
|
||||
TEST(Expected, testConstructibleFromConvertibleValue) {
|
||||
exp_int e = 3.3f; // double to int
|
||||
exp_string e2 = "hello"; // char* to std::string
|
||||
EXPECT_TRUE(e.has_value());
|
||||
EXPECT_EQ(3, e.value());
|
||||
|
||||
EXPECT_TRUE(e2.has_value());
|
||||
EXPECT_EQ("hello", e2.value());
|
||||
}
|
||||
|
||||
TEST(Expected, testConstructibleFromUnexpected) {
|
||||
exp_int::unexpected_type unexp = unexpected(10);
|
||||
exp_int e = unexp;
|
||||
|
||||
exp_double::unexpected_type unexp2 = unexpected(10.5f);
|
||||
exp_double e2 = unexp2;
|
||||
|
||||
exp_string::unexpected_type unexp3 = unexpected(std::string("error"));
|
||||
exp_string e3 = unexp3;
|
||||
|
||||
exp_void::unexpected_type unexp4 = unexpected(10);
|
||||
exp_void e4 = unexp4;
|
||||
|
||||
EXPECT_FALSE(e.has_value());
|
||||
EXPECT_FALSE(e2.has_value());
|
||||
EXPECT_FALSE(e3.has_value());
|
||||
EXPECT_FALSE(e4.has_value());
|
||||
EXPECT_EQ(10, e.error());
|
||||
EXPECT_EQ(10.5f, e2.error());
|
||||
EXPECT_EQ("error", e3.error());
|
||||
EXPECT_EQ(10, e4.error());
|
||||
}
|
||||
|
||||
TEST(Expected, testMoveConstructibleFromUnexpected) {
|
||||
exp_int e = unexpected(10);
|
||||
exp_double e2 = unexpected(10.5f);
|
||||
exp_string e3 = unexpected(std::string("error"));
|
||||
exp_void e4 = unexpected(10);
|
||||
|
||||
EXPECT_FALSE(e.has_value());
|
||||
EXPECT_FALSE(e2.has_value());
|
||||
EXPECT_FALSE(e3.has_value());
|
||||
EXPECT_FALSE(e4.has_value());
|
||||
EXPECT_EQ(10, e.error());
|
||||
EXPECT_EQ(10.5f, e2.error());
|
||||
EXPECT_EQ("error", e3.error());
|
||||
EXPECT_EQ(10, e4.error());
|
||||
}
|
||||
|
||||
TEST(Expected, testConstructibleByForwarding) {
|
||||
exp_string e(std::in_place, 5, 'a');
|
||||
EXPECT_TRUE(e.has_value());
|
||||
EXPECT_EQ("aaaaa", e.value());
|
||||
|
||||
exp_string e2({'a', 'b', 'c'});
|
||||
EXPECT_TRUE(e2.has_value());
|
||||
EXPECT_EQ("abc", e2.value());
|
||||
|
||||
exp_pair e3({"hello", 30});
|
||||
EXPECT_TRUE(e3.has_value());
|
||||
EXPECT_EQ("hello",e3->first);
|
||||
EXPECT_EQ(30,e3->second);
|
||||
|
||||
exp_void e4({});
|
||||
EXPECT_TRUE(e4.has_value());
|
||||
}
|
||||
|
||||
TEST(Expected, testDestructible) {
|
||||
bool destroyed = false;
|
||||
struct T {
|
||||
bool* flag_;
|
||||
T(bool* flag) : flag_(flag) {}
|
||||
~T() { *flag_ = true; }
|
||||
};
|
||||
{
|
||||
expected<T, int> exp = T(&destroyed);
|
||||
}
|
||||
EXPECT_TRUE(destroyed);
|
||||
}
|
||||
|
||||
TEST(Expected, testAssignable) {
|
||||
exp_int e = 10;
|
||||
exp_int e2 = 20;
|
||||
e = e2;
|
||||
|
||||
EXPECT_EQ(20, e.value());
|
||||
EXPECT_EQ(20, e2.value());
|
||||
|
||||
exp_int e3 = 10;
|
||||
exp_int e4 = 20;
|
||||
e3 = std::move(e4);
|
||||
|
||||
EXPECT_EQ(20, e3.value());
|
||||
EXPECT_EQ(20, e4.value());
|
||||
|
||||
exp_void e5 = unexpected(10);
|
||||
ASSERT_FALSE(e5.has_value());
|
||||
exp_void e6;
|
||||
e5 = e6;
|
||||
|
||||
EXPECT_TRUE(e5.has_value());
|
||||
EXPECT_TRUE(e6.has_value());
|
||||
}
|
||||
|
||||
TEST(Expected, testAssignableFromValue) {
|
||||
exp_int e = 10;
|
||||
e = 20;
|
||||
EXPECT_EQ(20, e.value());
|
||||
|
||||
exp_double e2 = 3.5f;
|
||||
e2 = 10.5f;
|
||||
EXPECT_EQ(10.5f, e2.value());
|
||||
|
||||
exp_string e3 = "hello";
|
||||
e3 = "world";
|
||||
EXPECT_EQ("world", e3.value());
|
||||
|
||||
exp_void e4 = unexpected(10);
|
||||
ASSERT_FALSE(e4.has_value());
|
||||
e4 = {};
|
||||
EXPECT_TRUE(e4.has_value());
|
||||
}
|
||||
|
||||
TEST(Expected, testAssignableFromUnexpected) {
|
||||
exp_int e = 10;
|
||||
e = unexpected(30);
|
||||
EXPECT_FALSE(e.has_value());
|
||||
EXPECT_EQ(30, e.error());
|
||||
|
||||
exp_double e2 = 3.5f;
|
||||
e2 = unexpected(10.5f);
|
||||
EXPECT_FALSE(e2.has_value());
|
||||
EXPECT_EQ(10.5f, e2.error());
|
||||
|
||||
exp_string e3 = "hello";
|
||||
e3 = unexpected("world");
|
||||
EXPECT_FALSE(e3.has_value());
|
||||
EXPECT_EQ("world", e3.error());
|
||||
|
||||
exp_void e4 = {};
|
||||
e4 = unexpected(10);
|
||||
EXPECT_FALSE(e4.has_value());
|
||||
EXPECT_EQ(10, e4.error());
|
||||
}
|
||||
|
||||
TEST(Expected, testAssignableFromMovedValue) {
|
||||
std::string world = "world";
|
||||
exp_string e = "hello";
|
||||
e = std::move(world);
|
||||
|
||||
EXPECT_TRUE(e.has_value());
|
||||
EXPECT_EQ("world", e.value());
|
||||
EXPECT_EQ("", world);
|
||||
}
|
||||
|
||||
TEST(Expected, testAssignableFromMovedUnexpected) {
|
||||
std::string world = "world";
|
||||
exp_string e = "hello";
|
||||
e = unexpected(std::move(world));
|
||||
|
||||
EXPECT_FALSE(e.has_value());
|
||||
EXPECT_EQ("world", e.error());
|
||||
EXPECT_EQ("", world);
|
||||
}
|
||||
|
||||
TEST(Expected, testEmplace) {
|
||||
struct T {
|
||||
int a;
|
||||
double b;
|
||||
T() {}
|
||||
T(int a, double b) noexcept : a(a), b(b) {}
|
||||
};
|
||||
expected<T, int> exp;
|
||||
T& t = exp.emplace(3, 10.5f);
|
||||
|
||||
EXPECT_TRUE(exp.has_value());
|
||||
EXPECT_EQ(3, t.a);
|
||||
EXPECT_EQ(10.5f, t.b);
|
||||
EXPECT_EQ(3, exp.value().a);
|
||||
EXPECT_EQ(10.5, exp.value().b);
|
||||
|
||||
exp_void e = unexpected(10);
|
||||
ASSERT_FALSE(e.has_value());
|
||||
e.emplace();
|
||||
EXPECT_TRUE(e.has_value());
|
||||
}
|
||||
|
||||
TEST(Expected, testSwapExpectedExpected) {
|
||||
exp_int e = 10;
|
||||
exp_int e2 = 20;
|
||||
e.swap(e2);
|
||||
|
||||
EXPECT_TRUE(e.has_value());
|
||||
EXPECT_TRUE(e2.has_value());
|
||||
EXPECT_EQ(20, e.value());
|
||||
EXPECT_EQ(10, e2.value());
|
||||
|
||||
exp_void e3;
|
||||
exp_void e4;
|
||||
e3.swap(e4);
|
||||
|
||||
EXPECT_TRUE(e3.has_value());
|
||||
EXPECT_TRUE(e4.has_value());
|
||||
}
|
||||
|
||||
TEST(Expected, testSwapUnexpectedUnexpected) {
|
||||
exp_int e = unexpected(10);
|
||||
exp_int e2 = unexpected(20);
|
||||
e.swap(e2);
|
||||
EXPECT_FALSE(e.has_value());
|
||||
EXPECT_FALSE(e2.has_value());
|
||||
EXPECT_EQ(20, e.error());
|
||||
EXPECT_EQ(10, e2.error());
|
||||
|
||||
exp_void e3 = unexpected(10);
|
||||
exp_void e4 = unexpected(20);
|
||||
e3.swap(e4);
|
||||
EXPECT_FALSE(e3.has_value());
|
||||
EXPECT_FALSE(e4.has_value());
|
||||
EXPECT_EQ(20, e3.error());
|
||||
EXPECT_EQ(10, e4.error());
|
||||
}
|
||||
|
||||
TEST(Expected, testSwapExpectedUnepected) {
|
||||
exp_int e = 10;
|
||||
exp_int e2 = unexpected(30);
|
||||
e.swap(e2);
|
||||
EXPECT_FALSE(e.has_value());
|
||||
EXPECT_TRUE(e2.has_value());
|
||||
EXPECT_EQ(30, e.error());
|
||||
EXPECT_EQ(10, e2.value());
|
||||
|
||||
exp_void e3;
|
||||
exp_void e4 = unexpected(10);
|
||||
e3.swap(e4);
|
||||
EXPECT_FALSE(e3.has_value());
|
||||
EXPECT_TRUE(e4.has_value());
|
||||
EXPECT_EQ(10, e3.error());
|
||||
}
|
||||
|
||||
TEST(Expected, testDereference) {
|
||||
struct T {
|
||||
int a;
|
||||
double b;
|
||||
T() {}
|
||||
T(int a, double b) : a(a), b(b) {}
|
||||
};
|
||||
expected<T, int> exp = T(3, 10.5f);
|
||||
|
||||
EXPECT_EQ(3, exp->a);
|
||||
EXPECT_EQ(10.5f, exp->b);
|
||||
|
||||
EXPECT_EQ(3, (*exp).a);
|
||||
EXPECT_EQ(10.5f, (*exp).b);
|
||||
}
|
||||
|
||||
TEST(Expected, testTest) {
|
||||
exp_int e = 10;
|
||||
EXPECT_TRUE(e.ok());
|
||||
EXPECT_TRUE(e.has_value());
|
||||
|
||||
exp_int e2 = unexpected(10);
|
||||
EXPECT_FALSE(e2.ok());
|
||||
EXPECT_FALSE(e2.has_value());
|
||||
}
|
||||
|
||||
TEST(Expected, testGetValue) {
|
||||
exp_int e = 10;
|
||||
EXPECT_EQ(10, e.value());
|
||||
EXPECT_EQ(10, e.value_or(20));
|
||||
|
||||
exp_int e2 = unexpected(10);
|
||||
EXPECT_EQ(10, e2.error());
|
||||
EXPECT_EQ(20, e2.value_or(20));
|
||||
}
|
||||
|
||||
TEST(Expected, testSameValues) {
|
||||
exp_int e = 10;
|
||||
exp_int e2 = 10;
|
||||
EXPECT_TRUE(e == e2);
|
||||
EXPECT_TRUE(e2 == e);
|
||||
EXPECT_FALSE(e != e2);
|
||||
EXPECT_FALSE(e2 != e);
|
||||
|
||||
exp_void e3;
|
||||
exp_void e4;
|
||||
EXPECT_TRUE(e3 == e4);
|
||||
EXPECT_TRUE(e4 == e3);
|
||||
EXPECT_FALSE(e3 != e4);
|
||||
EXPECT_FALSE(e4 != e3);
|
||||
}
|
||||
|
||||
TEST(Expected, testDifferentValues) {
|
||||
exp_int e = 10;
|
||||
exp_int e2 = 20;
|
||||
EXPECT_FALSE(e == e2);
|
||||
EXPECT_FALSE(e2 == e);
|
||||
EXPECT_TRUE(e != e2);
|
||||
EXPECT_TRUE(e2 != e);
|
||||
}
|
||||
|
||||
TEST(Expected, testValueWithError) {
|
||||
exp_int e = 10;
|
||||
exp_int e2 = unexpected(10);
|
||||
EXPECT_FALSE(e == e2);
|
||||
EXPECT_FALSE(e2 == e);
|
||||
EXPECT_TRUE(e != e2);
|
||||
EXPECT_TRUE(e2 != e);
|
||||
|
||||
exp_void e3;
|
||||
exp_void e4 = unexpected(10);
|
||||
EXPECT_FALSE(e3 == e4);
|
||||
EXPECT_FALSE(e4 == e3);
|
||||
EXPECT_TRUE(e3 != e4);
|
||||
EXPECT_TRUE(e4 != e3);
|
||||
}
|
||||
|
||||
TEST(Expected, testSameErrors) {
|
||||
exp_int e = unexpected(10);
|
||||
exp_int e2 = unexpected(10);
|
||||
EXPECT_TRUE(e == e2);
|
||||
EXPECT_TRUE(e2 == e);
|
||||
EXPECT_FALSE(e != e2);
|
||||
EXPECT_FALSE(e2 != e);
|
||||
|
||||
exp_void e3 = unexpected(10);
|
||||
exp_void e4 = unexpected(10);
|
||||
EXPECT_TRUE(e3 == e4);
|
||||
EXPECT_TRUE(e4 == e3);
|
||||
EXPECT_FALSE(e3 != e4);
|
||||
EXPECT_FALSE(e4 != e3);
|
||||
}
|
||||
|
||||
TEST(Expected, testDifferentErrors) {
|
||||
exp_int e = unexpected(10);
|
||||
exp_int e2 = unexpected(20);
|
||||
EXPECT_FALSE(e == e2);
|
||||
EXPECT_FALSE(e2 == e);
|
||||
EXPECT_TRUE(e != e2);
|
||||
EXPECT_TRUE(e2 != e);
|
||||
|
||||
exp_void e3 = unexpected(10);
|
||||
exp_void e4 = unexpected(20);
|
||||
EXPECT_FALSE(e3 == e4);
|
||||
EXPECT_FALSE(e4 == e3);
|
||||
EXPECT_TRUE(e3 != e4);
|
||||
EXPECT_TRUE(e4 != e3);
|
||||
}
|
||||
|
||||
TEST(Expected, testCompareWithSameError) {
|
||||
exp_int e = unexpected(10);
|
||||
exp_int::unexpected_type error = 10;
|
||||
EXPECT_TRUE(e == error);
|
||||
EXPECT_TRUE(error == e);
|
||||
EXPECT_FALSE(e != error);
|
||||
EXPECT_FALSE(error != e);
|
||||
|
||||
exp_void e2 = unexpected(10);
|
||||
exp_void::unexpected_type error2 = 10;
|
||||
EXPECT_TRUE(e2 == error2);
|
||||
EXPECT_TRUE(error2 == e2);
|
||||
EXPECT_FALSE(e2 != error2);
|
||||
EXPECT_FALSE(error2 != e2);
|
||||
}
|
||||
|
||||
TEST(Expected, testCompareWithDifferentError) {
|
||||
exp_int e = unexpected(10);
|
||||
exp_int::unexpected_type error = 20;
|
||||
EXPECT_FALSE(e == error);
|
||||
EXPECT_FALSE(error == e);
|
||||
EXPECT_TRUE(e != error);
|
||||
EXPECT_TRUE(error != e);
|
||||
|
||||
exp_void e2 = unexpected(10);
|
||||
exp_void::unexpected_type error2 = 20;
|
||||
EXPECT_FALSE(e2 == error2);
|
||||
EXPECT_FALSE(error2 == e2);
|
||||
EXPECT_TRUE(e2 != error2);
|
||||
EXPECT_TRUE(error2 != e2);
|
||||
}
|
||||
|
||||
TEST(Expected, testCompareDifferentType) {
|
||||
expected<int,int> e = 10;
|
||||
expected<int32_t, int> e2 = 10;
|
||||
EXPECT_TRUE(e == e2);
|
||||
e2 = 20;
|
||||
EXPECT_FALSE(e == e2);
|
||||
|
||||
expected<std::string_view,int> e3 = "hello";
|
||||
expected<std::string,int> e4 = "hello";
|
||||
EXPECT_TRUE(e3 == e4);
|
||||
e4 = "world";
|
||||
EXPECT_FALSE(e3 == e4);
|
||||
|
||||
expected<void,int> e5;
|
||||
expected<int,int> e6 = 10;
|
||||
EXPECT_FALSE(e5 == e6);
|
||||
EXPECT_FALSE(e6 == e5);
|
||||
}
|
||||
|
||||
TEST(Expected, testDivideExample) {
|
||||
struct QR {
|
||||
int quotient;
|
||||
int remainder;
|
||||
QR(int q, int r) noexcept : quotient(q), remainder(r) {}
|
||||
bool operator==(const QR& rhs) const {
|
||||
return quotient == rhs.quotient && remainder == rhs.remainder;
|
||||
}
|
||||
bool operator!=(const QR& rhs) const {
|
||||
return quotient != rhs.quotient || remainder == rhs.remainder;
|
||||
}
|
||||
};
|
||||
|
||||
auto divide = [](int x, int y) -> expected<QR,E> {
|
||||
if (y == 0) {
|
||||
return unexpected(E("divide by zero", -1));
|
||||
} else {
|
||||
return QR(x / y, x % y);
|
||||
}
|
||||
};
|
||||
|
||||
EXPECT_FALSE(divide(10, 0).ok());
|
||||
EXPECT_EQ("divide by zero", divide(10, 0).error().message);
|
||||
EXPECT_EQ(-1, divide(10, 0).error().cause);
|
||||
|
||||
EXPECT_TRUE(divide(10, 3).ok());
|
||||
EXPECT_EQ(QR(3, 1), *divide(10, 3));
|
||||
}
|
||||
|
||||
TEST(Expected, testPair) {
|
||||
auto test = [](bool yes) -> exp_pair {
|
||||
if (yes) {
|
||||
return exp_pair({"yes", 42});
|
||||
} else {
|
||||
return unexpected(42);
|
||||
}
|
||||
};
|
||||
|
||||
auto r = test(true);
|
||||
EXPECT_TRUE(r.ok());
|
||||
EXPECT_EQ("yes", r->first);
|
||||
}
|
||||
|
||||
TEST(Expected, testVoid) {
|
||||
auto test = [](bool ok) -> exp_void {
|
||||
if (ok) {
|
||||
return {};
|
||||
} else {
|
||||
return unexpected(10);
|
||||
}
|
||||
};
|
||||
|
||||
auto r = test(true);
|
||||
EXPECT_TRUE(r.ok());
|
||||
r = test(false);
|
||||
EXPECT_FALSE(r.ok());
|
||||
EXPECT_EQ(10, r.error());
|
||||
}
|
||||
|
||||
// copied from result_test.cpp
|
||||
struct ConstructorTracker {
|
||||
static size_t constructor_called;
|
||||
static size_t copy_constructor_called;
|
||||
static size_t move_constructor_called;
|
||||
static size_t copy_assignment_called;
|
||||
static size_t move_assignment_called;
|
||||
|
||||
template <typename T,
|
||||
typename std::enable_if_t<std::is_convertible_v<T, std::string>>* = nullptr>
|
||||
ConstructorTracker(T&& string) : string(string) {
|
||||
++constructor_called;
|
||||
}
|
||||
ConstructorTracker(const ConstructorTracker& ct) {
|
||||
++copy_constructor_called;
|
||||
string = ct.string;
|
||||
}
|
||||
ConstructorTracker(ConstructorTracker&& ct) noexcept {
|
||||
++move_constructor_called;
|
||||
string = std::move(ct.string);
|
||||
}
|
||||
ConstructorTracker& operator=(const ConstructorTracker& ct) {
|
||||
++copy_assignment_called;
|
||||
string = ct.string;
|
||||
return *this;
|
||||
}
|
||||
ConstructorTracker& operator=(ConstructorTracker&& ct) noexcept {
|
||||
++move_assignment_called;
|
||||
string = std::move(ct.string);
|
||||
return *this;
|
||||
}
|
||||
static void Reset() {
|
||||
constructor_called = 0;
|
||||
copy_constructor_called = 0;
|
||||
move_constructor_called = 0;
|
||||
copy_assignment_called = 0;
|
||||
move_assignment_called = 0;
|
||||
}
|
||||
std::string string;
|
||||
};
|
||||
|
||||
size_t ConstructorTracker::constructor_called = 0;
|
||||
size_t ConstructorTracker::copy_constructor_called = 0;
|
||||
size_t ConstructorTracker::move_constructor_called = 0;
|
||||
size_t ConstructorTracker::copy_assignment_called = 0;
|
||||
size_t ConstructorTracker::move_assignment_called = 0;
|
||||
|
||||
typedef expected<ConstructorTracker, int> exp_track;
|
||||
|
||||
TEST(Expected, testNumberOfCopies) {
|
||||
// default constructor
|
||||
ConstructorTracker::Reset();
|
||||
exp_track e("hello");
|
||||
EXPECT_EQ(1U, ConstructorTracker::constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
|
||||
|
||||
// copy constructor
|
||||
ConstructorTracker::Reset();
|
||||
exp_track e2 = e;
|
||||
EXPECT_EQ(0U, ConstructorTracker::constructor_called);
|
||||
EXPECT_EQ(1U, ConstructorTracker::copy_constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
|
||||
|
||||
// move constructor
|
||||
ConstructorTracker::Reset();
|
||||
exp_track e3 = std::move(e);
|
||||
EXPECT_EQ(0U, ConstructorTracker::constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
|
||||
EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
|
||||
|
||||
// construct from lvalue
|
||||
ConstructorTracker::Reset();
|
||||
ConstructorTracker ct = "hello";
|
||||
exp_track e4(ct);
|
||||
EXPECT_EQ(1U, ConstructorTracker::constructor_called);
|
||||
EXPECT_EQ(1U, ConstructorTracker::copy_constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
|
||||
|
||||
// construct from rvalue
|
||||
ConstructorTracker::Reset();
|
||||
ConstructorTracker ct2 = "hello";
|
||||
exp_track e5(std::move(ct2));
|
||||
EXPECT_EQ(1U, ConstructorTracker::constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
|
||||
EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
|
||||
|
||||
// copy assignment
|
||||
ConstructorTracker::Reset();
|
||||
exp_track e6 = "hello";
|
||||
exp_track e7 = "world";
|
||||
e7 = e6;
|
||||
EXPECT_EQ(2U, ConstructorTracker::constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
|
||||
EXPECT_EQ(1U, ConstructorTracker::copy_assignment_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
|
||||
|
||||
// move assignment
|
||||
ConstructorTracker::Reset();
|
||||
exp_track e8 = "hello";
|
||||
exp_track e9 = "world";
|
||||
e9 = std::move(e8);
|
||||
EXPECT_EQ(2U, ConstructorTracker::constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
|
||||
EXPECT_EQ(1U, ConstructorTracker::move_assignment_called);
|
||||
|
||||
// swap
|
||||
ConstructorTracker::Reset();
|
||||
exp_track e10 = "hello";
|
||||
exp_track e11 = "world";
|
||||
std::swap(e10, e11);
|
||||
EXPECT_EQ(2U, ConstructorTracker::constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
|
||||
EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
|
||||
EXPECT_EQ(2U, ConstructorTracker::move_assignment_called);
|
||||
}
|
||||
|
||||
TEST(Expected, testNoCopyOnReturn) {
|
||||
auto test = [](const std::string& in) -> exp_track {
|
||||
if (in.empty()) {
|
||||
return "literal string";
|
||||
}
|
||||
if (in == "test2") {
|
||||
return ConstructorTracker(in + in + "2");
|
||||
}
|
||||
ConstructorTracker result(in + " " + in);
|
||||
return result;
|
||||
};
|
||||
|
||||
ConstructorTracker::Reset();
|
||||
auto result1 = test("");
|
||||
ASSERT_TRUE(result1.ok());
|
||||
EXPECT_EQ("literal string", result1->string);
|
||||
EXPECT_EQ(1U, ConstructorTracker::constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
|
||||
|
||||
ConstructorTracker::Reset();
|
||||
auto result2 = test("test2");
|
||||
ASSERT_TRUE(result2.ok());
|
||||
EXPECT_EQ("test2test22", result2->string);
|
||||
EXPECT_EQ(1U, ConstructorTracker::constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
|
||||
EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
|
||||
|
||||
ConstructorTracker::Reset();
|
||||
auto result3 = test("test3");
|
||||
ASSERT_TRUE(result3.ok());
|
||||
EXPECT_EQ("test3 test3", result3->string);
|
||||
EXPECT_EQ(1U, ConstructorTracker::constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
|
||||
EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
|
||||
}
|
||||
|
||||
TEST(Expected, testNested) {
|
||||
expected<exp_string, std::string> e = "hello";
|
||||
|
||||
EXPECT_TRUE(e.ok());
|
||||
EXPECT_TRUE(e.has_value());
|
||||
EXPECT_TRUE(e.value().has_value());
|
||||
EXPECT_TRUE(e->ok());
|
||||
EXPECT_EQ("hello", e.value().value());
|
||||
|
||||
expected<exp_string, std::string> e2 = unexpected("world");
|
||||
EXPECT_FALSE(e2.has_value());
|
||||
EXPECT_FALSE(e2.ok());
|
||||
EXPECT_EQ("world", e2.error());
|
||||
|
||||
expected<exp_string, std::string> e3 = exp_string(unexpected("world"));
|
||||
EXPECT_TRUE(e3.has_value());
|
||||
EXPECT_FALSE(e3.value().has_value());
|
||||
EXPECT_TRUE(e3.ok());
|
||||
EXPECT_FALSE(e3->ok());
|
||||
EXPECT_EQ("world", e3.value().error());
|
||||
}
|
||||
|
||||
constexpr bool equals(const char* a, const char* b) {
|
||||
return (a == nullptr && b == nullptr) ||
|
||||
(a != nullptr && b != nullptr && *a == *b &&
|
||||
(*a == '\0' || equals(a + 1, b + 1)));
|
||||
}
|
||||
|
||||
TEST(Expected, testConstexpr) {
|
||||
// Compliation error will occur if these expressions can't be
|
||||
// evaluated at compile time
|
||||
constexpr exp_int e(3);
|
||||
constexpr exp_int::unexpected_type err(3);
|
||||
constexpr int i = 4;
|
||||
|
||||
// default constructor
|
||||
static_assert(exp_int().value() == 0);
|
||||
// copy constructor
|
||||
static_assert(exp_int(e).value() == 3);
|
||||
// move constructor
|
||||
static_assert(exp_int(exp_int(4)).value() == 4);
|
||||
// copy construct from value
|
||||
static_assert(exp_int(i).value() == 4);
|
||||
// copy construct from unexpected
|
||||
static_assert(exp_int(err).error() == 3);
|
||||
// move costruct from unexpected
|
||||
static_assert(exp_int(unexpected(3)).error() == 3);
|
||||
// observers
|
||||
static_assert(*exp_int(3) == 3);
|
||||
static_assert(exp_int(3).has_value() == true);
|
||||
static_assert(exp_int(3).value_or(4) == 3);
|
||||
|
||||
typedef expected<const char*, int> exp_s;
|
||||
constexpr exp_s s("hello");
|
||||
constexpr const char* c = "hello";
|
||||
static_assert(equals(exp_s().value(), nullptr));
|
||||
static_assert(equals(exp_s(s).value(), "hello"));
|
||||
static_assert(equals(exp_s(exp_s("hello")).value(), "hello"));
|
||||
static_assert(equals(exp_s("hello").value(), "hello"));
|
||||
static_assert(equals(exp_s(c).value(), "hello"));
|
||||
}
|
||||
|
||||
TEST(Expected, testWithNonConstructible) {
|
||||
struct AssertNotConstructed {
|
||||
AssertNotConstructed() = delete;
|
||||
};
|
||||
|
||||
expected<int, AssertNotConstructed> v(42);
|
||||
EXPECT_TRUE(v.has_value());
|
||||
EXPECT_EQ(42, v.value());
|
||||
|
||||
expected<AssertNotConstructed, int> e(unexpected(42));
|
||||
EXPECT_FALSE(e.has_value());
|
||||
EXPECT_EQ(42, e.error());
|
||||
}
|
||||
|
||||
TEST(Expected, testWithMoveOnlyType) {
|
||||
typedef expected<std::unique_ptr<int>,std::unique_ptr<int>> exp_ptr;
|
||||
exp_ptr e(std::make_unique<int>(3));
|
||||
exp_ptr e2(unexpected(std::make_unique<int>(4)));
|
||||
|
||||
EXPECT_TRUE(e.has_value());
|
||||
EXPECT_FALSE(e2.has_value());
|
||||
EXPECT_EQ(3, *(e.value()));
|
||||
EXPECT_EQ(4, *(e2.error()));
|
||||
|
||||
e2 = std::move(e);
|
||||
EXPECT_TRUE(e.has_value());
|
||||
EXPECT_TRUE(e2.has_value());
|
||||
EXPECT_EQ(3, *(e2.value()));
|
||||
}
|
522
base/file.cpp
522
base/file.cpp
|
@ -1,522 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "android-base/file.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <ftw.h>
|
||||
#include <libgen.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <mach-o/dyld.h>
|
||||
#endif
|
||||
#if defined(_WIN32)
|
||||
#include <direct.h>
|
||||
#include <windows.h>
|
||||
#define O_NOFOLLOW 0
|
||||
#define OS_PATH_SEPARATOR '\\'
|
||||
#else
|
||||
#define OS_PATH_SEPARATOR '/'
|
||||
#endif
|
||||
|
||||
#include "android-base/logging.h" // and must be after windows.h for ERROR
|
||||
#include "android-base/macros.h" // For TEMP_FAILURE_RETRY on Darwin.
|
||||
#include "android-base/unique_fd.h"
|
||||
#include "android-base/utf8.h"
|
||||
|
||||
namespace {
|
||||
|
||||
#ifdef _WIN32
|
||||
static int mkstemp(char* name_template, size_t size_in_chars) {
|
||||
std::wstring path;
|
||||
CHECK(android::base::UTF8ToWide(name_template, &path))
|
||||
<< "path can't be converted to wchar: " << name_template;
|
||||
if (_wmktemp_s(path.data(), path.size() + 1) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Use open() to match the close() that TemporaryFile's destructor does.
|
||||
// Use O_BINARY to match base file APIs.
|
||||
int fd = _wopen(path.c_str(), O_CREAT | O_EXCL | O_RDWR | O_BINARY, S_IRUSR | S_IWUSR);
|
||||
if (fd < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string path_utf8;
|
||||
CHECK(android::base::WideToUTF8(path, &path_utf8)) << "path can't be converted to utf8";
|
||||
CHECK(strcpy_s(name_template, size_in_chars, path_utf8.c_str()) == 0)
|
||||
<< "utf8 path can't be assigned back to name_template";
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static char* mkdtemp(char* name_template, size_t size_in_chars) {
|
||||
std::wstring path;
|
||||
CHECK(android::base::UTF8ToWide(name_template, &path))
|
||||
<< "path can't be converted to wchar: " << name_template;
|
||||
|
||||
if (_wmktemp_s(path.data(), path.size() + 1) != 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (_wmkdir(path.c_str()) != 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string path_utf8;
|
||||
CHECK(android::base::WideToUTF8(path, &path_utf8)) << "path can't be converted to utf8";
|
||||
CHECK(strcpy_s(name_template, size_in_chars, path_utf8.c_str()) == 0)
|
||||
<< "utf8 path can't be assigned back to name_template";
|
||||
|
||||
return name_template;
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string GetSystemTempDir() {
|
||||
#if defined(__ANDROID__)
|
||||
const auto* tmpdir = getenv("TMPDIR");
|
||||
if (tmpdir == nullptr) tmpdir = "/data/local/tmp";
|
||||
if (access(tmpdir, R_OK | W_OK | X_OK) == 0) {
|
||||
return tmpdir;
|
||||
}
|
||||
// Tests running in app context can't access /data/local/tmp,
|
||||
// so try current directory if /data/local/tmp is not accessible.
|
||||
return ".";
|
||||
#elif defined(_WIN32)
|
||||
wchar_t tmp_dir_w[MAX_PATH];
|
||||
DWORD result = GetTempPathW(std::size(tmp_dir_w), tmp_dir_w); // checks TMP env
|
||||
CHECK_NE(result, 0ul) << "GetTempPathW failed, error: " << GetLastError();
|
||||
CHECK_LT(result, std::size(tmp_dir_w)) << "path truncated to: " << result;
|
||||
|
||||
// GetTempPath() returns a path with a trailing slash, but init()
|
||||
// does not expect that, so remove it.
|
||||
if (tmp_dir_w[result - 1] == L'\\') {
|
||||
tmp_dir_w[result - 1] = L'\0';
|
||||
}
|
||||
|
||||
std::string tmp_dir;
|
||||
CHECK(android::base::WideToUTF8(tmp_dir_w, &tmp_dir)) << "path can't be converted to utf8";
|
||||
|
||||
return tmp_dir;
|
||||
#else
|
||||
const auto* tmpdir = getenv("TMPDIR");
|
||||
if (tmpdir == nullptr) tmpdir = "/tmp";
|
||||
return tmpdir;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TemporaryFile::TemporaryFile() {
|
||||
init(GetSystemTempDir());
|
||||
}
|
||||
|
||||
TemporaryFile::TemporaryFile(const std::string& tmp_dir) {
|
||||
init(tmp_dir);
|
||||
}
|
||||
|
||||
TemporaryFile::~TemporaryFile() {
|
||||
if (fd != -1) {
|
||||
close(fd);
|
||||
}
|
||||
if (remove_file_) {
|
||||
unlink(path);
|
||||
}
|
||||
}
|
||||
|
||||
int TemporaryFile::release() {
|
||||
int result = fd;
|
||||
fd = -1;
|
||||
return result;
|
||||
}
|
||||
|
||||
void TemporaryFile::init(const std::string& tmp_dir) {
|
||||
snprintf(path, sizeof(path), "%s%cTemporaryFile-XXXXXX", tmp_dir.c_str(), OS_PATH_SEPARATOR);
|
||||
#if defined(_WIN32)
|
||||
fd = mkstemp(path, sizeof(path));
|
||||
#else
|
||||
fd = mkstemp(path);
|
||||
#endif
|
||||
}
|
||||
|
||||
TemporaryDir::TemporaryDir() {
|
||||
init(GetSystemTempDir());
|
||||
}
|
||||
|
||||
TemporaryDir::~TemporaryDir() {
|
||||
if (!remove_dir_and_contents_) return;
|
||||
|
||||
auto callback = [](const char* child, const struct stat*, int file_type, struct FTW*) -> int {
|
||||
switch (file_type) {
|
||||
case FTW_D:
|
||||
case FTW_DP:
|
||||
case FTW_DNR:
|
||||
if (rmdir(child) == -1) {
|
||||
PLOG(ERROR) << "rmdir " << child;
|
||||
}
|
||||
break;
|
||||
case FTW_NS:
|
||||
default:
|
||||
if (rmdir(child) != -1) break;
|
||||
// FALLTHRU (for gcc, lint, pcc, etc; and following for clang)
|
||||
FALLTHROUGH_INTENDED;
|
||||
case FTW_F:
|
||||
case FTW_SL:
|
||||
case FTW_SLN:
|
||||
if (unlink(child) == -1) {
|
||||
PLOG(ERROR) << "unlink " << child;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
nftw(path, callback, 128, FTW_DEPTH | FTW_MOUNT | FTW_PHYS);
|
||||
}
|
||||
|
||||
bool TemporaryDir::init(const std::string& tmp_dir) {
|
||||
snprintf(path, sizeof(path), "%s%cTemporaryDir-XXXXXX", tmp_dir.c_str(), OS_PATH_SEPARATOR);
|
||||
#if defined(_WIN32)
|
||||
return (mkdtemp(path, sizeof(path)) != nullptr);
|
||||
#else
|
||||
return (mkdtemp(path) != nullptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
// Versions of standard library APIs that support UTF-8 strings.
|
||||
using namespace android::base::utf8;
|
||||
|
||||
bool ReadFdToString(borrowed_fd fd, std::string* content) {
|
||||
content->clear();
|
||||
|
||||
// Although original we had small files in mind, this code gets used for
|
||||
// very large files too, where the std::string growth heuristics might not
|
||||
// be suitable. https://code.google.com/p/android/issues/detail?id=258500.
|
||||
struct stat sb;
|
||||
if (fstat(fd.get(), &sb) != -1 && sb.st_size > 0) {
|
||||
content->reserve(sb.st_size);
|
||||
}
|
||||
|
||||
char buf[BUFSIZ] __attribute__((__uninitialized__));
|
||||
ssize_t n;
|
||||
while ((n = TEMP_FAILURE_RETRY(read(fd.get(), &buf[0], sizeof(buf)))) > 0) {
|
||||
content->append(buf, n);
|
||||
}
|
||||
return (n == 0) ? true : false;
|
||||
}
|
||||
|
||||
bool ReadFileToString(const std::string& path, std::string* content, bool follow_symlinks) {
|
||||
content->clear();
|
||||
|
||||
int flags = O_RDONLY | O_CLOEXEC | O_BINARY | (follow_symlinks ? 0 : O_NOFOLLOW);
|
||||
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags)));
|
||||
if (fd == -1) {
|
||||
return false;
|
||||
}
|
||||
return ReadFdToString(fd, content);
|
||||
}
|
||||
|
||||
bool WriteStringToFd(const std::string& content, borrowed_fd fd) {
|
||||
const char* p = content.data();
|
||||
size_t left = content.size();
|
||||
while (left > 0) {
|
||||
ssize_t n = TEMP_FAILURE_RETRY(write(fd.get(), p, left));
|
||||
if (n == -1) {
|
||||
return false;
|
||||
}
|
||||
p += n;
|
||||
left -= n;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool CleanUpAfterFailedWrite(const std::string& path) {
|
||||
// Something went wrong. Let's not leave a corrupt file lying around.
|
||||
int saved_errno = errno;
|
||||
unlink(path.c_str());
|
||||
errno = saved_errno;
|
||||
return false;
|
||||
}
|
||||
|
||||
#if !defined(_WIN32)
|
||||
bool WriteStringToFile(const std::string& content, const std::string& path,
|
||||
mode_t mode, uid_t owner, gid_t group,
|
||||
bool follow_symlinks) {
|
||||
int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY |
|
||||
(follow_symlinks ? 0 : O_NOFOLLOW);
|
||||
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode)));
|
||||
if (fd == -1) {
|
||||
PLOG(ERROR) << "android::WriteStringToFile open failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
// We do an explicit fchmod here because we assume that the caller really
|
||||
// meant what they said and doesn't want the umask-influenced mode.
|
||||
if (fchmod(fd, mode) == -1) {
|
||||
PLOG(ERROR) << "android::WriteStringToFile fchmod failed";
|
||||
return CleanUpAfterFailedWrite(path);
|
||||
}
|
||||
if (fchown(fd, owner, group) == -1) {
|
||||
PLOG(ERROR) << "android::WriteStringToFile fchown failed";
|
||||
return CleanUpAfterFailedWrite(path);
|
||||
}
|
||||
if (!WriteStringToFd(content, fd)) {
|
||||
PLOG(ERROR) << "android::WriteStringToFile write failed";
|
||||
return CleanUpAfterFailedWrite(path);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool WriteStringToFile(const std::string& content, const std::string& path,
|
||||
bool follow_symlinks) {
|
||||
int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY |
|
||||
(follow_symlinks ? 0 : O_NOFOLLOW);
|
||||
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags, 0666)));
|
||||
if (fd == -1) {
|
||||
return false;
|
||||
}
|
||||
return WriteStringToFd(content, fd) || CleanUpAfterFailedWrite(path);
|
||||
}
|
||||
|
||||
bool ReadFully(borrowed_fd fd, void* data, size_t byte_count) {
|
||||
uint8_t* p = reinterpret_cast<uint8_t*>(data);
|
||||
size_t remaining = byte_count;
|
||||
while (remaining > 0) {
|
||||
ssize_t n = TEMP_FAILURE_RETRY(read(fd.get(), p, remaining));
|
||||
if (n <= 0) return false;
|
||||
p += n;
|
||||
remaining -= n;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
// Windows implementation of pread. Note that this DOES move the file descriptors read position,
|
||||
// but it does so atomically.
|
||||
static ssize_t pread(borrowed_fd fd, void* data, size_t byte_count, off64_t offset) {
|
||||
DWORD bytes_read;
|
||||
OVERLAPPED overlapped;
|
||||
memset(&overlapped, 0, sizeof(OVERLAPPED));
|
||||
overlapped.Offset = static_cast<DWORD>(offset);
|
||||
overlapped.OffsetHigh = static_cast<DWORD>(offset >> 32);
|
||||
if (!ReadFile(reinterpret_cast<HANDLE>(_get_osfhandle(fd.get())), data,
|
||||
static_cast<DWORD>(byte_count), &bytes_read, &overlapped)) {
|
||||
// In case someone tries to read errno (since this is masquerading as a POSIX call)
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
return static_cast<ssize_t>(bytes_read);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool ReadFullyAtOffset(borrowed_fd fd, void* data, size_t byte_count, off64_t offset) {
|
||||
uint8_t* p = reinterpret_cast<uint8_t*>(data);
|
||||
while (byte_count > 0) {
|
||||
ssize_t n = TEMP_FAILURE_RETRY(pread(fd.get(), p, byte_count, offset));
|
||||
if (n <= 0) return false;
|
||||
p += n;
|
||||
byte_count -= n;
|
||||
offset += n;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteFully(borrowed_fd fd, const void* data, size_t byte_count) {
|
||||
const uint8_t* p = reinterpret_cast<const uint8_t*>(data);
|
||||
size_t remaining = byte_count;
|
||||
while (remaining > 0) {
|
||||
ssize_t n = TEMP_FAILURE_RETRY(write(fd.get(), p, remaining));
|
||||
if (n == -1) return false;
|
||||
p += n;
|
||||
remaining -= n;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RemoveFileIfExists(const std::string& path, std::string* err) {
|
||||
struct stat st;
|
||||
#if defined(_WIN32)
|
||||
// TODO: Windows version can't handle symbolic links correctly.
|
||||
int result = stat(path.c_str(), &st);
|
||||
bool file_type_removable = (result == 0 && S_ISREG(st.st_mode));
|
||||
#else
|
||||
int result = lstat(path.c_str(), &st);
|
||||
bool file_type_removable = (result == 0 && (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)));
|
||||
#endif
|
||||
if (result == -1) {
|
||||
if (errno == ENOENT || errno == ENOTDIR) return true;
|
||||
if (err != nullptr) *err = strerror(errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result == 0) {
|
||||
if (!file_type_removable) {
|
||||
if (err != nullptr) {
|
||||
*err = "is not a regular file or symbolic link";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (unlink(path.c_str()) == -1) {
|
||||
if (err != nullptr) {
|
||||
*err = strerror(errno);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#if !defined(_WIN32)
|
||||
bool Readlink(const std::string& path, std::string* result) {
|
||||
result->clear();
|
||||
|
||||
// Most Linux file systems (ext2 and ext4, say) limit symbolic links to
|
||||
// 4095 bytes. Since we'll copy out into the string anyway, it doesn't
|
||||
// waste memory to just start there. We add 1 so that we can recognize
|
||||
// whether it actually fit (rather than being truncated to 4095).
|
||||
std::vector<char> buf(4095 + 1);
|
||||
while (true) {
|
||||
ssize_t size = readlink(path.c_str(), &buf[0], buf.size());
|
||||
// Unrecoverable error?
|
||||
if (size == -1) return false;
|
||||
// It fit! (If size == buf.size(), it may have been truncated.)
|
||||
if (static_cast<size_t>(size) < buf.size()) {
|
||||
result->assign(&buf[0], size);
|
||||
return true;
|
||||
}
|
||||
// Double our buffer and try again.
|
||||
buf.resize(buf.size() * 2);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(_WIN32)
|
||||
bool Realpath(const std::string& path, std::string* result) {
|
||||
result->clear();
|
||||
|
||||
// realpath may exit with EINTR. Retry if so.
|
||||
char* realpath_buf = nullptr;
|
||||
do {
|
||||
realpath_buf = realpath(path.c_str(), nullptr);
|
||||
} while (realpath_buf == nullptr && errno == EINTR);
|
||||
|
||||
if (realpath_buf == nullptr) {
|
||||
return false;
|
||||
}
|
||||
result->assign(realpath_buf);
|
||||
free(realpath_buf);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string GetExecutablePath() {
|
||||
#if defined(__linux__)
|
||||
std::string path;
|
||||
android::base::Readlink("/proc/self/exe", &path);
|
||||
return path;
|
||||
#elif defined(__APPLE__)
|
||||
char path[PATH_MAX + 1];
|
||||
uint32_t path_len = sizeof(path);
|
||||
int rc = _NSGetExecutablePath(path, &path_len);
|
||||
if (rc < 0) {
|
||||
std::unique_ptr<char> path_buf(new char[path_len]);
|
||||
_NSGetExecutablePath(path_buf.get(), &path_len);
|
||||
return path_buf.get();
|
||||
}
|
||||
return path;
|
||||
#elif defined(_WIN32)
|
||||
char path[PATH_MAX + 1];
|
||||
DWORD result = GetModuleFileName(NULL, path, sizeof(path) - 1);
|
||||
if (result == 0 || result == sizeof(path) - 1) return "";
|
||||
path[PATH_MAX - 1] = 0;
|
||||
return path;
|
||||
#else
|
||||
#error unknown OS
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string GetExecutableDirectory() {
|
||||
return Dirname(GetExecutablePath());
|
||||
}
|
||||
|
||||
std::string Basename(const std::string& path) {
|
||||
// Copy path because basename may modify the string passed in.
|
||||
std::string result(path);
|
||||
|
||||
#if !defined(__BIONIC__)
|
||||
// Use lock because basename() may write to a process global and return a
|
||||
// pointer to that. Note that this locking strategy only works if all other
|
||||
// callers to basename in the process also grab this same lock, but its
|
||||
// better than nothing. Bionic's basename returns a thread-local buffer.
|
||||
static std::mutex& basename_lock = *new std::mutex();
|
||||
std::lock_guard<std::mutex> lock(basename_lock);
|
||||
#endif
|
||||
|
||||
// Note that if std::string uses copy-on-write strings, &str[0] will cause
|
||||
// the copy to be made, so there is no chance of us accidentally writing to
|
||||
// the storage for 'path'.
|
||||
char* name = basename(&result[0]);
|
||||
|
||||
// In case basename returned a pointer to a process global, copy that string
|
||||
// before leaving the lock.
|
||||
result.assign(name);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string Dirname(const std::string& path) {
|
||||
// Copy path because dirname may modify the string passed in.
|
||||
std::string result(path);
|
||||
|
||||
#if !defined(__BIONIC__)
|
||||
// Use lock because dirname() may write to a process global and return a
|
||||
// pointer to that. Note that this locking strategy only works if all other
|
||||
// callers to dirname in the process also grab this same lock, but its
|
||||
// better than nothing. Bionic's dirname returns a thread-local buffer.
|
||||
static std::mutex& dirname_lock = *new std::mutex();
|
||||
std::lock_guard<std::mutex> lock(dirname_lock);
|
||||
#endif
|
||||
|
||||
// Note that if std::string uses copy-on-write strings, &str[0] will cause
|
||||
// the copy to be made, so there is no chance of us accidentally writing to
|
||||
// the storage for 'path'.
|
||||
char* parent = dirname(&result[0]);
|
||||
|
||||
// In case dirname returned a pointer to a process global, copy that string
|
||||
// before leaving the lock.
|
||||
result.assign(parent);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,385 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "android-base/file.h"
|
||||
|
||||
#include "android-base/utf8.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#if !defined(_WIN32)
|
||||
#include <pwd.h>
|
||||
#else
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "android-base/logging.h" // and must be after windows.h for ERROR
|
||||
|
||||
TEST(file, ReadFileToString_ENOENT) {
|
||||
std::string s("hello");
|
||||
errno = 0;
|
||||
ASSERT_FALSE(android::base::ReadFileToString("/proc/does-not-exist", &s));
|
||||
EXPECT_EQ(ENOENT, errno);
|
||||
EXPECT_EQ("", s); // s was cleared.
|
||||
}
|
||||
|
||||
TEST(file, ReadFileToString_WriteStringToFile) {
|
||||
TemporaryFile tf;
|
||||
ASSERT_NE(tf.fd, -1) << tf.path;
|
||||
ASSERT_TRUE(android::base::WriteStringToFile("abc", tf.path))
|
||||
<< strerror(errno);
|
||||
std::string s;
|
||||
ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s))
|
||||
<< strerror(errno);
|
||||
EXPECT_EQ("abc", s);
|
||||
}
|
||||
|
||||
// symlinks require elevated privileges on Windows.
|
||||
#if !defined(_WIN32)
|
||||
TEST(file, ReadFileToString_WriteStringToFile_symlink) {
|
||||
TemporaryFile target, link;
|
||||
ASSERT_EQ(0, unlink(link.path));
|
||||
ASSERT_EQ(0, symlink(target.path, link.path));
|
||||
ASSERT_FALSE(android::base::WriteStringToFile("foo", link.path, false));
|
||||
ASSERT_EQ(ELOOP, errno);
|
||||
ASSERT_TRUE(android::base::WriteStringToFile("foo", link.path, true));
|
||||
|
||||
std::string s;
|
||||
ASSERT_FALSE(android::base::ReadFileToString(link.path, &s));
|
||||
ASSERT_EQ(ELOOP, errno);
|
||||
ASSERT_TRUE(android::base::ReadFileToString(link.path, &s, true));
|
||||
ASSERT_EQ("foo", s);
|
||||
}
|
||||
#endif
|
||||
|
||||
// WriteStringToFile2 is explicitly for setting Unix permissions, which make no
|
||||
// sense on Windows.
|
||||
#if !defined(_WIN32)
|
||||
TEST(file, WriteStringToFile2) {
|
||||
TemporaryFile tf;
|
||||
ASSERT_NE(tf.fd, -1) << tf.path;
|
||||
ASSERT_TRUE(android::base::WriteStringToFile("abc", tf.path, 0660,
|
||||
getuid(), getgid()))
|
||||
<< strerror(errno);
|
||||
struct stat sb;
|
||||
ASSERT_EQ(0, stat(tf.path, &sb));
|
||||
ASSERT_EQ(0660U, static_cast<unsigned int>(sb.st_mode & ~S_IFMT));
|
||||
ASSERT_EQ(getuid(), sb.st_uid);
|
||||
ASSERT_EQ(getgid(), sb.st_gid);
|
||||
std::string s;
|
||||
ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s))
|
||||
<< strerror(errno);
|
||||
EXPECT_EQ("abc", s);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
TEST(file, NonUnicodeCharsWindows) {
|
||||
constexpr auto kMaxEnvVariableValueSize = 32767;
|
||||
std::wstring old_tmp;
|
||||
old_tmp.resize(kMaxEnvVariableValueSize);
|
||||
old_tmp.resize(GetEnvironmentVariableW(L"TMP", old_tmp.data(), old_tmp.size()));
|
||||
if (old_tmp.empty()) {
|
||||
// Can't continue with empty TMP folder.
|
||||
return;
|
||||
}
|
||||
|
||||
std::wstring new_tmp = old_tmp;
|
||||
if (new_tmp.back() != L'\\') {
|
||||
new_tmp.push_back(L'\\');
|
||||
}
|
||||
|
||||
{
|
||||
auto path(new_tmp + L"锦绣成都\\");
|
||||
_wmkdir(path.c_str());
|
||||
ASSERT_TRUE(SetEnvironmentVariableW(L"TMP", path.c_str()));
|
||||
|
||||
TemporaryFile tf;
|
||||
ASSERT_NE(tf.fd, -1) << tf.path;
|
||||
ASSERT_TRUE(android::base::WriteStringToFd("abc", tf.fd));
|
||||
|
||||
ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
|
||||
|
||||
std::string s;
|
||||
ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s)) << strerror(errno);
|
||||
EXPECT_EQ("abc", s);
|
||||
}
|
||||
{
|
||||
auto path(new_tmp + L"директория с длинным именем\\");
|
||||
_wmkdir(path.c_str());
|
||||
ASSERT_TRUE(SetEnvironmentVariableW(L"TMP", path.c_str()));
|
||||
|
||||
TemporaryFile tf;
|
||||
ASSERT_NE(tf.fd, -1) << tf.path;
|
||||
ASSERT_TRUE(android::base::WriteStringToFd("abc", tf.fd));
|
||||
|
||||
ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
|
||||
|
||||
std::string s;
|
||||
ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s)) << strerror(errno);
|
||||
EXPECT_EQ("abc", s);
|
||||
}
|
||||
{
|
||||
auto path(new_tmp + L"äüöß weiß\\");
|
||||
_wmkdir(path.c_str());
|
||||
ASSERT_TRUE(SetEnvironmentVariableW(L"TMP", path.c_str()));
|
||||
|
||||
TemporaryFile tf;
|
||||
ASSERT_NE(tf.fd, -1) << tf.path;
|
||||
ASSERT_TRUE(android::base::WriteStringToFd("abc", tf.fd));
|
||||
|
||||
ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
|
||||
|
||||
std::string s;
|
||||
ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s)) << strerror(errno);
|
||||
EXPECT_EQ("abc", s);
|
||||
}
|
||||
|
||||
SetEnvironmentVariableW(L"TMP", old_tmp.c_str());
|
||||
}
|
||||
|
||||
TEST(file, RootDirectoryWindows) {
|
||||
constexpr auto kMaxEnvVariableValueSize = 32767;
|
||||
std::wstring old_tmp;
|
||||
bool tmp_is_empty = false;
|
||||
old_tmp.resize(kMaxEnvVariableValueSize);
|
||||
old_tmp.resize(GetEnvironmentVariableW(L"TMP", old_tmp.data(), old_tmp.size()));
|
||||
if (old_tmp.empty()) {
|
||||
tmp_is_empty = (GetLastError() == ERROR_ENVVAR_NOT_FOUND);
|
||||
}
|
||||
SetEnvironmentVariableW(L"TMP", L"C:");
|
||||
|
||||
TemporaryFile tf;
|
||||
ASSERT_NE(tf.fd, -1) << tf.path;
|
||||
|
||||
SetEnvironmentVariableW(L"TMP", tmp_is_empty ? nullptr : old_tmp.c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(file, WriteStringToFd) {
|
||||
TemporaryFile tf;
|
||||
ASSERT_NE(tf.fd, -1) << tf.path;
|
||||
ASSERT_TRUE(android::base::WriteStringToFd("abc", tf.fd));
|
||||
|
||||
ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
|
||||
|
||||
std::string s;
|
||||
ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s)) << strerror(errno);
|
||||
EXPECT_EQ("abc", s);
|
||||
}
|
||||
|
||||
TEST(file, WriteFully) {
|
||||
TemporaryFile tf;
|
||||
ASSERT_NE(tf.fd, -1) << tf.path;
|
||||
ASSERT_TRUE(android::base::WriteFully(tf.fd, "abc", 3));
|
||||
|
||||
ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
|
||||
|
||||
std::string s;
|
||||
s.resize(3);
|
||||
ASSERT_TRUE(android::base::ReadFully(tf.fd, &s[0], s.size()))
|
||||
<< strerror(errno);
|
||||
EXPECT_EQ("abc", s);
|
||||
|
||||
ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
|
||||
|
||||
s.resize(1024);
|
||||
ASSERT_FALSE(android::base::ReadFully(tf.fd, &s[0], s.size()));
|
||||
}
|
||||
|
||||
TEST(file, RemoveFileIfExists) {
|
||||
TemporaryFile tf;
|
||||
ASSERT_NE(tf.fd, -1) << tf.path;
|
||||
close(tf.fd);
|
||||
tf.fd = -1;
|
||||
std::string err;
|
||||
ASSERT_TRUE(android::base::RemoveFileIfExists(tf.path, &err)) << err;
|
||||
ASSERT_TRUE(android::base::RemoveFileIfExists(tf.path));
|
||||
TemporaryDir td;
|
||||
ASSERT_FALSE(android::base::RemoveFileIfExists(td.path));
|
||||
ASSERT_FALSE(android::base::RemoveFileIfExists(td.path, &err));
|
||||
ASSERT_EQ("is not a regular file or symbolic link", err);
|
||||
}
|
||||
|
||||
TEST(file, RemoveFileIfExists_ENOTDIR) {
|
||||
TemporaryFile tf;
|
||||
close(tf.fd);
|
||||
tf.fd = -1;
|
||||
std::string err{"xxx"};
|
||||
ASSERT_TRUE(android::base::RemoveFileIfExists(std::string{tf.path} + "/abc", &err));
|
||||
ASSERT_EQ("xxx", err);
|
||||
}
|
||||
|
||||
#if !defined(_WIN32)
|
||||
TEST(file, RemoveFileIfExists_EACCES) {
|
||||
// EACCES -- one of the directories in the path has no search permission
|
||||
// root can bypass permission restrictions, so drop root.
|
||||
if (getuid() == 0) {
|
||||
passwd* shell = getpwnam("shell");
|
||||
setgid(shell->pw_gid);
|
||||
setuid(shell->pw_uid);
|
||||
}
|
||||
|
||||
TemporaryDir td;
|
||||
TemporaryFile tf(td.path);
|
||||
close(tf.fd);
|
||||
tf.fd = -1;
|
||||
std::string err{"xxx"};
|
||||
// Remove dir's search permission.
|
||||
ASSERT_TRUE(chmod(td.path, S_IRUSR | S_IWUSR) == 0);
|
||||
ASSERT_FALSE(android::base::RemoveFileIfExists(tf.path, &err));
|
||||
ASSERT_EQ("Permission denied", err);
|
||||
// Set dir's search permission again.
|
||||
ASSERT_TRUE(chmod(td.path, S_IRWXU) == 0);
|
||||
ASSERT_TRUE(android::base::RemoveFileIfExists(tf.path, &err));
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(file, Readlink) {
|
||||
#if !defined(_WIN32)
|
||||
// Linux doesn't allow empty symbolic links.
|
||||
std::string min("x");
|
||||
// ext2 and ext4 both have PAGE_SIZE limits.
|
||||
// If file encryption is enabled, there's extra overhead to store the
|
||||
// size of the encrypted symlink target. There's also an off-by-one
|
||||
// in current kernels (and marlin/sailfish where we're seeing this
|
||||
// failure are still on 3.18, far from current). http://b/33306057.
|
||||
std::string max(static_cast<size_t>(4096 - 2 - 1 - 1), 'x');
|
||||
|
||||
TemporaryDir td;
|
||||
std::string min_path{std::string(td.path) + "/" + "min"};
|
||||
std::string max_path{std::string(td.path) + "/" + "max"};
|
||||
|
||||
ASSERT_EQ(0, symlink(min.c_str(), min_path.c_str()));
|
||||
ASSERT_EQ(0, symlink(max.c_str(), max_path.c_str()));
|
||||
|
||||
std::string result;
|
||||
|
||||
result = "wrong";
|
||||
ASSERT_TRUE(android::base::Readlink(min_path, &result));
|
||||
ASSERT_EQ(min, result);
|
||||
|
||||
result = "wrong";
|
||||
ASSERT_TRUE(android::base::Readlink(max_path, &result));
|
||||
ASSERT_EQ(max, result);
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(file, Realpath) {
|
||||
#if !defined(_WIN32)
|
||||
TemporaryDir td;
|
||||
std::string basename = android::base::Basename(td.path);
|
||||
std::string dir_name = android::base::Dirname(td.path);
|
||||
std::string base_dir_name = android::base::Basename(dir_name);
|
||||
|
||||
{
|
||||
std::string path = dir_name + "/../" + base_dir_name + "/" + basename;
|
||||
std::string result;
|
||||
ASSERT_TRUE(android::base::Realpath(path, &result));
|
||||
ASSERT_EQ(td.path, result);
|
||||
}
|
||||
|
||||
{
|
||||
std::string path = std::string(td.path) + "/..";
|
||||
std::string result;
|
||||
ASSERT_TRUE(android::base::Realpath(path, &result));
|
||||
ASSERT_EQ(dir_name, result);
|
||||
}
|
||||
|
||||
{
|
||||
errno = 0;
|
||||
std::string path = std::string(td.path) + "/foo.noent";
|
||||
std::string result = "wrong";
|
||||
ASSERT_TRUE(!android::base::Realpath(path, &result));
|
||||
ASSERT_TRUE(result.empty());
|
||||
ASSERT_EQ(ENOENT, errno);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(file, GetExecutableDirectory) {
|
||||
std::string path = android::base::GetExecutableDirectory();
|
||||
ASSERT_NE("", path);
|
||||
ASSERT_NE(android::base::GetExecutablePath(), path);
|
||||
ASSERT_EQ('/', path[0]);
|
||||
ASSERT_NE('/', path[path.size() - 1]);
|
||||
}
|
||||
|
||||
TEST(file, GetExecutablePath) {
|
||||
ASSERT_NE("", android::base::GetExecutablePath());
|
||||
}
|
||||
|
||||
TEST(file, Basename) {
|
||||
EXPECT_EQ("sh", android::base::Basename("/system/bin/sh"));
|
||||
EXPECT_EQ("sh", android::base::Basename("sh"));
|
||||
EXPECT_EQ("sh", android::base::Basename("/system/bin/sh/"));
|
||||
}
|
||||
|
||||
TEST(file, Dirname) {
|
||||
EXPECT_EQ("/system/bin", android::base::Dirname("/system/bin/sh"));
|
||||
EXPECT_EQ(".", android::base::Dirname("sh"));
|
||||
EXPECT_EQ("/system/bin", android::base::Dirname("/system/bin/sh/"));
|
||||
}
|
||||
|
||||
TEST(file, ReadFileToString_capacity) {
|
||||
TemporaryFile tf;
|
||||
ASSERT_NE(tf.fd, -1) << tf.path;
|
||||
|
||||
// For a huge file, the overhead should still be small.
|
||||
std::string s;
|
||||
size_t size = 16 * 1024 * 1024;
|
||||
ASSERT_TRUE(android::base::WriteStringToFile(std::string(size, 'x'), tf.path));
|
||||
ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s));
|
||||
EXPECT_EQ(size, s.size());
|
||||
EXPECT_LT(s.capacity(), size + 16);
|
||||
|
||||
// Even for weird badly-aligned sizes.
|
||||
size += 12345;
|
||||
ASSERT_TRUE(android::base::WriteStringToFile(std::string(size, 'x'), tf.path));
|
||||
ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s));
|
||||
EXPECT_EQ(size, s.size());
|
||||
EXPECT_LT(s.capacity(), size + 16);
|
||||
|
||||
// We'll shrink an enormous string if you read a small file into it.
|
||||
size = 64;
|
||||
ASSERT_TRUE(android::base::WriteStringToFile(std::string(size, 'x'), tf.path));
|
||||
ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s));
|
||||
EXPECT_EQ(size, s.size());
|
||||
EXPECT_LT(s.capacity(), size + 16);
|
||||
}
|
||||
|
||||
TEST(file, ReadFileToString_capacity_0) {
|
||||
TemporaryFile tf;
|
||||
ASSERT_NE(tf.fd, -1) << tf.path;
|
||||
|
||||
// Because /proc reports its files as zero-length, we don't actually trust
|
||||
// any file that claims to be zero-length. Rather than add increasingly
|
||||
// complex heuristics for shrinking the passed-in string in that case, we
|
||||
// currently leave it alone.
|
||||
std::string s;
|
||||
size_t initial_capacity = s.capacity();
|
||||
ASSERT_TRUE(android::base::WriteStringToFile("", tf.path));
|
||||
ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s));
|
||||
EXPECT_EQ(0U, s.size());
|
||||
EXPECT_EQ(initial_capacity, s.capacity());
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "android-base/format.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
#include "android-base/stringprintf.h"
|
||||
|
||||
using android::base::StringPrintf;
|
||||
|
||||
static void BenchmarkFormatInt(benchmark::State& state) {
|
||||
for (auto _ : state) {
|
||||
benchmark::DoNotOptimize(fmt::format("{} {} {}", 42, std::numeric_limits<int>::min(),
|
||||
std::numeric_limits<int>::max()));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK(BenchmarkFormatInt);
|
||||
|
||||
static void BenchmarkStringPrintfInt(benchmark::State& state) {
|
||||
for (auto _ : state) {
|
||||
benchmark::DoNotOptimize(StringPrintf("%d %d %d", 42, std::numeric_limits<int>::min(),
|
||||
std::numeric_limits<int>::max()));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK(BenchmarkStringPrintfInt);
|
||||
|
||||
static void BenchmarkFormatFloat(benchmark::State& state) {
|
||||
for (auto _ : state) {
|
||||
benchmark::DoNotOptimize(fmt::format("{} {} {}", 42.42, std::numeric_limits<float>::min(),
|
||||
std::numeric_limits<float>::max()));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK(BenchmarkFormatFloat);
|
||||
|
||||
static void BenchmarkStringPrintfFloat(benchmark::State& state) {
|
||||
for (auto _ : state) {
|
||||
benchmark::DoNotOptimize(StringPrintf("%f %f %f", 42.42, std::numeric_limits<float>::min(),
|
||||
std::numeric_limits<float>::max()));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK(BenchmarkStringPrintfFloat);
|
||||
|
||||
static void BenchmarkFormatStrings(benchmark::State& state) {
|
||||
for (auto _ : state) {
|
||||
benchmark::DoNotOptimize(fmt::format("{} hello there {}", "hi,", "!!"));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK(BenchmarkFormatStrings);
|
||||
|
||||
static void BenchmarkStringPrintfStrings(benchmark::State& state) {
|
||||
for (auto _ : state) {
|
||||
benchmark::DoNotOptimize(StringPrintf("%s hello there %s", "hi,", "!!"));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK(BenchmarkStringPrintfStrings);
|
||||
|
||||
// Run the benchmark
|
||||
BENCHMARK_MAIN();
|
|
@ -1,53 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <sstream>
|
||||
|
||||
#if __cplusplus > 201103L && !defined(__WIN32) // C++14
|
||||
using namespace std::chrono_literals;
|
||||
#endif
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
// A std::chrono clock based on CLOCK_BOOTTIME.
|
||||
class boot_clock {
|
||||
public:
|
||||
typedef std::chrono::nanoseconds duration;
|
||||
typedef std::chrono::time_point<boot_clock, duration> time_point;
|
||||
|
||||
static time_point now();
|
||||
};
|
||||
|
||||
class Timer {
|
||||
public:
|
||||
Timer() : start_(boot_clock::now()) {}
|
||||
|
||||
std::chrono::milliseconds duration() const {
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(boot_clock::now() - start_);
|
||||
}
|
||||
|
||||
private:
|
||||
boot_clock::time_point start_;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Timer& t);
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,106 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/collections.h>
|
||||
#include <android-base/macros.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
#if !defined(_WIN32)
|
||||
|
||||
// Helpers for sending and receiving file descriptors across Unix domain sockets.
|
||||
//
|
||||
// The cmsg(3) API is very hard to get right, with multiple landmines that can
|
||||
// lead to death. Almost all of the uses of cmsg in Android make at least one of
|
||||
// the following mistakes:
|
||||
//
|
||||
// - not aligning the cmsg buffer
|
||||
// - leaking fds if more fds are received than expected
|
||||
// - blindly dereferencing CMSG_DATA without checking the header
|
||||
// - using CMSG_SPACE instead of CMSG_LEN for .cmsg_len
|
||||
// - using CMSG_LEN instead of CMSG_SPACE for .msg_controllen
|
||||
// - using a length specified in number of fds instead of bytes
|
||||
//
|
||||
// These functions wrap the hard-to-use cmsg API with an easier to use abstraction.
|
||||
|
||||
// Send file descriptors across a Unix domain socket.
|
||||
//
|
||||
// Note that the write can return short if the socket type is SOCK_STREAM. When
|
||||
// this happens, file descriptors are still sent to the other end, but with
|
||||
// truncated data. For this reason, using SOCK_SEQPACKET or SOCK_DGRAM is recommended.
|
||||
ssize_t SendFileDescriptorVector(borrowed_fd sock, const void* data, size_t len,
|
||||
const std::vector<int>& fds);
|
||||
|
||||
// Receive file descriptors from a Unix domain socket.
|
||||
//
|
||||
// If more FDs (or bytes, for datagram sockets) are received than expected,
|
||||
// -1 is returned with errno set to EMSGSIZE, and all received FDs are thrown away.
|
||||
ssize_t ReceiveFileDescriptorVector(borrowed_fd sock, void* data, size_t len, size_t max_fds,
|
||||
std::vector<android::base::unique_fd>* fds);
|
||||
|
||||
// Helper for SendFileDescriptorVector that constructs a std::vector for you, e.g.:
|
||||
// SendFileDescriptors(sock, "foo", 3, std::move(fd1), std::move(fd2))
|
||||
template <typename... Args>
|
||||
ssize_t SendFileDescriptors(borrowed_fd sock, const void* data, size_t len, Args&&... sent_fds) {
|
||||
// Do not allow implicit conversion to int: people might try to do something along the lines of:
|
||||
// SendFileDescriptors(..., std::move(a_unique_fd))
|
||||
// and be surprised when the unique_fd isn't closed afterwards.
|
||||
AssertType<int>(std::forward<Args>(sent_fds)...);
|
||||
std::vector<int> fds;
|
||||
Append(fds, std::forward<Args>(sent_fds)...);
|
||||
return SendFileDescriptorVector(sock, data, len, fds);
|
||||
}
|
||||
|
||||
// Helper for ReceiveFileDescriptorVector that receives an exact number of file descriptors.
|
||||
// If more file descriptors are received than requested, -1 is returned with errno set to EMSGSIZE.
|
||||
// If fewer file descriptors are received than requested, -1 is returned with errno set to ENOMSG.
|
||||
// In both cases, all arguments are cleared and any received FDs are thrown away.
|
||||
template <typename... Args>
|
||||
ssize_t ReceiveFileDescriptors(borrowed_fd sock, void* data, size_t len, Args&&... received_fds) {
|
||||
std::vector<unique_fd*> fds;
|
||||
Append(fds, std::forward<Args>(received_fds)...);
|
||||
|
||||
std::vector<unique_fd> result;
|
||||
ssize_t rc = ReceiveFileDescriptorVector(sock, data, len, fds.size(), &result);
|
||||
if (rc == -1 || result.size() != fds.size()) {
|
||||
int err = rc == -1 ? errno : ENOMSG;
|
||||
for (unique_fd* fd : fds) {
|
||||
fd->reset();
|
||||
}
|
||||
errno = err;
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < fds.size(); ++i) {
|
||||
*fds[i] = std::move(result[i]);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,60 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
// Helpers for converting a variadic template parameter pack to a homogeneous collection.
|
||||
// Parameters must be implictly convertible to the contained type (including via move/copy ctors).
|
||||
//
|
||||
// Use as follows:
|
||||
//
|
||||
// template <typename... Args>
|
||||
// std::vector<int> CreateVector(Args&&... args) {
|
||||
// std::vector<int> result;
|
||||
// Append(result, std::forward<Args>(args)...);
|
||||
// return result;
|
||||
// }
|
||||
template <typename CollectionType, typename T>
|
||||
void Append(CollectionType& collection, T&& arg) {
|
||||
collection.push_back(std::forward<T>(arg));
|
||||
}
|
||||
|
||||
template <typename CollectionType, typename T, typename... Args>
|
||||
void Append(CollectionType& collection, T&& arg, Args&&... args) {
|
||||
collection.push_back(std::forward<T>(arg));
|
||||
return Append(collection, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// Assert that all of the arguments in a variadic template parameter pack are of a given type
|
||||
// after std::decay.
|
||||
template <typename T, typename Arg, typename... Args>
|
||||
void AssertType(Arg&&) {
|
||||
static_assert(std::is_same<T, typename std::decay<Arg>::type>::value);
|
||||
}
|
||||
|
||||
template <typename T, typename Arg, typename... Args>
|
||||
void AssertType(Arg&&, Args&&... args) {
|
||||
static_assert(std::is_same<T, typename std::decay<Arg>::type>::value);
|
||||
AssertType<T>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,98 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/* A cross-platform equivalent of bionic's <sys/endian.h>. */
|
||||
|
||||
/* For __BIONIC__ and __GLIBC__ */
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#if defined(__BIONIC__)
|
||||
|
||||
#include <sys/endian.h>
|
||||
|
||||
#elif defined(__GLIBC__)
|
||||
|
||||
/* glibc's <endian.h> is like bionic's <sys/endian.h>. */
|
||||
#include <endian.h>
|
||||
|
||||
/* glibc keeps htons and htonl in <netinet/in.h>. */
|
||||
#include <netinet/in.h>
|
||||
|
||||
/* glibc doesn't have the 64-bit variants. */
|
||||
#define htonq(x) htobe64(x)
|
||||
#define ntohq(x) be64toh(x)
|
||||
|
||||
/* glibc has different names to BSD for these. */
|
||||
#define betoh16(x) be16toh(x)
|
||||
#define betoh32(x) be32toh(x)
|
||||
#define betoh64(x) be64toh(x)
|
||||
#define letoh16(x) le16toh(x)
|
||||
#define letoh32(x) le32toh(x)
|
||||
#define letoh64(x) le64toh(x)
|
||||
|
||||
#else
|
||||
|
||||
#if defined(__APPLE__)
|
||||
/* macOS has some of the basics. */
|
||||
#include <sys/_endian.h>
|
||||
#else
|
||||
/* Windows has some of the basics as well. */
|
||||
#include <sys/param.h>
|
||||
#include <winsock2.h>
|
||||
/* winsock2.h *must* be included before the following four macros. */
|
||||
#define htons(x) __builtin_bswap16(x)
|
||||
#define htonl(x) __builtin_bswap32(x)
|
||||
#define ntohs(x) __builtin_bswap16(x)
|
||||
#define ntohl(x) __builtin_bswap32(x)
|
||||
#endif
|
||||
|
||||
/* Neither macOS nor Windows have the rest. */
|
||||
|
||||
#define __LITTLE_ENDIAN 1234
|
||||
#define __BIG_ENDIAN 4321
|
||||
#define __BYTE_ORDER __LITTLE_ENDIAN
|
||||
|
||||
#define htonq(x) __builtin_bswap64(x)
|
||||
|
||||
#define ntohq(x) __builtin_bswap64(x)
|
||||
|
||||
#define htobe16(x) __builtin_bswap16(x)
|
||||
#define htobe32(x) __builtin_bswap32(x)
|
||||
#define htobe64(x) __builtin_bswap64(x)
|
||||
|
||||
#define betoh16(x) __builtin_bswap16(x)
|
||||
#define betoh32(x) __builtin_bswap32(x)
|
||||
#define betoh64(x) __builtin_bswap64(x)
|
||||
|
||||
#define htole16(x) (x)
|
||||
#define htole32(x) (x)
|
||||
#define htole64(x) (x)
|
||||
|
||||
#define letoh16(x) (x)
|
||||
#define letoh32(x) (x)
|
||||
#define letoh64(x) (x)
|
||||
|
||||
#define be16toh(x) __builtin_bswap16(x)
|
||||
#define be32toh(x) __builtin_bswap32(x)
|
||||
#define be64toh(x) __builtin_bswap64(x)
|
||||
|
||||
#define le16toh(x) (x)
|
||||
#define le32toh(x) (x)
|
||||
#define le64toh(x) (x)
|
||||
|
||||
#endif
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "errno.h"
|
||||
|
||||
#include "android-base/macros.h"
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
class ErrnoRestorer {
|
||||
public:
|
||||
ErrnoRestorer() : saved_errno_(errno) {}
|
||||
|
||||
~ErrnoRestorer() { errno = saved_errno_; }
|
||||
|
||||
// Allow this object to be used as part of && operation.
|
||||
operator bool() const { return true; }
|
||||
|
||||
private:
|
||||
const int saved_errno_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ErrnoRestorer);
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// Portable error handling functions. This is only necessary for host-side
|
||||
// code that needs to be cross-platform; code that is only run on Unix should
|
||||
// just use errno and strerror() for simplicity.
|
||||
//
|
||||
// There is some complexity since Windows has (at least) three different error
|
||||
// numbers, not all of which share the same type:
|
||||
// * errno: for C runtime errors.
|
||||
// * GetLastError(): Windows non-socket errors.
|
||||
// * WSAGetLastError(): Windows socket errors.
|
||||
// errno can be passed to strerror() on all platforms, but the other two require
|
||||
// special handling to get the error string. Refer to Microsoft documentation
|
||||
// to determine which error code to check for each function.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
// Returns a string describing the given system error code. |error_code| must
|
||||
// be errno on Unix or GetLastError()/WSAGetLastError() on Windows. Passing
|
||||
// errno on Windows has undefined behavior.
|
||||
std::string SystemErrorCodeToString(int error_code);
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,744 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <initializer_list>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
// android::base::expected is an Android implementation of the std::expected
|
||||
// proposal.
|
||||
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0323r7.html
|
||||
//
|
||||
// Usage:
|
||||
// using android::base::expected;
|
||||
// using android::base::unexpected;
|
||||
//
|
||||
// expected<double,std::string> safe_divide(double i, double j) {
|
||||
// if (j == 0) return unexpected("divide by zero");
|
||||
// else return i / j;
|
||||
// }
|
||||
//
|
||||
// void test() {
|
||||
// auto q = safe_divide(10, 0);
|
||||
// if (q) { printf("%f\n", q.value()); }
|
||||
// else { printf("%s\n", q.error().c_str()); }
|
||||
// }
|
||||
//
|
||||
// When the proposal becomes part of the standard and is implemented by
|
||||
// libcxx, this will be removed and android::base::expected will be
|
||||
// type alias to std::expected.
|
||||
//
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
// Synopsis
|
||||
template<class T, class E>
|
||||
class expected;
|
||||
|
||||
template<class E>
|
||||
class unexpected;
|
||||
template<class E>
|
||||
unexpected(E) -> unexpected<E>;
|
||||
|
||||
template<class E>
|
||||
class bad_expected_access;
|
||||
|
||||
template<>
|
||||
class bad_expected_access<void>;
|
||||
|
||||
struct unexpect_t {
|
||||
explicit unexpect_t() = default;
|
||||
};
|
||||
inline constexpr unexpect_t unexpect{};
|
||||
|
||||
// macros for SFINAE
|
||||
#define _ENABLE_IF(...) \
|
||||
, std::enable_if_t<(__VA_ARGS__)>* = nullptr
|
||||
|
||||
// Define NODISCARD_EXPECTED to prevent expected<T,E> from being
|
||||
// ignored when used as a return value. This is off by default.
|
||||
#ifdef NODISCARD_EXPECTED
|
||||
#define _NODISCARD_ [[nodiscard]]
|
||||
#else
|
||||
#define _NODISCARD_
|
||||
#endif
|
||||
|
||||
// Class expected
|
||||
template<class T, class E>
|
||||
class _NODISCARD_ expected {
|
||||
public:
|
||||
using value_type = T;
|
||||
using error_type = E;
|
||||
using unexpected_type = unexpected<E>;
|
||||
|
||||
template<class U>
|
||||
using rebind = expected<U, error_type>;
|
||||
|
||||
// constructors
|
||||
constexpr expected() = default;
|
||||
constexpr expected(const expected& rhs) = default;
|
||||
constexpr expected(expected&& rhs) noexcept = default;
|
||||
|
||||
template<class U, class G _ENABLE_IF(
|
||||
std::is_constructible_v<T, const U&> &&
|
||||
std::is_constructible_v<E, const G&> &&
|
||||
!std::is_constructible_v<T, expected<U, G>&> &&
|
||||
!std::is_constructible_v<T, expected<U, G>&&> &&
|
||||
!std::is_constructible_v<T, const expected<U, G>&> &&
|
||||
!std::is_constructible_v<T, const expected<U, G>&&> &&
|
||||
!std::is_convertible_v<expected<U, G>&, T> &&
|
||||
!std::is_convertible_v<expected<U, G>&&, T> &&
|
||||
!std::is_convertible_v<const expected<U, G>&, T> &&
|
||||
!std::is_convertible_v<const expected<U, G>&&, T> &&
|
||||
!(!std::is_convertible_v<const U&, T> ||
|
||||
!std::is_convertible_v<const G&, E>) /* non-explicit */
|
||||
)>
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
constexpr expected(const expected<U, G>& rhs) {
|
||||
if (rhs.has_value()) var_ = rhs.value();
|
||||
else var_ = unexpected(rhs.error());
|
||||
}
|
||||
|
||||
template<class U, class G _ENABLE_IF(
|
||||
std::is_constructible_v<T, const U&> &&
|
||||
std::is_constructible_v<E, const G&> &&
|
||||
!std::is_constructible_v<T, expected<U, G>&> &&
|
||||
!std::is_constructible_v<T, expected<U, G>&&> &&
|
||||
!std::is_constructible_v<T, const expected<U, G>&> &&
|
||||
!std::is_constructible_v<T, const expected<U, G>&&> &&
|
||||
!std::is_convertible_v<expected<U, G>&, T> &&
|
||||
!std::is_convertible_v<expected<U, G>&&, T> &&
|
||||
!std::is_convertible_v<const expected<U, G>&, T> &&
|
||||
!std::is_convertible_v<const expected<U, G>&&, T> &&
|
||||
(!std::is_convertible_v<const U&, T> ||
|
||||
!std::is_convertible_v<const G&, E>) /* explicit */
|
||||
)>
|
||||
constexpr explicit expected(const expected<U, G>& rhs) {
|
||||
if (rhs.has_value()) var_ = rhs.value();
|
||||
else var_ = unexpected(rhs.error());
|
||||
}
|
||||
|
||||
template<class U, class G _ENABLE_IF(
|
||||
std::is_constructible_v<T, const U&> &&
|
||||
std::is_constructible_v<E, const G&> &&
|
||||
!std::is_constructible_v<T, expected<U, G>&> &&
|
||||
!std::is_constructible_v<T, expected<U, G>&&> &&
|
||||
!std::is_constructible_v<T, const expected<U, G>&> &&
|
||||
!std::is_constructible_v<T, const expected<U, G>&&> &&
|
||||
!std::is_convertible_v<expected<U, G>&, T> &&
|
||||
!std::is_convertible_v<expected<U, G>&&, T> &&
|
||||
!std::is_convertible_v<const expected<U, G>&, T> &&
|
||||
!std::is_convertible_v<const expected<U, G>&&, T> &&
|
||||
!(!std::is_convertible_v<const U&, T> ||
|
||||
!std::is_convertible_v<const G&, E>) /* non-explicit */
|
||||
)>
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
constexpr expected(expected<U, G>&& rhs) {
|
||||
if (rhs.has_value()) var_ = std::move(rhs.value());
|
||||
else var_ = unexpected(std::move(rhs.error()));
|
||||
}
|
||||
|
||||
template<class U, class G _ENABLE_IF(
|
||||
std::is_constructible_v<T, const U&> &&
|
||||
std::is_constructible_v<E, const G&> &&
|
||||
!std::is_constructible_v<T, expected<U, G>&> &&
|
||||
!std::is_constructible_v<T, expected<U, G>&&> &&
|
||||
!std::is_constructible_v<T, const expected<U, G>&> &&
|
||||
!std::is_constructible_v<T, const expected<U, G>&&> &&
|
||||
!std::is_convertible_v<expected<U, G>&, T> &&
|
||||
!std::is_convertible_v<expected<U, G>&&, T> &&
|
||||
!std::is_convertible_v<const expected<U, G>&, T> &&
|
||||
!std::is_convertible_v<const expected<U, G>&&, T> &&
|
||||
(!std::is_convertible_v<const U&, T> ||
|
||||
!std::is_convertible_v<const G&, E>) /* explicit */
|
||||
)>
|
||||
constexpr explicit expected(expected<U, G>&& rhs) {
|
||||
if (rhs.has_value()) var_ = std::move(rhs.value());
|
||||
else var_ = unexpected(std::move(rhs.error()));
|
||||
}
|
||||
|
||||
template <class U = T _ENABLE_IF(
|
||||
std::is_constructible_v<T, U&&> &&
|
||||
!std::is_same_v<std::remove_cv_t<std::remove_reference_t<U>>, std::in_place_t> &&
|
||||
!std::is_same_v<expected<T, E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
|
||||
!std::is_same_v<unexpected<E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
|
||||
std::is_convertible_v<U&&, T> /* non-explicit */
|
||||
)>
|
||||
// NOLINTNEXTLINE(google-explicit-constructor,bugprone-forwarding-reference-overload)
|
||||
constexpr expected(U&& v) : var_(std::in_place_index<0>, std::forward<U>(v)) {}
|
||||
|
||||
template <class U = T _ENABLE_IF(
|
||||
std::is_constructible_v<T, U&&> &&
|
||||
!std::is_same_v<std::remove_cv_t<std::remove_reference_t<U>>, std::in_place_t> &&
|
||||
!std::is_same_v<expected<T, E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
|
||||
!std::is_same_v<unexpected<E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
|
||||
!std::is_convertible_v<U&&, T> /* explicit */
|
||||
)>
|
||||
// NOLINTNEXTLINE(bugprone-forwarding-reference-overload)
|
||||
constexpr explicit expected(U&& v) : var_(std::in_place_index<0>, T(std::forward<U>(v))) {}
|
||||
|
||||
template<class G = E _ENABLE_IF(
|
||||
std::is_constructible_v<E, const G&> &&
|
||||
std::is_convertible_v<const G&, E> /* non-explicit */
|
||||
)>
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
constexpr expected(const unexpected<G>& e)
|
||||
: var_(std::in_place_index<1>, e.value()) {}
|
||||
|
||||
template<class G = E _ENABLE_IF(
|
||||
std::is_constructible_v<E, const G&> &&
|
||||
!std::is_convertible_v<const G&, E> /* explicit */
|
||||
)>
|
||||
constexpr explicit expected(const unexpected<G>& e)
|
||||
: var_(std::in_place_index<1>, E(e.value())) {}
|
||||
|
||||
template<class G = E _ENABLE_IF(
|
||||
std::is_constructible_v<E, G&&> &&
|
||||
std::is_convertible_v<G&&, E> /* non-explicit */
|
||||
)>
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
constexpr expected(unexpected<G>&& e)
|
||||
: var_(std::in_place_index<1>, std::move(e.value())) {}
|
||||
|
||||
template<class G = E _ENABLE_IF(
|
||||
std::is_constructible_v<E, G&&> &&
|
||||
!std::is_convertible_v<G&&, E> /* explicit */
|
||||
)>
|
||||
constexpr explicit expected(unexpected<G>&& e)
|
||||
: var_(std::in_place_index<1>, E(std::move(e.value()))) {}
|
||||
|
||||
template<class... Args _ENABLE_IF(
|
||||
std::is_constructible_v<T, Args&&...>
|
||||
)>
|
||||
constexpr explicit expected(std::in_place_t, Args&&... args)
|
||||
: var_(std::in_place_index<0>, std::forward<Args>(args)...) {}
|
||||
|
||||
template<class U, class... Args _ENABLE_IF(
|
||||
std::is_constructible_v<T, std::initializer_list<U>&, Args...>
|
||||
)>
|
||||
constexpr explicit expected(std::in_place_t, std::initializer_list<U> il, Args&&... args)
|
||||
: var_(std::in_place_index<0>, il, std::forward<Args>(args)...) {}
|
||||
|
||||
template<class... Args _ENABLE_IF(
|
||||
std::is_constructible_v<E, Args...>
|
||||
)>
|
||||
constexpr explicit expected(unexpect_t, Args&&... args)
|
||||
: var_(unexpected_type(std::forward<Args>(args)...)) {}
|
||||
|
||||
template<class U, class... Args _ENABLE_IF(
|
||||
std::is_constructible_v<E, std::initializer_list<U>&, Args...>
|
||||
)>
|
||||
constexpr explicit expected(unexpect_t, std::initializer_list<U> il, Args&&... args)
|
||||
: var_(unexpected_type(il, std::forward<Args>(args)...)) {}
|
||||
|
||||
// destructor
|
||||
~expected() = default;
|
||||
|
||||
// assignment
|
||||
// Note: SFNAIE doesn't work here because assignment operator should be
|
||||
// non-template. We could workaround this by defining a templated parent class
|
||||
// having the assignment operator. This incomplete implementation however
|
||||
// doesn't allow us to copy assign expected<T,E> even when T is non-copy
|
||||
// assignable. The copy assignment will fail by the underlying std::variant
|
||||
// anyway though the error message won't be clear.
|
||||
expected& operator=(const expected& rhs) = default;
|
||||
|
||||
// Note for SFNAIE above applies to here as well
|
||||
expected& operator=(expected&& rhs) noexcept(
|
||||
std::is_nothrow_move_assignable_v<T>&& std::is_nothrow_move_assignable_v<E>) = default;
|
||||
|
||||
template <class U = T _ENABLE_IF(
|
||||
!std::is_void_v<T> &&
|
||||
!std::is_same_v<expected<T, E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
|
||||
!std::conjunction_v<std::is_scalar<T>, std::is_same<T, std::decay_t<U>>> &&
|
||||
std::is_constructible_v<T, U> && std::is_assignable_v<T&, U> &&
|
||||
std::is_nothrow_move_constructible_v<E>)>
|
||||
expected& operator=(U&& rhs) {
|
||||
var_ = T(std::forward<U>(rhs));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class G = E>
|
||||
expected& operator=(const unexpected<G>& rhs) {
|
||||
var_ = rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class G = E _ENABLE_IF(
|
||||
std::is_nothrow_move_constructible_v<G> &&
|
||||
std::is_move_assignable_v<G>
|
||||
)>
|
||||
expected& operator=(unexpected<G>&& rhs) {
|
||||
var_ = std::move(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// modifiers
|
||||
template<class... Args _ENABLE_IF(
|
||||
std::is_nothrow_constructible_v<T, Args...>
|
||||
)>
|
||||
T& emplace(Args&&... args) {
|
||||
expected(std::in_place, std::forward<Args>(args)...).swap(*this);
|
||||
return value();
|
||||
}
|
||||
|
||||
template<class U, class... Args _ENABLE_IF(
|
||||
std::is_nothrow_constructible_v<T, std::initializer_list<U>&, Args...>
|
||||
)>
|
||||
T& emplace(std::initializer_list<U> il, Args&&... args) {
|
||||
expected(std::in_place, il, std::forward<Args>(args)...).swap(*this);
|
||||
return value();
|
||||
}
|
||||
|
||||
// swap
|
||||
template<typename U = T, typename = std::enable_if_t<(
|
||||
std::is_swappable_v<U> &&
|
||||
std::is_swappable_v<E> &&
|
||||
(std::is_move_constructible_v<U> ||
|
||||
std::is_move_constructible_v<E>))>>
|
||||
void swap(expected& rhs) noexcept(
|
||||
std::is_nothrow_move_constructible_v<T> &&
|
||||
std::is_nothrow_swappable_v<T> &&
|
||||
std::is_nothrow_move_constructible_v<E> &&
|
||||
std::is_nothrow_swappable_v<E>) {
|
||||
var_.swap(rhs.var_);
|
||||
}
|
||||
|
||||
// observers
|
||||
constexpr const T* operator->() const { return std::addressof(value()); }
|
||||
constexpr T* operator->() { return std::addressof(value()); }
|
||||
constexpr const T& operator*() const& { return value(); }
|
||||
constexpr T& operator*() & { return value(); }
|
||||
constexpr const T&& operator*() const&& { return std::move(std::get<T>(var_)); }
|
||||
constexpr T&& operator*() && { return std::move(std::get<T>(var_)); }
|
||||
|
||||
constexpr explicit operator bool() const noexcept { return has_value(); }
|
||||
constexpr bool has_value() const noexcept { return var_.index() == 0; }
|
||||
constexpr bool ok() const noexcept { return has_value(); }
|
||||
|
||||
constexpr const T& value() const& { return std::get<T>(var_); }
|
||||
constexpr T& value() & { return std::get<T>(var_); }
|
||||
constexpr const T&& value() const&& { return std::move(std::get<T>(var_)); }
|
||||
constexpr T&& value() && { return std::move(std::get<T>(var_)); }
|
||||
|
||||
constexpr const E& error() const& { return std::get<unexpected_type>(var_).value(); }
|
||||
constexpr E& error() & { return std::get<unexpected_type>(var_).value(); }
|
||||
constexpr const E&& error() const&& { return std::move(std::get<unexpected_type>(var_)).value(); }
|
||||
constexpr E&& error() && { return std::move(std::get<unexpected_type>(var_)).value(); }
|
||||
|
||||
template<class U _ENABLE_IF(
|
||||
std::is_copy_constructible_v<T> &&
|
||||
std::is_convertible_v<U, T>
|
||||
)>
|
||||
constexpr T value_or(U&& v) const& {
|
||||
if (has_value()) return value();
|
||||
else return static_cast<T>(std::forward<U>(v));
|
||||
}
|
||||
|
||||
template<class U _ENABLE_IF(
|
||||
std::is_move_constructible_v<T> &&
|
||||
std::is_convertible_v<U, T>
|
||||
)>
|
||||
constexpr T value_or(U&& v) && {
|
||||
if (has_value()) return std::move(value());
|
||||
else return static_cast<T>(std::forward<U>(v));
|
||||
}
|
||||
|
||||
// expected equality operators
|
||||
template<class T1, class E1, class T2, class E2>
|
||||
friend constexpr bool operator==(const expected<T1, E1>& x, const expected<T2, E2>& y);
|
||||
template<class T1, class E1, class T2, class E2>
|
||||
friend constexpr bool operator!=(const expected<T1, E1>& x, const expected<T2, E2>& y);
|
||||
|
||||
// Comparison with unexpected<E>
|
||||
template<class T1, class E1, class E2>
|
||||
friend constexpr bool operator==(const expected<T1, E1>&, const unexpected<E2>&);
|
||||
template<class T1, class E1, class E2>
|
||||
friend constexpr bool operator==(const unexpected<E2>&, const expected<T1, E1>&);
|
||||
template<class T1, class E1, class E2>
|
||||
friend constexpr bool operator!=(const expected<T1, E1>&, const unexpected<E2>&);
|
||||
template<class T1, class E1, class E2>
|
||||
friend constexpr bool operator!=(const unexpected<E2>&, const expected<T1, E1>&);
|
||||
|
||||
// Specialized algorithms
|
||||
template<class T1, class E1>
|
||||
friend void swap(expected<T1, E1>&, expected<T1, E1>&) noexcept;
|
||||
|
||||
private:
|
||||
std::variant<value_type, unexpected_type> var_;
|
||||
};
|
||||
|
||||
template<class T1, class E1, class T2, class E2>
|
||||
constexpr bool operator==(const expected<T1, E1>& x, const expected<T2, E2>& y) {
|
||||
if (x.has_value() != y.has_value()) return false;
|
||||
if (!x.has_value()) return x.error() == y.error();
|
||||
return *x == *y;
|
||||
}
|
||||
|
||||
template<class T1, class E1, class T2, class E2>
|
||||
constexpr bool operator!=(const expected<T1, E1>& x, const expected<T2, E2>& y) {
|
||||
return !(x == y);
|
||||
}
|
||||
|
||||
// Comparison with unexpected<E>
|
||||
template<class T1, class E1, class E2>
|
||||
constexpr bool operator==(const expected<T1, E1>& x, const unexpected<E2>& y) {
|
||||
return !x.has_value() && (x.error() == y.value());
|
||||
}
|
||||
template<class T1, class E1, class E2>
|
||||
constexpr bool operator==(const unexpected<E2>& x, const expected<T1, E1>& y) {
|
||||
return !y.has_value() && (x.value() == y.error());
|
||||
}
|
||||
template<class T1, class E1, class E2>
|
||||
constexpr bool operator!=(const expected<T1, E1>& x, const unexpected<E2>& y) {
|
||||
return x.has_value() || (x.error() != y.value());
|
||||
}
|
||||
template<class T1, class E1, class E2>
|
||||
constexpr bool operator!=(const unexpected<E2>& x, const expected<T1, E1>& y) {
|
||||
return y.has_value() || (x.value() != y.error());
|
||||
}
|
||||
|
||||
template<class E>
|
||||
class _NODISCARD_ expected<void, E> {
|
||||
public:
|
||||
using value_type = void;
|
||||
using error_type = E;
|
||||
using unexpected_type = unexpected<E>;
|
||||
|
||||
// constructors
|
||||
constexpr expected() = default;
|
||||
constexpr expected(const expected& rhs) = default;
|
||||
constexpr expected(expected&& rhs) noexcept = default;
|
||||
|
||||
template<class U, class G _ENABLE_IF(
|
||||
std::is_void_v<U> &&
|
||||
std::is_convertible_v<const G&, E> /* non-explicit */
|
||||
)>
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
constexpr expected(const expected<U, G>& rhs) {
|
||||
if (!rhs.has_value()) var_ = unexpected(rhs.error());
|
||||
}
|
||||
|
||||
template<class U, class G _ENABLE_IF(
|
||||
std::is_void_v<U> &&
|
||||
!std::is_convertible_v<const G&, E> /* explicit */
|
||||
)>
|
||||
constexpr explicit expected(const expected<U, G>& rhs) {
|
||||
if (!rhs.has_value()) var_ = unexpected(rhs.error());
|
||||
}
|
||||
|
||||
template<class U, class G _ENABLE_IF(
|
||||
std::is_void_v<U> &&
|
||||
std::is_convertible_v<const G&&, E> /* non-explicit */
|
||||
)>
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
constexpr expected(expected<U, G>&& rhs) {
|
||||
if (!rhs.has_value()) var_ = unexpected(std::move(rhs.error()));
|
||||
}
|
||||
|
||||
template<class U, class G _ENABLE_IF(
|
||||
std::is_void_v<U> &&
|
||||
!std::is_convertible_v<const G&&, E> /* explicit */
|
||||
)>
|
||||
constexpr explicit expected(expected<U, G>&& rhs) {
|
||||
if (!rhs.has_value()) var_ = unexpected(std::move(rhs.error()));
|
||||
}
|
||||
|
||||
template<class G = E _ENABLE_IF(
|
||||
std::is_constructible_v<E, const G&> &&
|
||||
std::is_convertible_v<const G&, E> /* non-explicit */
|
||||
)>
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
constexpr expected(const unexpected<G>& e)
|
||||
: var_(std::in_place_index<1>, e.value()) {}
|
||||
|
||||
template<class G = E _ENABLE_IF(
|
||||
std::is_constructible_v<E, const G&> &&
|
||||
!std::is_convertible_v<const G&, E> /* explicit */
|
||||
)>
|
||||
constexpr explicit expected(const unexpected<G>& e)
|
||||
: var_(std::in_place_index<1>, E(e.value())) {}
|
||||
|
||||
template<class G = E _ENABLE_IF(
|
||||
std::is_constructible_v<E, G&&> &&
|
||||
std::is_convertible_v<G&&, E> /* non-explicit */
|
||||
)>
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
constexpr expected(unexpected<G>&& e)
|
||||
: var_(std::in_place_index<1>, std::move(e.value())) {}
|
||||
|
||||
template<class G = E _ENABLE_IF(
|
||||
std::is_constructible_v<E, G&&> &&
|
||||
!std::is_convertible_v<G&&, E> /* explicit */
|
||||
)>
|
||||
constexpr explicit expected(unexpected<G>&& e)
|
||||
: var_(std::in_place_index<1>, E(std::move(e.value()))) {}
|
||||
|
||||
template<class... Args _ENABLE_IF(
|
||||
sizeof...(Args) == 0
|
||||
)>
|
||||
constexpr explicit expected(std::in_place_t, Args&&...) {}
|
||||
|
||||
template<class... Args _ENABLE_IF(
|
||||
std::is_constructible_v<E, Args...>
|
||||
)>
|
||||
constexpr explicit expected(unexpect_t, Args&&... args)
|
||||
: var_(unexpected_type(std::forward<Args>(args)...)) {}
|
||||
|
||||
template<class U, class... Args _ENABLE_IF(
|
||||
std::is_constructible_v<E, std::initializer_list<U>&, Args...>
|
||||
)>
|
||||
constexpr explicit expected(unexpect_t, std::initializer_list<U> il, Args&&... args)
|
||||
: var_(unexpected_type(il, std::forward<Args>(args)...)) {}
|
||||
|
||||
// destructor
|
||||
~expected() = default;
|
||||
|
||||
// assignment
|
||||
// Note: SFNAIE doesn't work here because assignment operator should be
|
||||
// non-template. We could workaround this by defining a templated parent class
|
||||
// having the assignment operator. This incomplete implementation however
|
||||
// doesn't allow us to copy assign expected<T,E> even when T is non-copy
|
||||
// assignable. The copy assignment will fail by the underlying std::variant
|
||||
// anyway though the error message won't be clear.
|
||||
expected& operator=(const expected& rhs) = default;
|
||||
|
||||
// Note for SFNAIE above applies to here as well
|
||||
expected& operator=(expected&& rhs) noexcept(std::is_nothrow_move_assignable_v<E>) = default;
|
||||
|
||||
template<class G = E>
|
||||
expected& operator=(const unexpected<G>& rhs) {
|
||||
var_ = rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class G = E _ENABLE_IF(
|
||||
std::is_nothrow_move_constructible_v<G> &&
|
||||
std::is_move_assignable_v<G>
|
||||
)>
|
||||
expected& operator=(unexpected<G>&& rhs) {
|
||||
var_ = std::move(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// modifiers
|
||||
void emplace() {
|
||||
var_ = std::monostate();
|
||||
}
|
||||
|
||||
// swap
|
||||
template<typename = std::enable_if_t<
|
||||
std::is_swappable_v<E>>
|
||||
>
|
||||
void swap(expected& rhs) noexcept(std::is_nothrow_move_constructible_v<E>) {
|
||||
var_.swap(rhs.var_);
|
||||
}
|
||||
|
||||
// observers
|
||||
constexpr explicit operator bool() const noexcept { return has_value(); }
|
||||
constexpr bool has_value() const noexcept { return var_.index() == 0; }
|
||||
constexpr bool ok() const noexcept { return has_value(); }
|
||||
|
||||
constexpr void value() const& { if (!has_value()) std::get<0>(var_); }
|
||||
|
||||
constexpr const E& error() const& { return std::get<unexpected_type>(var_).value(); }
|
||||
constexpr E& error() & { return std::get<unexpected_type>(var_).value(); }
|
||||
constexpr const E&& error() const&& { return std::move(std::get<unexpected_type>(var_)).value(); }
|
||||
constexpr E&& error() && { return std::move(std::get<unexpected_type>(var_)).value(); }
|
||||
|
||||
// expected equality operators
|
||||
template<class E1, class E2>
|
||||
friend constexpr bool operator==(const expected<void, E1>& x, const expected<void, E2>& y);
|
||||
|
||||
// Specialized algorithms
|
||||
template<class T1, class E1>
|
||||
friend void swap(expected<T1, E1>&, expected<T1, E1>&) noexcept;
|
||||
|
||||
private:
|
||||
std::variant<std::monostate, unexpected_type> var_;
|
||||
};
|
||||
|
||||
template<class E1, class E2>
|
||||
constexpr bool operator==(const expected<void, E1>& x, const expected<void, E2>& y) {
|
||||
if (x.has_value() != y.has_value()) return false;
|
||||
if (!x.has_value()) return x.error() == y.error();
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class T1, class E1, class E2>
|
||||
constexpr bool operator==(const expected<T1, E1>& x, const expected<void, E2>& y) {
|
||||
if (x.has_value() != y.has_value()) return false;
|
||||
if (!x.has_value()) return x.error() == y.error();
|
||||
return false;
|
||||
}
|
||||
|
||||
template<class E1, class T2, class E2>
|
||||
constexpr bool operator==(const expected<void, E1>& x, const expected<T2, E2>& y) {
|
||||
if (x.has_value() != y.has_value()) return false;
|
||||
if (!x.has_value()) return x.error() == y.error();
|
||||
return false;
|
||||
}
|
||||
|
||||
template<class E>
|
||||
class unexpected {
|
||||
public:
|
||||
// constructors
|
||||
constexpr unexpected(const unexpected&) = default;
|
||||
constexpr unexpected(unexpected&&) noexcept(std::is_nothrow_move_constructible_v<E>) = default;
|
||||
|
||||
template <class Err = E _ENABLE_IF(
|
||||
std::is_constructible_v<E, Err> &&
|
||||
!std::is_same_v<std::remove_cv_t<std::remove_reference_t<E>>, std::in_place_t> &&
|
||||
!std::is_same_v<std::remove_cv_t<std::remove_reference_t<E>>, unexpected>)>
|
||||
// NOLINTNEXTLINE(google-explicit-constructor,bugprone-forwarding-reference-overload)
|
||||
constexpr unexpected(Err&& e) : val_(std::forward<Err>(e)) {}
|
||||
|
||||
template<class U, class... Args _ENABLE_IF(
|
||||
std::is_constructible_v<E, std::initializer_list<U>&, Args...>
|
||||
)>
|
||||
constexpr explicit unexpected(std::in_place_t, std::initializer_list<U> il, Args&&... args)
|
||||
: val_(il, std::forward<Args>(args)...) {}
|
||||
|
||||
template<class Err _ENABLE_IF(
|
||||
std::is_constructible_v<E, Err> &&
|
||||
!std::is_constructible_v<E, unexpected<Err>&> &&
|
||||
!std::is_constructible_v<E, unexpected<Err>> &&
|
||||
!std::is_constructible_v<E, const unexpected<Err>&> &&
|
||||
!std::is_constructible_v<E, const unexpected<Err>> &&
|
||||
!std::is_convertible_v<unexpected<Err>&, E> &&
|
||||
!std::is_convertible_v<unexpected<Err>, E> &&
|
||||
!std::is_convertible_v<const unexpected<Err>&, E> &&
|
||||
!std::is_convertible_v<const unexpected<Err>, E> &&
|
||||
std::is_convertible_v<Err, E> /* non-explicit */
|
||||
)>
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
constexpr unexpected(const unexpected<Err>& rhs)
|
||||
: val_(rhs.value()) {}
|
||||
|
||||
template<class Err _ENABLE_IF(
|
||||
std::is_constructible_v<E, Err> &&
|
||||
!std::is_constructible_v<E, unexpected<Err>&> &&
|
||||
!std::is_constructible_v<E, unexpected<Err>> &&
|
||||
!std::is_constructible_v<E, const unexpected<Err>&> &&
|
||||
!std::is_constructible_v<E, const unexpected<Err>> &&
|
||||
!std::is_convertible_v<unexpected<Err>&, E> &&
|
||||
!std::is_convertible_v<unexpected<Err>, E> &&
|
||||
!std::is_convertible_v<const unexpected<Err>&, E> &&
|
||||
!std::is_convertible_v<const unexpected<Err>, E> &&
|
||||
!std::is_convertible_v<Err, E> /* explicit */
|
||||
)>
|
||||
constexpr explicit unexpected(const unexpected<Err>& rhs)
|
||||
: val_(E(rhs.value())) {}
|
||||
|
||||
template<class Err _ENABLE_IF(
|
||||
std::is_constructible_v<E, Err> &&
|
||||
!std::is_constructible_v<E, unexpected<Err>&> &&
|
||||
!std::is_constructible_v<E, unexpected<Err>> &&
|
||||
!std::is_constructible_v<E, const unexpected<Err>&> &&
|
||||
!std::is_constructible_v<E, const unexpected<Err>> &&
|
||||
!std::is_convertible_v<unexpected<Err>&, E> &&
|
||||
!std::is_convertible_v<unexpected<Err>, E> &&
|
||||
!std::is_convertible_v<const unexpected<Err>&, E> &&
|
||||
!std::is_convertible_v<const unexpected<Err>, E> &&
|
||||
std::is_convertible_v<Err, E> /* non-explicit */
|
||||
)>
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
constexpr unexpected(unexpected<Err>&& rhs)
|
||||
: val_(std::move(rhs.value())) {}
|
||||
|
||||
template<class Err _ENABLE_IF(
|
||||
std::is_constructible_v<E, Err> &&
|
||||
!std::is_constructible_v<E, unexpected<Err>&> &&
|
||||
!std::is_constructible_v<E, unexpected<Err>> &&
|
||||
!std::is_constructible_v<E, const unexpected<Err>&> &&
|
||||
!std::is_constructible_v<E, const unexpected<Err>> &&
|
||||
!std::is_convertible_v<unexpected<Err>&, E> &&
|
||||
!std::is_convertible_v<unexpected<Err>, E> &&
|
||||
!std::is_convertible_v<const unexpected<Err>&, E> &&
|
||||
!std::is_convertible_v<const unexpected<Err>, E> &&
|
||||
!std::is_convertible_v<Err, E> /* explicit */
|
||||
)>
|
||||
constexpr explicit unexpected(unexpected<Err>&& rhs)
|
||||
: val_(E(std::move(rhs.value()))) {}
|
||||
|
||||
// assignment
|
||||
constexpr unexpected& operator=(const unexpected&) = default;
|
||||
constexpr unexpected& operator=(unexpected&&) noexcept(std::is_nothrow_move_assignable_v<E>) =
|
||||
default;
|
||||
template<class Err = E>
|
||||
constexpr unexpected& operator=(const unexpected<Err>& rhs) {
|
||||
val_ = rhs.value();
|
||||
return *this;
|
||||
}
|
||||
template<class Err = E>
|
||||
constexpr unexpected& operator=(unexpected<Err>&& rhs) {
|
||||
val_ = std::forward<E>(rhs.value());
|
||||
return *this;
|
||||
}
|
||||
|
||||
// observer
|
||||
constexpr const E& value() const& noexcept { return val_; }
|
||||
constexpr E& value() & noexcept { return val_; }
|
||||
constexpr const E&& value() const&& noexcept { return std::move(val_); }
|
||||
constexpr E&& value() && noexcept { return std::move(val_); }
|
||||
|
||||
void swap(unexpected& other) noexcept(std::is_nothrow_swappable_v<E>) {
|
||||
std::swap(val_, other.val_);
|
||||
}
|
||||
|
||||
template<class E1, class E2>
|
||||
friend constexpr bool
|
||||
operator==(const unexpected<E1>& e1, const unexpected<E2>& e2);
|
||||
template<class E1, class E2>
|
||||
friend constexpr bool
|
||||
operator!=(const unexpected<E1>& e1, const unexpected<E2>& e2);
|
||||
|
||||
template<class E1>
|
||||
friend void swap(unexpected<E1>& x, unexpected<E1>& y) noexcept(noexcept(x.swap(y)));
|
||||
|
||||
private:
|
||||
E val_;
|
||||
};
|
||||
|
||||
template<class E1, class E2>
|
||||
constexpr bool
|
||||
operator==(const unexpected<E1>& e1, const unexpected<E2>& e2) {
|
||||
return e1.value() == e2.value();
|
||||
}
|
||||
|
||||
template<class E1, class E2>
|
||||
constexpr bool
|
||||
operator!=(const unexpected<E1>& e1, const unexpected<E2>& e2) {
|
||||
return e1.value() != e2.value();
|
||||
}
|
||||
|
||||
template<class E1>
|
||||
void swap(unexpected<E1>& x, unexpected<E1>& y) noexcept(noexcept(x.swap(y))) {
|
||||
x.swap(y);
|
||||
}
|
||||
|
||||
// TODO: bad_expected_access class
|
||||
|
||||
#undef _ENABLE_IF
|
||||
#undef _NODISCARD_
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,125 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "android-base/macros.h"
|
||||
#include "android-base/off64_t.h"
|
||||
#include "android-base/unique_fd.h"
|
||||
|
||||
#if !defined(_WIN32) && !defined(O_BINARY)
|
||||
/** Windows needs O_BINARY, but Unix never mangles line endings. */
|
||||
#define O_BINARY 0
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) && !defined(O_CLOEXEC)
|
||||
/** Windows has O_CLOEXEC but calls it O_NOINHERIT for some reason. */
|
||||
#define O_CLOEXEC O_NOINHERIT
|
||||
#endif
|
||||
|
||||
class TemporaryFile {
|
||||
public:
|
||||
TemporaryFile();
|
||||
explicit TemporaryFile(const std::string& tmp_dir);
|
||||
~TemporaryFile();
|
||||
|
||||
// Release the ownership of fd, caller is reponsible for closing the
|
||||
// fd or stream properly.
|
||||
int release();
|
||||
// Don't remove the temporary file in the destructor.
|
||||
void DoNotRemove() { remove_file_ = false; }
|
||||
|
||||
int fd;
|
||||
char path[1024];
|
||||
|
||||
private:
|
||||
void init(const std::string& tmp_dir);
|
||||
|
||||
bool remove_file_ = true;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(TemporaryFile);
|
||||
};
|
||||
|
||||
class TemporaryDir {
|
||||
public:
|
||||
TemporaryDir();
|
||||
~TemporaryDir();
|
||||
// Don't remove the temporary dir in the destructor.
|
||||
void DoNotRemove() { remove_dir_and_contents_ = false; }
|
||||
|
||||
char path[1024];
|
||||
|
||||
private:
|
||||
bool init(const std::string& tmp_dir);
|
||||
|
||||
bool remove_dir_and_contents_ = true;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(TemporaryDir);
|
||||
};
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
bool ReadFdToString(borrowed_fd fd, std::string* content);
|
||||
bool ReadFileToString(const std::string& path, std::string* content,
|
||||
bool follow_symlinks = false);
|
||||
|
||||
bool WriteStringToFile(const std::string& content, const std::string& path,
|
||||
bool follow_symlinks = false);
|
||||
bool WriteStringToFd(const std::string& content, borrowed_fd fd);
|
||||
|
||||
#if !defined(_WIN32)
|
||||
bool WriteStringToFile(const std::string& content, const std::string& path,
|
||||
mode_t mode, uid_t owner, gid_t group,
|
||||
bool follow_symlinks = false);
|
||||
#endif
|
||||
|
||||
bool ReadFully(borrowed_fd fd, void* data, size_t byte_count);
|
||||
|
||||
// Reads `byte_count` bytes from the file descriptor at the specified offset.
|
||||
// Returns false if there was an IO error or EOF was reached before reading `byte_count` bytes.
|
||||
//
|
||||
// NOTE: On Linux/Mac, this function wraps pread, which provides atomic read support without
|
||||
// modifying the read pointer of the file descriptor. On Windows, however, the read pointer does
|
||||
// get modified. This means that ReadFullyAtOffset can be used concurrently with other calls to the
|
||||
// same function, but concurrently seeking or reading incrementally can lead to unexpected
|
||||
// behavior.
|
||||
bool ReadFullyAtOffset(borrowed_fd fd, void* data, size_t byte_count, off64_t offset);
|
||||
|
||||
bool WriteFully(borrowed_fd fd, const void* data, size_t byte_count);
|
||||
|
||||
bool RemoveFileIfExists(const std::string& path, std::string* err = nullptr);
|
||||
|
||||
#if !defined(_WIN32)
|
||||
bool Realpath(const std::string& path, std::string* result);
|
||||
bool Readlink(const std::string& path, std::string* result);
|
||||
#endif
|
||||
|
||||
std::string GetExecutablePath();
|
||||
std::string GetExecutableDirectory();
|
||||
|
||||
// Like the regular basename and dirname, but thread-safe on all
|
||||
// platforms and capable of correctly handling exotic Windows paths.
|
||||
std::string Basename(const std::string& path);
|
||||
std::string Dirname(const std::string& path);
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,28 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// We include fmtlib here as an alias, since libbase will have fmtlib statically linked already.
|
||||
// It is accessed through its normal fmt:: namespace.
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wshadow"
|
||||
#include <fmt/chrono.h>
|
||||
#pragma clang diagnostic pop
|
||||
#include <fmt/core.h>
|
||||
#include <fmt/format.h>
|
||||
#include <fmt/ostream.h>
|
||||
#include <fmt/printf.h>
|
|
@ -1,464 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//
|
||||
// Google-style C++ logging.
|
||||
//
|
||||
|
||||
// This header provides a C++ stream interface to logging.
|
||||
//
|
||||
// To log:
|
||||
//
|
||||
// LOG(INFO) << "Some text; " << some_value;
|
||||
//
|
||||
// Replace `INFO` with any severity from `enum LogSeverity`.
|
||||
//
|
||||
// To log the result of a failed function and include the string
|
||||
// representation of `errno` at the end:
|
||||
//
|
||||
// PLOG(ERROR) << "Write failed";
|
||||
//
|
||||
// The output will be something like `Write failed: I/O error`.
|
||||
// Remember this as 'P' as in perror(3).
|
||||
//
|
||||
// To output your own types, simply implement operator<< as normal.
|
||||
//
|
||||
// By default, output goes to logcat on Android and stderr on the host.
|
||||
// A process can use `SetLogger` to decide where all logging goes.
|
||||
// Implementations are provided for logcat, stderr, and dmesg.
|
||||
//
|
||||
// By default, the process' name is used as the log tag.
|
||||
// Code can choose a specific log tag by defining LOG_TAG
|
||||
// before including this header.
|
||||
|
||||
// This header also provides assertions:
|
||||
//
|
||||
// CHECK(must_be_true);
|
||||
// CHECK_EQ(a, b) << z_is_interesting_too;
|
||||
|
||||
// NOTE: For Windows, you must include logging.h after windows.h to allow the
|
||||
// following code to suppress the evil ERROR macro:
|
||||
#ifdef _WIN32
|
||||
// windows.h includes wingdi.h which defines an evil macro ERROR.
|
||||
#ifdef ERROR
|
||||
#undef ERROR
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
|
||||
#include "android-base/errno_restorer.h"
|
||||
#include "android-base/macros.h"
|
||||
|
||||
// Note: DO NOT USE DIRECTLY. Use LOG_TAG instead.
|
||||
#ifdef _LOG_TAG_INTERNAL
|
||||
#error "_LOG_TAG_INTERNAL must not be defined"
|
||||
#endif
|
||||
#ifdef LOG_TAG
|
||||
#define _LOG_TAG_INTERNAL LOG_TAG
|
||||
#else
|
||||
#define _LOG_TAG_INTERNAL nullptr
|
||||
#endif
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
enum LogSeverity {
|
||||
VERBOSE,
|
||||
DEBUG,
|
||||
INFO,
|
||||
WARNING,
|
||||
ERROR,
|
||||
FATAL_WITHOUT_ABORT, // For loggability tests, this is considered identical to FATAL.
|
||||
FATAL,
|
||||
};
|
||||
|
||||
enum LogId {
|
||||
DEFAULT,
|
||||
MAIN,
|
||||
SYSTEM,
|
||||
RADIO,
|
||||
CRASH,
|
||||
};
|
||||
|
||||
using LogFunction = std::function<void(LogId, LogSeverity, const char*, const char*,
|
||||
unsigned int, const char*)>;
|
||||
using AbortFunction = std::function<void(const char*)>;
|
||||
|
||||
// Loggers for use with InitLogging/SetLogger.
|
||||
|
||||
// Log to the kernel log (dmesg).
|
||||
void KernelLogger(LogId, LogSeverity, const char*, const char*, unsigned int, const char*);
|
||||
// Log to stderr in the full logcat format (with pid/tid/time/tag details).
|
||||
void StderrLogger(LogId, LogSeverity, const char*, const char*, unsigned int, const char*);
|
||||
// Log just the message to stdout/stderr (without pid/tid/time/tag details).
|
||||
// The choice of stdout versus stderr is based on the severity.
|
||||
// Errors are also prefixed by the program name (as with err(3)/error(3)).
|
||||
// Useful for replacing printf(3)/perror(3)/err(3)/error(3) in command-line tools.
|
||||
void StdioLogger(LogId, LogSeverity, const char*, const char*, unsigned int, const char*);
|
||||
|
||||
void DefaultAborter(const char* abort_message);
|
||||
|
||||
void SetDefaultTag(const std::string& tag);
|
||||
|
||||
// The LogdLogger sends chunks of up to ~4000 bytes at a time to logd. It does not prevent other
|
||||
// threads from writing to logd between sending each chunk, so other threads may interleave their
|
||||
// messages. If preventing interleaving is required, then a custom logger that takes a lock before
|
||||
// calling this logger should be provided.
|
||||
class LogdLogger {
|
||||
public:
|
||||
explicit LogdLogger(LogId default_log_id = android::base::MAIN);
|
||||
|
||||
void operator()(LogId, LogSeverity, const char* tag, const char* file,
|
||||
unsigned int line, const char* message);
|
||||
|
||||
private:
|
||||
LogId default_log_id_;
|
||||
};
|
||||
|
||||
// Configure logging based on ANDROID_LOG_TAGS environment variable.
|
||||
// We need to parse a string that looks like
|
||||
//
|
||||
// *:v jdwp:d dalvikvm:d dalvikvm-gc:i dalvikvmi:i
|
||||
//
|
||||
// The tag (or '*' for the global level) comes first, followed by a colon and a
|
||||
// letter indicating the minimum priority level we're expected to log. This can
|
||||
// be used to reveal or conceal logs with specific tags.
|
||||
#ifdef __ANDROID__
|
||||
#define INIT_LOGGING_DEFAULT_LOGGER LogdLogger()
|
||||
#else
|
||||
#define INIT_LOGGING_DEFAULT_LOGGER StderrLogger
|
||||
#endif
|
||||
void InitLogging(char* argv[],
|
||||
LogFunction&& logger = INIT_LOGGING_DEFAULT_LOGGER,
|
||||
AbortFunction&& aborter = DefaultAborter);
|
||||
#undef INIT_LOGGING_DEFAULT_LOGGER
|
||||
|
||||
// Replace the current logger.
|
||||
void SetLogger(LogFunction&& logger);
|
||||
|
||||
// Replace the current aborter.
|
||||
void SetAborter(AbortFunction&& aborter);
|
||||
|
||||
// A helper macro that produces an expression that accepts both a qualified name and an
|
||||
// unqualified name for a LogSeverity, and returns a LogSeverity value.
|
||||
// Note: DO NOT USE DIRECTLY. This is an implementation detail.
|
||||
#define SEVERITY_LAMBDA(severity) ([&]() { \
|
||||
using ::android::base::VERBOSE; \
|
||||
using ::android::base::DEBUG; \
|
||||
using ::android::base::INFO; \
|
||||
using ::android::base::WARNING; \
|
||||
using ::android::base::ERROR; \
|
||||
using ::android::base::FATAL_WITHOUT_ABORT; \
|
||||
using ::android::base::FATAL; \
|
||||
return (severity); }())
|
||||
|
||||
#ifdef __clang_analyzer__
|
||||
// Clang's static analyzer does not see the conditional statement inside
|
||||
// LogMessage's destructor that will abort on FATAL severity.
|
||||
#define ABORT_AFTER_LOG_FATAL for (;; abort())
|
||||
|
||||
struct LogAbortAfterFullExpr {
|
||||
~LogAbortAfterFullExpr() __attribute__((noreturn)) { abort(); }
|
||||
explicit operator bool() const { return false; }
|
||||
};
|
||||
// Provides an expression that evaluates to the truthiness of `x`, automatically
|
||||
// aborting if `c` is true.
|
||||
#define ABORT_AFTER_LOG_EXPR_IF(c, x) (((c) && ::android::base::LogAbortAfterFullExpr()) || (x))
|
||||
// Note to the static analyzer that we always execute FATAL logs in practice.
|
||||
#define MUST_LOG_MESSAGE(severity) (SEVERITY_LAMBDA(severity) == ::android::base::FATAL)
|
||||
#else
|
||||
#define ABORT_AFTER_LOG_FATAL
|
||||
#define ABORT_AFTER_LOG_EXPR_IF(c, x) (x)
|
||||
#define MUST_LOG_MESSAGE(severity) false
|
||||
#endif
|
||||
#define ABORT_AFTER_LOG_FATAL_EXPR(x) ABORT_AFTER_LOG_EXPR_IF(true, x)
|
||||
|
||||
// Defines whether the given severity will be logged or silently swallowed.
|
||||
#define WOULD_LOG(severity) \
|
||||
(UNLIKELY(::android::base::ShouldLog(SEVERITY_LAMBDA(severity), _LOG_TAG_INTERNAL)) || \
|
||||
MUST_LOG_MESSAGE(severity))
|
||||
|
||||
// Get an ostream that can be used for logging at the given severity and to the default
|
||||
// destination.
|
||||
//
|
||||
// Notes:
|
||||
// 1) This will not check whether the severity is high enough. One should use WOULD_LOG to filter
|
||||
// usage manually.
|
||||
// 2) This does not save and restore errno.
|
||||
#define LOG_STREAM(severity) \
|
||||
::android::base::LogMessage(__FILE__, __LINE__, SEVERITY_LAMBDA(severity), _LOG_TAG_INTERNAL, \
|
||||
-1) \
|
||||
.stream()
|
||||
|
||||
// Logs a message to logcat on Android otherwise to stderr. If the severity is
|
||||
// FATAL it also causes an abort. For example:
|
||||
//
|
||||
// LOG(FATAL) << "We didn't expect to reach here";
|
||||
#define LOG(severity) LOGGING_PREAMBLE(severity) && LOG_STREAM(severity)
|
||||
|
||||
// Checks if we want to log something, and sets up appropriate RAII objects if
|
||||
// so.
|
||||
// Note: DO NOT USE DIRECTLY. This is an implementation detail.
|
||||
#define LOGGING_PREAMBLE(severity) \
|
||||
(WOULD_LOG(severity) && \
|
||||
ABORT_AFTER_LOG_EXPR_IF((SEVERITY_LAMBDA(severity)) == ::android::base::FATAL, true) && \
|
||||
::android::base::ErrnoRestorer())
|
||||
|
||||
// A variant of LOG that also logs the current errno value. To be used when
|
||||
// library calls fail.
|
||||
#define PLOG(severity) \
|
||||
LOGGING_PREAMBLE(severity) && \
|
||||
::android::base::LogMessage(__FILE__, __LINE__, SEVERITY_LAMBDA(severity), \
|
||||
_LOG_TAG_INTERNAL, errno) \
|
||||
.stream()
|
||||
|
||||
// Marker that code is yet to be implemented.
|
||||
#define UNIMPLEMENTED(level) \
|
||||
LOG(level) << __PRETTY_FUNCTION__ << " unimplemented "
|
||||
|
||||
// Check whether condition x holds and LOG(FATAL) if not. The value of the
|
||||
// expression x is only evaluated once. Extra logging can be appended using <<
|
||||
// after. For example:
|
||||
//
|
||||
// CHECK(false == true) results in a log message of
|
||||
// "Check failed: false == true".
|
||||
#define CHECK(x) \
|
||||
LIKELY((x)) || ABORT_AFTER_LOG_FATAL_EXPR(false) || \
|
||||
::android::base::LogMessage(__FILE__, __LINE__, ::android::base::FATAL, _LOG_TAG_INTERNAL, \
|
||||
-1) \
|
||||
.stream() \
|
||||
<< "Check failed: " #x << " "
|
||||
|
||||
// clang-format off
|
||||
// Helper for CHECK_xx(x,y) macros.
|
||||
#define CHECK_OP(LHS, RHS, OP) \
|
||||
for (auto _values = ::android::base::MakeEagerEvaluator(LHS, RHS); \
|
||||
UNLIKELY(!(_values.lhs OP _values.rhs)); \
|
||||
/* empty */) \
|
||||
ABORT_AFTER_LOG_FATAL \
|
||||
::android::base::LogMessage(__FILE__, __LINE__, ::android::base::FATAL, _LOG_TAG_INTERNAL, -1) \
|
||||
.stream() \
|
||||
<< "Check failed: " << #LHS << " " << #OP << " " << #RHS << " (" #LHS "=" << _values.lhs \
|
||||
<< ", " #RHS "=" << _values.rhs << ") "
|
||||
// clang-format on
|
||||
|
||||
// Check whether a condition holds between x and y, LOG(FATAL) if not. The value
|
||||
// of the expressions x and y is evaluated once. Extra logging can be appended
|
||||
// using << after. For example:
|
||||
//
|
||||
// CHECK_NE(0 == 1, false) results in
|
||||
// "Check failed: false != false (0==1=false, false=false) ".
|
||||
#define CHECK_EQ(x, y) CHECK_OP(x, y, == )
|
||||
#define CHECK_NE(x, y) CHECK_OP(x, y, != )
|
||||
#define CHECK_LE(x, y) CHECK_OP(x, y, <= )
|
||||
#define CHECK_LT(x, y) CHECK_OP(x, y, < )
|
||||
#define CHECK_GE(x, y) CHECK_OP(x, y, >= )
|
||||
#define CHECK_GT(x, y) CHECK_OP(x, y, > )
|
||||
|
||||
// clang-format off
|
||||
// Helper for CHECK_STRxx(s1,s2) macros.
|
||||
#define CHECK_STROP(s1, s2, sense) \
|
||||
while (UNLIKELY((strcmp(s1, s2) == 0) != (sense))) \
|
||||
ABORT_AFTER_LOG_FATAL \
|
||||
::android::base::LogMessage(__FILE__, __LINE__, ::android::base::FATAL, \
|
||||
_LOG_TAG_INTERNAL, -1) \
|
||||
.stream() \
|
||||
<< "Check failed: " << "\"" << (s1) << "\"" \
|
||||
<< ((sense) ? " == " : " != ") << "\"" << (s2) << "\""
|
||||
// clang-format on
|
||||
|
||||
// Check for string (const char*) equality between s1 and s2, LOG(FATAL) if not.
|
||||
#define CHECK_STREQ(s1, s2) CHECK_STROP(s1, s2, true)
|
||||
#define CHECK_STRNE(s1, s2) CHECK_STROP(s1, s2, false)
|
||||
|
||||
// Perform the pthread function call(args), LOG(FATAL) on error.
|
||||
#define CHECK_PTHREAD_CALL(call, args, what) \
|
||||
do { \
|
||||
int rc = call args; \
|
||||
if (rc != 0) { \
|
||||
errno = rc; \
|
||||
ABORT_AFTER_LOG_FATAL \
|
||||
PLOG(FATAL) << #call << " failed for " << (what); \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
// CHECK that can be used in a constexpr function. For example:
|
||||
//
|
||||
// constexpr int half(int n) {
|
||||
// return
|
||||
// DCHECK_CONSTEXPR(n >= 0, , 0)
|
||||
// CHECK_CONSTEXPR((n & 1) == 0),
|
||||
// << "Extra debugging output: n = " << n, 0)
|
||||
// n / 2;
|
||||
// }
|
||||
#define CHECK_CONSTEXPR(x, out, dummy) \
|
||||
(UNLIKELY(!(x))) \
|
||||
? (LOG(FATAL) << "Check failed: " << #x out, dummy) \
|
||||
:
|
||||
|
||||
// DCHECKs are debug variants of CHECKs only enabled in debug builds. Generally
|
||||
// CHECK should be used unless profiling identifies a CHECK as being in
|
||||
// performance critical code.
|
||||
#if defined(NDEBUG) && !defined(__clang_analyzer__)
|
||||
static constexpr bool kEnableDChecks = false;
|
||||
#else
|
||||
static constexpr bool kEnableDChecks = true;
|
||||
#endif
|
||||
|
||||
#define DCHECK(x) \
|
||||
if (::android::base::kEnableDChecks) CHECK(x)
|
||||
#define DCHECK_EQ(x, y) \
|
||||
if (::android::base::kEnableDChecks) CHECK_EQ(x, y)
|
||||
#define DCHECK_NE(x, y) \
|
||||
if (::android::base::kEnableDChecks) CHECK_NE(x, y)
|
||||
#define DCHECK_LE(x, y) \
|
||||
if (::android::base::kEnableDChecks) CHECK_LE(x, y)
|
||||
#define DCHECK_LT(x, y) \
|
||||
if (::android::base::kEnableDChecks) CHECK_LT(x, y)
|
||||
#define DCHECK_GE(x, y) \
|
||||
if (::android::base::kEnableDChecks) CHECK_GE(x, y)
|
||||
#define DCHECK_GT(x, y) \
|
||||
if (::android::base::kEnableDChecks) CHECK_GT(x, y)
|
||||
#define DCHECK_STREQ(s1, s2) \
|
||||
if (::android::base::kEnableDChecks) CHECK_STREQ(s1, s2)
|
||||
#define DCHECK_STRNE(s1, s2) \
|
||||
if (::android::base::kEnableDChecks) CHECK_STRNE(s1, s2)
|
||||
#if defined(NDEBUG) && !defined(__clang_analyzer__)
|
||||
#define DCHECK_CONSTEXPR(x, out, dummy)
|
||||
#else
|
||||
#define DCHECK_CONSTEXPR(x, out, dummy) CHECK_CONSTEXPR(x, out, dummy)
|
||||
#endif
|
||||
|
||||
// Temporary class created to evaluate the LHS and RHS, used with
|
||||
// MakeEagerEvaluator to infer the types of LHS and RHS.
|
||||
template <typename LHS, typename RHS>
|
||||
struct EagerEvaluator {
|
||||
constexpr EagerEvaluator(LHS l, RHS r) : lhs(l), rhs(r) {
|
||||
}
|
||||
LHS lhs;
|
||||
RHS rhs;
|
||||
};
|
||||
|
||||
// Helper function for CHECK_xx.
|
||||
template <typename LHS, typename RHS>
|
||||
constexpr EagerEvaluator<LHS, RHS> MakeEagerEvaluator(LHS lhs, RHS rhs) {
|
||||
return EagerEvaluator<LHS, RHS>(lhs, rhs);
|
||||
}
|
||||
|
||||
// Explicitly instantiate EagerEvalue for pointers so that char*s aren't treated
|
||||
// as strings. To compare strings use CHECK_STREQ and CHECK_STRNE. We rely on
|
||||
// signed/unsigned warnings to protect you against combinations not explicitly
|
||||
// listed below.
|
||||
#define EAGER_PTR_EVALUATOR(T1, T2) \
|
||||
template <> \
|
||||
struct EagerEvaluator<T1, T2> { \
|
||||
EagerEvaluator(T1 l, T2 r) \
|
||||
: lhs(reinterpret_cast<const void*>(l)), \
|
||||
rhs(reinterpret_cast<const void*>(r)) { \
|
||||
} \
|
||||
const void* lhs; \
|
||||
const void* rhs; \
|
||||
}
|
||||
EAGER_PTR_EVALUATOR(const char*, const char*);
|
||||
EAGER_PTR_EVALUATOR(const char*, char*);
|
||||
EAGER_PTR_EVALUATOR(char*, const char*);
|
||||
EAGER_PTR_EVALUATOR(char*, char*);
|
||||
EAGER_PTR_EVALUATOR(const unsigned char*, const unsigned char*);
|
||||
EAGER_PTR_EVALUATOR(const unsigned char*, unsigned char*);
|
||||
EAGER_PTR_EVALUATOR(unsigned char*, const unsigned char*);
|
||||
EAGER_PTR_EVALUATOR(unsigned char*, unsigned char*);
|
||||
EAGER_PTR_EVALUATOR(const signed char*, const signed char*);
|
||||
EAGER_PTR_EVALUATOR(const signed char*, signed char*);
|
||||
EAGER_PTR_EVALUATOR(signed char*, const signed char*);
|
||||
EAGER_PTR_EVALUATOR(signed char*, signed char*);
|
||||
|
||||
// Data for the log message, not stored in LogMessage to avoid increasing the
|
||||
// stack size.
|
||||
class LogMessageData;
|
||||
|
||||
// A LogMessage is a temporarily scoped object used by LOG and the unlikely part
|
||||
// of a CHECK. The destructor will abort if the severity is FATAL.
|
||||
class LogMessage {
|
||||
public:
|
||||
// LogId has been deprecated, but this constructor must exist for prebuilts.
|
||||
LogMessage(const char* file, unsigned int line, LogId, LogSeverity severity, const char* tag,
|
||||
int error);
|
||||
LogMessage(const char* file, unsigned int line, LogSeverity severity, const char* tag, int error);
|
||||
|
||||
~LogMessage();
|
||||
|
||||
// Returns the stream associated with the message, the LogMessage performs
|
||||
// output when it goes out of scope.
|
||||
std::ostream& stream();
|
||||
|
||||
// The routine that performs the actual logging.
|
||||
static void LogLine(const char* file, unsigned int line, LogSeverity severity, const char* tag,
|
||||
const char* msg);
|
||||
|
||||
private:
|
||||
const std::unique_ptr<LogMessageData> data_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(LogMessage);
|
||||
};
|
||||
|
||||
// Get the minimum severity level for logging.
|
||||
LogSeverity GetMinimumLogSeverity();
|
||||
|
||||
// Set the minimum severity level for logging, returning the old severity.
|
||||
LogSeverity SetMinimumLogSeverity(LogSeverity new_severity);
|
||||
|
||||
// Return whether or not a log message with the associated tag should be logged.
|
||||
bool ShouldLog(LogSeverity severity, const char* tag);
|
||||
|
||||
// Allows to temporarily change the minimum severity level for logging.
|
||||
class ScopedLogSeverity {
|
||||
public:
|
||||
explicit ScopedLogSeverity(LogSeverity level);
|
||||
~ScopedLogSeverity();
|
||||
|
||||
private:
|
||||
LogSeverity old_;
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
||||
|
||||
namespace std { // NOLINT(cert-dcl58-cpp)
|
||||
|
||||
// Emit a warning of ostream<< with std::string*. The intention was most likely to print *string.
|
||||
//
|
||||
// Note: for this to work, we need to have this in a namespace.
|
||||
// Note: using a pragma because "-Wgcc-compat" (included in "-Weverything") complains about
|
||||
// diagnose_if.
|
||||
// Note: to print the pointer, use "<< static_cast<const void*>(string_pointer)" instead.
|
||||
// Note: a not-recommended alternative is to let Clang ignore the warning by adding
|
||||
// -Wno-user-defined-warnings to CPPFLAGS.
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wgcc-compat"
|
||||
#define OSTREAM_STRING_POINTER_USAGE_WARNING \
|
||||
__attribute__((diagnose_if(true, "Unexpected logging of string pointer", "warning")))
|
||||
inline OSTREAM_STRING_POINTER_USAGE_WARNING
|
||||
std::ostream& operator<<(std::ostream& stream, const std::string* string_pointer) {
|
||||
return stream << static_cast<const void*>(string_pointer);
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
} // namespace std
|
|
@ -1,146 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h> // for size_t
|
||||
#include <unistd.h> // for TEMP_FAILURE_RETRY
|
||||
|
||||
#include <utility>
|
||||
|
||||
// bionic and glibc both have TEMP_FAILURE_RETRY, but eg Mac OS' libc doesn't.
|
||||
#ifndef TEMP_FAILURE_RETRY
|
||||
#define TEMP_FAILURE_RETRY(exp) \
|
||||
({ \
|
||||
decltype(exp) _rc; \
|
||||
do { \
|
||||
_rc = (exp); \
|
||||
} while (_rc == -1 && errno == EINTR); \
|
||||
_rc; \
|
||||
})
|
||||
#endif
|
||||
|
||||
// A macro to disallow the copy constructor and operator= functions
|
||||
// This must be placed in the private: declarations for a class.
|
||||
//
|
||||
// For disallowing only assign or copy, delete the relevant operator or
|
||||
// constructor, for example:
|
||||
// void operator=(const TypeName&) = delete;
|
||||
// Note, that most uses of DISALLOW_ASSIGN and DISALLOW_COPY are broken
|
||||
// semantically, one should either use disallow both or neither. Try to
|
||||
// avoid these in new code.
|
||||
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
|
||||
TypeName(const TypeName&) = delete; \
|
||||
void operator=(const TypeName&) = delete
|
||||
|
||||
// A macro to disallow all the implicit constructors, namely the
|
||||
// default constructor, copy constructor and operator= functions.
|
||||
//
|
||||
// This should be used in the private: declarations for a class
|
||||
// that wants to prevent anyone from instantiating it. This is
|
||||
// especially useful for classes containing only static methods.
|
||||
#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
|
||||
TypeName() = delete; \
|
||||
DISALLOW_COPY_AND_ASSIGN(TypeName)
|
||||
|
||||
// The arraysize(arr) macro returns the # of elements in an array arr.
|
||||
// The expression is a compile-time constant, and therefore can be
|
||||
// used in defining new arrays, for example. If you use arraysize on
|
||||
// a pointer by mistake, you will get a compile-time error.
|
||||
//
|
||||
// One caveat is that arraysize() doesn't accept any array of an
|
||||
// anonymous type or a type defined inside a function. In these rare
|
||||
// cases, you have to use the unsafe ARRAYSIZE_UNSAFE() macro below. This is
|
||||
// due to a limitation in C++'s template system. The limitation might
|
||||
// eventually be removed, but it hasn't happened yet.
|
||||
|
||||
// This template function declaration is used in defining arraysize.
|
||||
// Note that the function doesn't need an implementation, as we only
|
||||
// use its type.
|
||||
template <typename T, size_t N>
|
||||
char(&ArraySizeHelper(T(&array)[N]))[N]; // NOLINT(readability/casting)
|
||||
|
||||
#define arraysize(array) (sizeof(ArraySizeHelper(array)))
|
||||
|
||||
#define SIZEOF_MEMBER(t, f) sizeof(std::declval<t>().f)
|
||||
|
||||
// Changing this definition will cause you a lot of pain. A majority of
|
||||
// vendor code defines LIKELY and UNLIKELY this way, and includes
|
||||
// this header through an indirect path.
|
||||
#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true ))
|
||||
#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false ))
|
||||
|
||||
#define WARN_UNUSED __attribute__((warn_unused_result))
|
||||
|
||||
// A deprecated function to call to create a false use of the parameter, for
|
||||
// example:
|
||||
// int foo(int x) { UNUSED(x); return 10; }
|
||||
// to avoid compiler warnings. Going forward we prefer ATTRIBUTE_UNUSED.
|
||||
template <typename... T>
|
||||
void UNUSED(const T&...) {
|
||||
}
|
||||
|
||||
// An attribute to place on a parameter to a function, for example:
|
||||
// int foo(int x ATTRIBUTE_UNUSED) { return 10; }
|
||||
// to avoid compiler warnings.
|
||||
#define ATTRIBUTE_UNUSED __attribute__((__unused__))
|
||||
|
||||
// The FALLTHROUGH_INTENDED macro can be used to annotate implicit fall-through
|
||||
// between switch labels:
|
||||
// switch (x) {
|
||||
// case 40:
|
||||
// case 41:
|
||||
// if (truth_is_out_there) {
|
||||
// ++x;
|
||||
// FALLTHROUGH_INTENDED; // Use instead of/along with annotations in
|
||||
// // comments.
|
||||
// } else {
|
||||
// return x;
|
||||
// }
|
||||
// case 42:
|
||||
// ...
|
||||
//
|
||||
// As shown in the example above, the FALLTHROUGH_INTENDED macro should be
|
||||
// followed by a semicolon. It is designed to mimic control-flow statements
|
||||
// like 'break;', so it can be placed in most places where 'break;' can, but
|
||||
// only if there are no statements on the execution path between it and the
|
||||
// next switch label.
|
||||
//
|
||||
// When compiled with clang, the FALLTHROUGH_INTENDED macro is expanded to
|
||||
// [[clang::fallthrough]] attribute, which is analysed when performing switch
|
||||
// labels fall-through diagnostic ('-Wimplicit-fallthrough'). See clang
|
||||
// documentation on language extensions for details:
|
||||
// http://clang.llvm.org/docs/LanguageExtensions.html#clang__fallthrough
|
||||
//
|
||||
// When used with unsupported compilers, the FALLTHROUGH_INTENDED macro has no
|
||||
// effect on diagnostics.
|
||||
//
|
||||
// In either case this macro has no effect on runtime behavior and performance
|
||||
// of code.
|
||||
#ifndef FALLTHROUGH_INTENDED
|
||||
#define FALLTHROUGH_INTENDED [[clang::fallthrough]] // NOLINT
|
||||
#endif
|
||||
|
||||
// Current ABI string
|
||||
#if defined(__arm__)
|
||||
#define ABI_STRING "arm"
|
||||
#elif defined(__aarch64__)
|
||||
#define ABI_STRING "arm64"
|
||||
#elif defined(__i386__)
|
||||
#define ABI_STRING "x86"
|
||||
#elif defined(__x86_64__)
|
||||
#define ABI_STRING "x86_64"
|
||||
#endif
|
|
@ -1,93 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "android-base/macros.h"
|
||||
#include "android-base/off64_t.h"
|
||||
#include "android-base/unique_fd.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#define PROT_READ 1
|
||||
#define PROT_WRITE 2
|
||||
using os_handle = HANDLE;
|
||||
#else
|
||||
#include <sys/mman.h>
|
||||
using os_handle = int;
|
||||
#endif
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
/**
|
||||
* A region of a file mapped into memory (for grepping: also known as MmapFile or file mapping).
|
||||
*/
|
||||
class MappedFile {
|
||||
public:
|
||||
/**
|
||||
* Creates a new mapping of the file pointed to by `fd`. Unlike the underlying OS primitives,
|
||||
* `offset` does not need to be page-aligned. If `PROT_WRITE` is set in `prot`, the mapping
|
||||
* will be writable, otherwise it will be read-only. Mappings are always `MAP_SHARED`.
|
||||
*/
|
||||
static std::unique_ptr<MappedFile> FromFd(borrowed_fd fd, off64_t offset, size_t length,
|
||||
int prot);
|
||||
|
||||
/**
|
||||
* Same thing, but using the raw OS file handle instead of a CRT wrapper.
|
||||
*/
|
||||
static std::unique_ptr<MappedFile> FromOsHandle(os_handle h, off64_t offset, size_t length,
|
||||
int prot);
|
||||
|
||||
/**
|
||||
* Removes the mapping.
|
||||
*/
|
||||
~MappedFile();
|
||||
|
||||
/**
|
||||
* Not copyable but movable.
|
||||
*/
|
||||
MappedFile(MappedFile&& other);
|
||||
MappedFile& operator=(MappedFile&& other);
|
||||
|
||||
char* data() const { return base_ + offset_; }
|
||||
size_t size() const { return size_; }
|
||||
|
||||
private:
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(MappedFile);
|
||||
|
||||
void Close();
|
||||
|
||||
char* base_;
|
||||
size_t size_;
|
||||
|
||||
size_t offset_;
|
||||
|
||||
#if defined(_WIN32)
|
||||
MappedFile(char* base, size_t size, size_t offset, HANDLE handle)
|
||||
: base_(base), size_(size), offset_(offset), handle_(handle) {}
|
||||
HANDLE handle_;
|
||||
#else
|
||||
MappedFile(char* base, size_t size, size_t offset) : base_(base), size_(size), offset_(offset) {}
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,38 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
// Use memcpy for access to unaligned data on targets with alignment
|
||||
// restrictions. The compiler will generate appropriate code to access these
|
||||
// structures without generating alignment exceptions.
|
||||
template <typename T>
|
||||
static inline T get_unaligned(const void* address) {
|
||||
T result;
|
||||
memcpy(&result, address, sizeof(T));
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline void put_unaligned(void* address, T v) {
|
||||
memcpy(address, &v, sizeof(T));
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,94 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "android-base/macros.h"
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
// A wrapper that makes it easy to create an object of type T with static
|
||||
// storage duration that:
|
||||
// - is only constructed on first access
|
||||
// - never invokes the destructor
|
||||
// in order to satisfy the styleguide ban on global constructors and
|
||||
// destructors.
|
||||
//
|
||||
// Runtime constant example:
|
||||
// const std::string& GetLineSeparator() {
|
||||
// // Forwards to std::string(size_t, char, const Allocator&) constructor.
|
||||
// static const base::NoDestructor<std::string> s(5, '-');
|
||||
// return *s;
|
||||
// }
|
||||
//
|
||||
// More complex initialization with a lambda:
|
||||
// const std::string& GetSessionNonce() {
|
||||
// static const base::NoDestructor<std::string> nonce([] {
|
||||
// std::string s(16);
|
||||
// crypto::RandString(s.data(), s.size());
|
||||
// return s;
|
||||
// }());
|
||||
// return *nonce;
|
||||
// }
|
||||
//
|
||||
// NoDestructor<T> stores the object inline, so it also avoids a pointer
|
||||
// indirection and a malloc. Also note that since C++11 static local variable
|
||||
// initialization is thread-safe and so is this pattern. Code should prefer to
|
||||
// use NoDestructor<T> over:
|
||||
// - A function scoped static T* or T& that is dynamically initialized.
|
||||
// - A global base::LazyInstance<T>.
|
||||
//
|
||||
// Note that since the destructor is never run, this *will* leak memory if used
|
||||
// as a stack or member variable. Furthermore, a NoDestructor<T> should never
|
||||
// have global scope as that may require a static initializer.
|
||||
template <typename T>
|
||||
class NoDestructor {
|
||||
public:
|
||||
// Not constexpr; just write static constexpr T x = ...; if the value should
|
||||
// be a constexpr.
|
||||
template <typename... Args>
|
||||
explicit NoDestructor(Args&&... args) {
|
||||
new (storage_) T(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// Allows copy and move construction of the contained type, to allow
|
||||
// construction from an initializer list, e.g. for std::vector.
|
||||
explicit NoDestructor(const T& x) { new (storage_) T(x); }
|
||||
explicit NoDestructor(T&& x) { new (storage_) T(std::move(x)); }
|
||||
|
||||
NoDestructor(const NoDestructor&) = delete;
|
||||
NoDestructor& operator=(const NoDestructor&) = delete;
|
||||
|
||||
~NoDestructor() = default;
|
||||
|
||||
const T& operator*() const { return *get(); }
|
||||
T& operator*() { return *get(); }
|
||||
|
||||
const T* operator->() const { return get(); }
|
||||
T* operator->() { return get(); }
|
||||
|
||||
const T* get() const { return reinterpret_cast<const T*>(storage_); }
|
||||
T* get() { return reinterpret_cast<T*>(storage_); }
|
||||
|
||||
private:
|
||||
alignas(T) char storage_[sizeof(T)];
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,22 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(__APPLE__)
|
||||
/** Mac OS has always had a 64-bit off_t, so it doesn't have off64_t. */
|
||||
typedef off_t off64_t;
|
||||
#endif
|
|
@ -1,58 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
// Parse the given string as yes or no inactivation of some sort. Return one of the
|
||||
// ParseBoolResult enumeration values.
|
||||
//
|
||||
// The following values parse as true:
|
||||
//
|
||||
// 1
|
||||
// on
|
||||
// true
|
||||
// y
|
||||
// yes
|
||||
//
|
||||
//
|
||||
// The following values parse as false:
|
||||
//
|
||||
// 0
|
||||
// false
|
||||
// n
|
||||
// no
|
||||
// off
|
||||
//
|
||||
// Anything else is a parse error.
|
||||
//
|
||||
// The purpose of this function is to have a single canonical parser for yes-or-no indications
|
||||
// throughout the system.
|
||||
|
||||
enum class ParseBoolResult {
|
||||
kError,
|
||||
kFalse,
|
||||
kTrue,
|
||||
};
|
||||
|
||||
ParseBoolResult ParseBool(std::string_view s);
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,77 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
// Parse floating value in the string 's' and sets 'out' to that value if it exists.
|
||||
// Optionally allows the caller to define a 'min' and 'max' beyond which
|
||||
// otherwise valid values will be rejected. Returns boolean success.
|
||||
template <typename T, T (*strtox)(const char* str, char** endptr)>
|
||||
static inline bool ParseFloatingPoint(const char* s, T* out, T min, T max) {
|
||||
errno = 0;
|
||||
char* end;
|
||||
T result = strtox(s, &end);
|
||||
if (errno != 0 || s == end || *end != '\0') {
|
||||
return false;
|
||||
}
|
||||
if (result < min || max < result) {
|
||||
return false;
|
||||
}
|
||||
if (out != nullptr) {
|
||||
*out = result;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Parse double value in the string 's' and sets 'out' to that value if it exists.
|
||||
// Optionally allows the caller to define a 'min' and 'max' beyond which
|
||||
// otherwise valid values will be rejected. Returns boolean success.
|
||||
static inline bool ParseDouble(const char* s, double* out,
|
||||
double min = std::numeric_limits<double>::lowest(),
|
||||
double max = std::numeric_limits<double>::max()) {
|
||||
return ParseFloatingPoint<double, strtod>(s, out, min, max);
|
||||
}
|
||||
static inline bool ParseDouble(const std::string& s, double* out,
|
||||
double min = std::numeric_limits<double>::lowest(),
|
||||
double max = std::numeric_limits<double>::max()) {
|
||||
return ParseFloatingPoint<double, strtod>(s.c_str(), out, min, max);
|
||||
}
|
||||
|
||||
// Parse float value in the string 's' and sets 'out' to that value if it exists.
|
||||
// Optionally allows the caller to define a 'min' and 'max' beyond which
|
||||
// otherwise valid values will be rejected. Returns boolean success.
|
||||
static inline bool ParseFloat(const char* s, float* out,
|
||||
float min = std::numeric_limits<float>::lowest(),
|
||||
float max = std::numeric_limits<float>::max()) {
|
||||
return ParseFloatingPoint<float, strtof>(s, out, min, max);
|
||||
}
|
||||
static inline bool ParseFloat(const std::string& s, float* out,
|
||||
float min = std::numeric_limits<float>::lowest(),
|
||||
float max = std::numeric_limits<float>::max()) {
|
||||
return ParseFloatingPoint<float, strtof>(s.c_str(), out, min, max);
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,136 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
// Parses the unsigned decimal or hexadecimal integer in the string 's' and sets
|
||||
// 'out' to that value if it is specified. Optionally allows the caller to define
|
||||
// a 'max' beyond which otherwise valid values will be rejected. Returns boolean
|
||||
// success; 'out' is untouched if parsing fails.
|
||||
template <typename T>
|
||||
bool ParseUint(const char* s, T* out, T max = std::numeric_limits<T>::max(),
|
||||
bool allow_suffixes = false) {
|
||||
static_assert(std::is_unsigned<T>::value, "ParseUint can only be used with unsigned types");
|
||||
while (isspace(*s)) {
|
||||
s++;
|
||||
}
|
||||
|
||||
if (s[0] == '-') {
|
||||
errno = EINVAL;
|
||||
return false;
|
||||
}
|
||||
|
||||
int base = (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) ? 16 : 10;
|
||||
errno = 0;
|
||||
char* end;
|
||||
unsigned long long int result = strtoull(s, &end, base);
|
||||
if (errno != 0) return false;
|
||||
if (end == s) {
|
||||
errno = EINVAL;
|
||||
return false;
|
||||
}
|
||||
if (*end != '\0') {
|
||||
const char* suffixes = "bkmgtpe";
|
||||
const char* suffix;
|
||||
if ((!allow_suffixes || (suffix = strchr(suffixes, tolower(*end))) == nullptr) ||
|
||||
__builtin_mul_overflow(result, 1ULL << (10 * (suffix - suffixes)), &result)) {
|
||||
errno = EINVAL;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (max < result) {
|
||||
errno = ERANGE;
|
||||
return false;
|
||||
}
|
||||
if (out != nullptr) {
|
||||
*out = static_cast<T>(result);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: string_view
|
||||
template <typename T>
|
||||
bool ParseUint(const std::string& s, T* out, T max = std::numeric_limits<T>::max(),
|
||||
bool allow_suffixes = false) {
|
||||
return ParseUint(s.c_str(), out, max, allow_suffixes);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool ParseByteCount(const char* s, T* out, T max = std::numeric_limits<T>::max()) {
|
||||
return ParseUint(s, out, max, true);
|
||||
}
|
||||
|
||||
// TODO: string_view
|
||||
template <typename T>
|
||||
bool ParseByteCount(const std::string& s, T* out, T max = std::numeric_limits<T>::max()) {
|
||||
return ParseByteCount(s.c_str(), out, max);
|
||||
}
|
||||
|
||||
// Parses the signed decimal or hexadecimal integer in the string 's' and sets
|
||||
// 'out' to that value if it is specified. Optionally allows the caller to define
|
||||
// a 'min' and 'max' beyond which otherwise valid values will be rejected. Returns
|
||||
// boolean success; 'out' is untouched if parsing fails.
|
||||
template <typename T>
|
||||
bool ParseInt(const char* s, T* out,
|
||||
T min = std::numeric_limits<T>::min(),
|
||||
T max = std::numeric_limits<T>::max()) {
|
||||
static_assert(std::is_signed<T>::value, "ParseInt can only be used with signed types");
|
||||
while (isspace(*s)) {
|
||||
s++;
|
||||
}
|
||||
|
||||
int base = (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) ? 16 : 10;
|
||||
errno = 0;
|
||||
char* end;
|
||||
long long int result = strtoll(s, &end, base);
|
||||
if (errno != 0) {
|
||||
return false;
|
||||
}
|
||||
if (s == end || *end != '\0') {
|
||||
errno = EINVAL;
|
||||
return false;
|
||||
}
|
||||
if (result < min || max < result) {
|
||||
errno = ERANGE;
|
||||
return false;
|
||||
}
|
||||
if (out != nullptr) {
|
||||
*out = static_cast<T>(result);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: string_view
|
||||
template <typename T>
|
||||
bool ParseInt(const std::string& s, T* out,
|
||||
T min = std::numeric_limits<T>::min(),
|
||||
T max = std::numeric_limits<T>::max()) {
|
||||
return ParseInt(s.c_str(), out, min, max);
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,35 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
// Parses |address| into |host| and |port|.
|
||||
//
|
||||
// If |address| doesn't contain a port number, the default value is taken from
|
||||
// |port|. If |canonical_address| is non-null it will be set to "host:port" or
|
||||
// "[host]:port" as appropriate.
|
||||
//
|
||||
// On failure, returns false and fills |error|.
|
||||
bool ParseNetAddress(const std::string& address, std::string* host, int* port,
|
||||
std::string* canonical_address, std::string* error);
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,60 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <dirent.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
class AllPids {
|
||||
class PidIterator {
|
||||
public:
|
||||
PidIterator(DIR* dir) : dir_(dir, closedir) { Increment(); }
|
||||
PidIterator& operator++() {
|
||||
Increment();
|
||||
return *this;
|
||||
}
|
||||
bool operator==(const PidIterator& other) const { return pid_ == other.pid_; }
|
||||
bool operator!=(const PidIterator& other) const { return !(*this == other); }
|
||||
long operator*() const { return pid_; }
|
||||
// iterator traits
|
||||
using difference_type = pid_t;
|
||||
using value_type = pid_t;
|
||||
using pointer = const pid_t*;
|
||||
using reference = const pid_t&;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
|
||||
private:
|
||||
void Increment();
|
||||
|
||||
pid_t pid_ = -1;
|
||||
std::unique_ptr<DIR, decltype(&closedir)> dir_;
|
||||
};
|
||||
|
||||
public:
|
||||
PidIterator begin() { return opendir("/proc"); }
|
||||
PidIterator end() { return nullptr; }
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,101 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
struct prop_info;
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
// Returns the current value of the system property `key`,
|
||||
// or `default_value` if the property is empty or doesn't exist.
|
||||
std::string GetProperty(const std::string& key, const std::string& default_value);
|
||||
|
||||
// Returns true if the system property `key` has the value "1", "y", "yes", "on", or "true",
|
||||
// false for "0", "n", "no", "off", or "false", or `default_value` otherwise.
|
||||
bool GetBoolProperty(const std::string& key, bool default_value);
|
||||
|
||||
// Returns the signed integer corresponding to the system property `key`.
|
||||
// If the property is empty, doesn't exist, doesn't have an integer value, or is outside
|
||||
// the optional bounds, returns `default_value`.
|
||||
template <typename T> T GetIntProperty(const std::string& key,
|
||||
T default_value,
|
||||
T min = std::numeric_limits<T>::min(),
|
||||
T max = std::numeric_limits<T>::max());
|
||||
|
||||
// Returns the unsigned integer corresponding to the system property `key`.
|
||||
// If the property is empty, doesn't exist, doesn't have an integer value, or is outside
|
||||
// the optional bound, returns `default_value`.
|
||||
template <typename T> T GetUintProperty(const std::string& key,
|
||||
T default_value,
|
||||
T max = std::numeric_limits<T>::max());
|
||||
|
||||
// Sets the system property `key` to `value`.
|
||||
bool SetProperty(const std::string& key, const std::string& value);
|
||||
|
||||
// Waits for the system property `key` to have the value `expected_value`.
|
||||
// Times out after `relative_timeout`.
|
||||
// Returns true on success, false on timeout.
|
||||
#if defined(__BIONIC__)
|
||||
bool WaitForProperty(const std::string& key, const std::string& expected_value,
|
||||
std::chrono::milliseconds relative_timeout = std::chrono::milliseconds::max());
|
||||
#endif
|
||||
|
||||
// Waits for the system property `key` to be created.
|
||||
// Times out after `relative_timeout`.
|
||||
// Returns true on success, false on timeout.
|
||||
#if defined(__BIONIC__)
|
||||
bool WaitForPropertyCreation(const std::string& key, std::chrono::milliseconds relative_timeout =
|
||||
std::chrono::milliseconds::max());
|
||||
#endif
|
||||
|
||||
#if defined(__BIONIC__) && __cplusplus >= 201703L
|
||||
// Cached system property lookup. For code that needs to read the same property multiple times,
|
||||
// this class helps optimize those lookups.
|
||||
class CachedProperty {
|
||||
public:
|
||||
explicit CachedProperty(const char* property_name);
|
||||
|
||||
// Returns the current value of the underlying system property as cheaply as possible.
|
||||
// The returned pointer is valid until the next call to Get. Because most callers are going
|
||||
// to want to parse the string returned here and cached that as well, this function performs
|
||||
// no locking, and is completely thread unsafe. It is the caller's responsibility to provide a
|
||||
// lock for thread-safety.
|
||||
//
|
||||
// Note: *changed can be set to true even if the contents of the property remain the same.
|
||||
const char* Get(bool* changed = nullptr);
|
||||
|
||||
private:
|
||||
std::string property_name_;
|
||||
const prop_info* prop_info_;
|
||||
std::optional<uint32_t> cached_area_serial_;
|
||||
std::optional<uint32_t> cached_property_serial_;
|
||||
char cached_value_[92];
|
||||
bool is_read_only_;
|
||||
const char* read_only_property_;
|
||||
};
|
||||
#endif
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,235 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// This file contains classes for returning a successful result along with an optional
|
||||
// arbitrarily typed return value or for returning a failure result along with an optional string
|
||||
// indicating why the function failed.
|
||||
|
||||
// There are 3 classes that implement this functionality and one additional helper type.
|
||||
//
|
||||
// Result<T> either contains a member of type T that can be accessed using similar semantics as
|
||||
// std::optional<T> or it contains a ResultError describing an error, which can be accessed via
|
||||
// Result<T>::error().
|
||||
//
|
||||
// ResultError is a type that contains both a std::string describing the error and a copy of errno
|
||||
// from when the error occurred. ResultError can be used in an ostream directly to print its
|
||||
// string value.
|
||||
//
|
||||
// Result<void> is the correct return type for a function that either returns successfully or
|
||||
// returns an error value. Returning {} from a function that returns Result<void> is the
|
||||
// correct way to indicate that a function without a return type has completed successfully.
|
||||
//
|
||||
// A successful Result<T> is constructed implicitly from any type that can be implicitly converted
|
||||
// to T or from the constructor arguments for T. This allows you to return a type T directly from
|
||||
// a function that returns Result<T>.
|
||||
//
|
||||
// Error and ErrnoError are used to construct a Result<T> that has failed. The Error class takes
|
||||
// an ostream as an input and are implicitly cast to a Result<T> containing that failure.
|
||||
// ErrnoError() is a helper function to create an Error class that appends ": " + strerror(errno)
|
||||
// to the end of the failure string to aid in interacting with C APIs. Alternatively, an errno
|
||||
// value can be directly specified via the Error() constructor.
|
||||
//
|
||||
// Errorf and ErrnoErrorf accept the format string syntax of the fmblib (https://fmt.dev).
|
||||
// Errorf("{} errors", num) is equivalent to Error() << num << " errors".
|
||||
//
|
||||
// ResultError can be used in the ostream and when using Error/Errorf to construct a Result<T>.
|
||||
// In this case, the string that the ResultError takes is passed through the stream normally, but
|
||||
// the errno is passed to the Result<T>. This can be used to pass errno from a failing C function up
|
||||
// multiple callers. Note that when the outer Result<T> is created with ErrnoError/ErrnoErrorf then
|
||||
// the errno from the inner ResultError is not passed. Also when multiple ResultError objects are
|
||||
// used, the errno of the last one is respected.
|
||||
//
|
||||
// ResultError can also directly construct a Result<T>. This is particularly useful if you have a
|
||||
// function that return Result<T> but you have a Result<U> and want to return its error. In this
|
||||
// case, you can return the .error() from the Result<U> to construct the Result<T>.
|
||||
|
||||
// An example of how to use these is below:
|
||||
// Result<U> CalculateResult(const T& input) {
|
||||
// U output;
|
||||
// if (!SomeOtherCppFunction(input, &output)) {
|
||||
// return Errorf("SomeOtherCppFunction {} failed", input);
|
||||
// }
|
||||
// if (!c_api_function(output)) {
|
||||
// return ErrnoErrorf("c_api_function {} failed", output);
|
||||
// }
|
||||
// return output;
|
||||
// }
|
||||
//
|
||||
// auto output = CalculateResult(input);
|
||||
// if (!output) return Error() << "CalculateResult failed: " << output.error();
|
||||
// UseOutput(*output);
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "android-base/expected.h"
|
||||
#include "android-base/format.h"
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
struct ResultError {
|
||||
template <typename T>
|
||||
ResultError(T&& message, int code) : message_(std::forward<T>(message)), code_(code) {}
|
||||
|
||||
template <typename T>
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator android::base::expected<T, ResultError>() {
|
||||
return android::base::unexpected(ResultError(message_, code_));
|
||||
}
|
||||
|
||||
std::string message() const { return message_; }
|
||||
int code() const { return code_; }
|
||||
|
||||
private:
|
||||
std::string message_;
|
||||
int code_;
|
||||
};
|
||||
|
||||
inline bool operator==(const ResultError& lhs, const ResultError& rhs) {
|
||||
return lhs.message() == rhs.message() && lhs.code() == rhs.code();
|
||||
}
|
||||
|
||||
inline bool operator!=(const ResultError& lhs, const ResultError& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, const ResultError& t) {
|
||||
os << t.message();
|
||||
return os;
|
||||
}
|
||||
|
||||
class Error {
|
||||
public:
|
||||
Error() : errno_(0), append_errno_(false) {}
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
Error(int errno_to_append) : errno_(errno_to_append), append_errno_(true) {}
|
||||
|
||||
template <typename T>
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator android::base::expected<T, ResultError>() {
|
||||
return android::base::unexpected(ResultError(str(), errno_));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Error& operator<<(T&& t) {
|
||||
// NOLINTNEXTLINE(bugprone-suspicious-semicolon)
|
||||
if constexpr (std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, ResultError>) {
|
||||
errno_ = t.code();
|
||||
return (*this) << t.message();
|
||||
}
|
||||
int saved = errno;
|
||||
ss_ << t;
|
||||
errno = saved;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const std::string str() const {
|
||||
std::string str = ss_.str();
|
||||
if (append_errno_) {
|
||||
if (str.empty()) {
|
||||
return strerror(errno_);
|
||||
}
|
||||
return std::move(str) + ": " + strerror(errno_);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
Error(const Error&) = delete;
|
||||
Error(Error&&) = delete;
|
||||
Error& operator=(const Error&) = delete;
|
||||
Error& operator=(Error&&) = delete;
|
||||
|
||||
template <typename T, typename... Args>
|
||||
friend Error ErrorfImpl(const T&& fmt, const Args&... args);
|
||||
|
||||
template <typename T, typename... Args>
|
||||
friend Error ErrnoErrorfImpl(const T&& fmt, const Args&... args);
|
||||
|
||||
private:
|
||||
Error(bool append_errno, int errno_to_append, const std::string& message)
|
||||
: errno_(errno_to_append), append_errno_(append_errno) {
|
||||
(*this) << message;
|
||||
}
|
||||
|
||||
std::stringstream ss_;
|
||||
int errno_;
|
||||
const bool append_errno_;
|
||||
};
|
||||
|
||||
inline Error ErrnoError() {
|
||||
return Error(errno);
|
||||
}
|
||||
|
||||
inline int ErrorCode(int code) {
|
||||
return code;
|
||||
}
|
||||
|
||||
// Return the error code of the last ResultError object, if any.
|
||||
// Otherwise, return `code` as it is.
|
||||
template <typename T, typename... Args>
|
||||
inline int ErrorCode(int code, T&& t, const Args&... args) {
|
||||
if constexpr (std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, ResultError>) {
|
||||
return ErrorCode(t.code(), args...);
|
||||
}
|
||||
return ErrorCode(code, args...);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
inline Error ErrorfImpl(const T&& fmt, const Args&... args) {
|
||||
return Error(false, ErrorCode(0, args...), fmt::format(fmt, args...));
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
inline Error ErrnoErrorfImpl(const T&& fmt, const Args&... args) {
|
||||
return Error(true, errno, fmt::format(fmt, args...));
|
||||
}
|
||||
|
||||
#define Errorf(fmt, ...) android::base::ErrorfImpl(FMT_STRING(fmt), ##__VA_ARGS__)
|
||||
#define ErrnoErrorf(fmt, ...) android::base::ErrnoErrorfImpl(FMT_STRING(fmt), ##__VA_ARGS__)
|
||||
|
||||
template <typename T>
|
||||
using Result = android::base::expected<T, ResultError>;
|
||||
|
||||
// Macros for testing the results of functions that return android::base::Result.
|
||||
// These also work with base::android::expected.
|
||||
|
||||
#define CHECK_RESULT_OK(stmt) \
|
||||
do { \
|
||||
const auto& tmp = (stmt); \
|
||||
CHECK(tmp.ok()) << tmp.error(); \
|
||||
} while (0)
|
||||
|
||||
#define ASSERT_RESULT_OK(stmt) \
|
||||
do { \
|
||||
const auto& tmp = (stmt); \
|
||||
ASSERT_TRUE(tmp.ok()) << tmp.error(); \
|
||||
} while (0)
|
||||
|
||||
#define EXPECT_RESULT_OK(stmt) \
|
||||
do { \
|
||||
auto tmp = (stmt); \
|
||||
EXPECT_TRUE(tmp.ok()) << tmp.error(); \
|
||||
} while (0)
|
||||
|
||||
// TODO: Maybe add RETURN_IF_ERROR() and ASSIGN_OR_RETURN()
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,67 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility> // for std::move, std::forward
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
// ScopeGuard ensures that the specified functor is executed no matter how the
|
||||
// current scope exits.
|
||||
template <typename F>
|
||||
class ScopeGuard {
|
||||
public:
|
||||
ScopeGuard(F&& f) : f_(std::forward<F>(f)), active_(true) {}
|
||||
|
||||
ScopeGuard(ScopeGuard&& that) noexcept : f_(std::move(that.f_)), active_(that.active_) {
|
||||
that.active_ = false;
|
||||
}
|
||||
|
||||
template <typename Functor>
|
||||
ScopeGuard(ScopeGuard<Functor>&& that) : f_(std::move(that.f_)), active_(that.active_) {
|
||||
that.active_ = false;
|
||||
}
|
||||
|
||||
~ScopeGuard() {
|
||||
if (active_) f_();
|
||||
}
|
||||
|
||||
ScopeGuard() = delete;
|
||||
ScopeGuard(const ScopeGuard&) = delete;
|
||||
void operator=(const ScopeGuard&) = delete;
|
||||
void operator=(ScopeGuard&& that) = delete;
|
||||
|
||||
void Disable() { active_ = false; }
|
||||
|
||||
bool active() const { return active_; }
|
||||
|
||||
private:
|
||||
template <typename Functor>
|
||||
friend class ScopeGuard;
|
||||
|
||||
F f_;
|
||||
bool active_;
|
||||
};
|
||||
|
||||
template <typename F>
|
||||
ScopeGuard<F> make_scope_guard(F&& f) {
|
||||
return ScopeGuard<F>(std::forward<F>(f));
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,40 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <string>
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
// These printf-like functions are implemented in terms of vsnprintf, so they
|
||||
// use the same attribute for compile-time format string checking.
|
||||
|
||||
// Returns a string corresponding to printf-like formatting of the arguments.
|
||||
std::string StringPrintf(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
|
||||
|
||||
// Appends a printf-like formatting of the arguments to 'dst'.
|
||||
void StringAppendF(std::string* dst, const char* fmt, ...)
|
||||
__attribute__((__format__(__printf__, 2, 3)));
|
||||
|
||||
// Appends a printf-like formatting of the arguments to 'dst'.
|
||||
void StringAppendV(std::string* dst, const char* format, va_list ap)
|
||||
__attribute__((__format__(__printf__, 2, 0)));
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,94 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
// Splits a string into a vector of strings.
|
||||
//
|
||||
// The string is split at each occurrence of a character in delimiters.
|
||||
//
|
||||
// The empty string is not a valid delimiter list.
|
||||
std::vector<std::string> Split(const std::string& s,
|
||||
const std::string& delimiters);
|
||||
|
||||
// Trims whitespace off both ends of the given string.
|
||||
std::string Trim(const std::string& s);
|
||||
|
||||
// Joins a container of things into a single string, using the given separator.
|
||||
template <typename ContainerT, typename SeparatorT>
|
||||
std::string Join(const ContainerT& things, SeparatorT separator) {
|
||||
if (things.empty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
std::ostringstream result;
|
||||
result << *things.begin();
|
||||
for (auto it = std::next(things.begin()); it != things.end(); ++it) {
|
||||
result << separator << *it;
|
||||
}
|
||||
return result.str();
|
||||
}
|
||||
|
||||
// We instantiate the common cases in strings.cpp.
|
||||
extern template std::string Join(const std::vector<std::string>&, char);
|
||||
extern template std::string Join(const std::vector<const char*>&, char);
|
||||
extern template std::string Join(const std::vector<std::string>&, const std::string&);
|
||||
extern template std::string Join(const std::vector<const char*>&, const std::string&);
|
||||
|
||||
// Tests whether 's' starts with 'prefix'.
|
||||
bool StartsWith(std::string_view s, std::string_view prefix);
|
||||
bool StartsWith(std::string_view s, char prefix);
|
||||
bool StartsWithIgnoreCase(std::string_view s, std::string_view prefix);
|
||||
|
||||
// Tests whether 's' ends with 'suffix'.
|
||||
bool EndsWith(std::string_view s, std::string_view suffix);
|
||||
bool EndsWith(std::string_view s, char suffix);
|
||||
bool EndsWithIgnoreCase(std::string_view s, std::string_view suffix);
|
||||
|
||||
// Tests whether 'lhs' equals 'rhs', ignoring case.
|
||||
bool EqualsIgnoreCase(std::string_view lhs, std::string_view rhs);
|
||||
|
||||
// Removes `prefix` from the start of the given string and returns true (if
|
||||
// it was present), false otherwise.
|
||||
inline bool ConsumePrefix(std::string_view* s, std::string_view prefix) {
|
||||
if (!StartsWith(*s, prefix)) return false;
|
||||
s->remove_prefix(prefix.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
// Removes `suffix` from the end of the given string and returns true (if
|
||||
// it was present), false otherwise.
|
||||
inline bool ConsumeSuffix(std::string_view* s, std::string_view suffix) {
|
||||
if (!EndsWith(*s, suffix)) return false;
|
||||
s->remove_suffix(suffix.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
// Replaces `from` with `to` in `s`, once if `all == false`, or as many times as
|
||||
// there are matches if `all == true`.
|
||||
[[nodiscard]] std::string StringReplace(std::string_view s, std::string_view from,
|
||||
std::string_view to, bool all);
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,86 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <regex>
|
||||
#include <string>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/macros.h>
|
||||
|
||||
class CapturedStdFd {
|
||||
public:
|
||||
CapturedStdFd(int std_fd);
|
||||
~CapturedStdFd();
|
||||
|
||||
std::string str();
|
||||
|
||||
void Start();
|
||||
void Stop();
|
||||
void Reset();
|
||||
|
||||
private:
|
||||
int fd() const;
|
||||
|
||||
TemporaryFile temp_file_;
|
||||
int std_fd_;
|
||||
int old_fd_ = -1;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CapturedStdFd);
|
||||
};
|
||||
|
||||
class CapturedStderr : public CapturedStdFd {
|
||||
public:
|
||||
CapturedStderr() : CapturedStdFd(STDERR_FILENO) {}
|
||||
};
|
||||
|
||||
class CapturedStdout : public CapturedStdFd {
|
||||
public:
|
||||
CapturedStdout() : CapturedStdFd(STDOUT_FILENO) {}
|
||||
};
|
||||
|
||||
#define ASSERT_MATCH(str, pattern) \
|
||||
do { \
|
||||
auto __s = (str); \
|
||||
if (!std::regex_search(__s, std::regex((pattern)))) { \
|
||||
FAIL() << "regex mismatch: expected " << (pattern) << " in:\n" << __s; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define ASSERT_NOT_MATCH(str, pattern) \
|
||||
do { \
|
||||
auto __s = (str); \
|
||||
if (std::regex_search(__s, std::regex((pattern)))) { \
|
||||
FAIL() << "regex mismatch: expected to not find " << (pattern) << " in:\n" << __s; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define EXPECT_MATCH(str, pattern) \
|
||||
do { \
|
||||
auto __s = (str); \
|
||||
if (!std::regex_search(__s, std::regex((pattern)))) { \
|
||||
ADD_FAILURE() << "regex mismatch: expected " << (pattern) << " in:\n" << __s; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define EXPECT_NOT_MATCH(str, pattern) \
|
||||
do { \
|
||||
auto __s = (str); \
|
||||
if (std::regex_search(__s, std::regex((pattern)))) { \
|
||||
ADD_FAILURE() << "regex mismatch: expected to not find " << (pattern) << " in:\n" << __s; \
|
||||
} \
|
||||
} while (0)
|
|
@ -1,144 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
|
||||
|
||||
#define CAPABILITY(x) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(capability(x))
|
||||
|
||||
#define SCOPED_CAPABILITY \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
|
||||
|
||||
#define SHARED_CAPABILITY(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(shared_capability(__VA_ARGS__))
|
||||
|
||||
#define GUARDED_BY(x) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
|
||||
|
||||
#define PT_GUARDED_BY(x) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
|
||||
|
||||
#define EXCLUSIVE_LOCKS_REQUIRED(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__))
|
||||
|
||||
#define SHARED_LOCKS_REQUIRED(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__))
|
||||
|
||||
#define ACQUIRED_BEFORE(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
|
||||
|
||||
#define ACQUIRED_AFTER(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))
|
||||
|
||||
#define REQUIRES(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__))
|
||||
|
||||
#define REQUIRES_SHARED(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__))
|
||||
|
||||
#define ACQUIRE(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__))
|
||||
|
||||
#define ACQUIRE_SHARED(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__))
|
||||
|
||||
#define RELEASE(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__))
|
||||
|
||||
#define RELEASE_SHARED(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__))
|
||||
|
||||
#define TRY_ACQUIRE(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__))
|
||||
|
||||
#define TRY_ACQUIRE_SHARED(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__))
|
||||
|
||||
#define EXCLUDES(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
|
||||
|
||||
#define ASSERT_CAPABILITY(x) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x))
|
||||
|
||||
#define ASSERT_SHARED_CAPABILITY(x) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x))
|
||||
|
||||
#define RETURN_CAPABILITY(x) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
|
||||
|
||||
#define EXCLUSIVE_LOCK_FUNCTION(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__))
|
||||
|
||||
#define EXCLUSIVE_TRYLOCK_FUNCTION(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__))
|
||||
|
||||
#define SHARED_LOCK_FUNCTION(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__))
|
||||
|
||||
#define SHARED_TRYLOCK_FUNCTION(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__))
|
||||
|
||||
#define UNLOCK_FUNCTION(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__))
|
||||
|
||||
#define SCOPED_LOCKABLE \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
|
||||
|
||||
#define LOCK_RETURNED(x) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
|
||||
|
||||
#define NO_THREAD_SAFETY_ANALYSIS \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
// A class to help thread safety analysis deal with std::unique_lock and condition_variable.
|
||||
//
|
||||
// Clang's thread safety analysis currently doesn't perform alias analysis, so movable types
|
||||
// like std::unique_lock can't be marked with thread safety annotations. This helper allows
|
||||
// for manual assertion of lock state in a scope.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// std::mutex mutex;
|
||||
// std::condition_variable cv;
|
||||
// std::vector<int> vec GUARDED_BY(mutex);
|
||||
//
|
||||
// int pop() {
|
||||
// std::unique_lock lock(mutex);
|
||||
// ScopedLockAssertion lock_assertion(mutex);
|
||||
// cv.wait(lock, []() {
|
||||
// ScopedLockAssertion lock_assertion(mutex);
|
||||
// return !vec.empty();
|
||||
// });
|
||||
//
|
||||
// int result = vec.back();
|
||||
// vec.pop_back();
|
||||
// return result;
|
||||
// }
|
||||
class SCOPED_CAPABILITY ScopedLockAssertion {
|
||||
public:
|
||||
ScopedLockAssertion(std::mutex& mutex) ACQUIRE(mutex) {}
|
||||
~ScopedLockAssertion() RELEASE() {}
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,30 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
uint64_t GetThreadId();
|
||||
}
|
||||
} // namespace android
|
||||
|
||||
#if defined(__GLIBC__)
|
||||
// bionic has this Linux-specifix call, but glibc doesn't.
|
||||
extern "C" int tgkill(int tgid, int tid, int sig);
|
||||
#endif
|
|
@ -1,293 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#if !defined(_WIN32)
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// DO NOT INCLUDE OTHER LIBBASE HEADERS!
|
||||
// This file gets used in libbinder, and libbinder is used everywhere.
|
||||
// Including other headers from libbase frequently results in inclusion of
|
||||
// android-base/macros.h, which causes macro collisions.
|
||||
|
||||
// Container for a file descriptor that automatically closes the descriptor as
|
||||
// it goes out of scope.
|
||||
//
|
||||
// unique_fd ufd(open("/some/path", "r"));
|
||||
// if (ufd.get() == -1) return error;
|
||||
//
|
||||
// // Do something useful, possibly including 'return'.
|
||||
//
|
||||
// return 0; // Descriptor is closed for you.
|
||||
//
|
||||
// unique_fd is also known as ScopedFd/ScopedFD/scoped_fd; mentioned here to help
|
||||
// you find this class if you're searching for one of those names.
|
||||
|
||||
#if defined(__BIONIC__)
|
||||
#include <android/fdsan.h>
|
||||
#endif
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
struct DefaultCloser {
|
||||
#if defined(__BIONIC__)
|
||||
static void Tag(int fd, void* old_addr, void* new_addr) {
|
||||
if (android_fdsan_exchange_owner_tag) {
|
||||
uint64_t old_tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD,
|
||||
reinterpret_cast<uint64_t>(old_addr));
|
||||
uint64_t new_tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD,
|
||||
reinterpret_cast<uint64_t>(new_addr));
|
||||
android_fdsan_exchange_owner_tag(fd, old_tag, new_tag);
|
||||
}
|
||||
}
|
||||
static void Close(int fd, void* addr) {
|
||||
if (android_fdsan_close_with_tag) {
|
||||
uint64_t tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD,
|
||||
reinterpret_cast<uint64_t>(addr));
|
||||
android_fdsan_close_with_tag(fd, tag);
|
||||
} else {
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
#else
|
||||
static void Close(int fd) {
|
||||
// Even if close(2) fails with EINTR, the fd will have been closed.
|
||||
// Using TEMP_FAILURE_RETRY will either lead to EBADF or closing someone
|
||||
// else's fd.
|
||||
// http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html
|
||||
::close(fd);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
template <typename Closer>
|
||||
class unique_fd_impl final {
|
||||
public:
|
||||
unique_fd_impl() {}
|
||||
|
||||
explicit unique_fd_impl(int fd) { reset(fd); }
|
||||
~unique_fd_impl() { reset(); }
|
||||
|
||||
unique_fd_impl(const unique_fd_impl&) = delete;
|
||||
void operator=(const unique_fd_impl&) = delete;
|
||||
unique_fd_impl(unique_fd_impl&& other) noexcept { reset(other.release()); }
|
||||
unique_fd_impl& operator=(unique_fd_impl&& s) noexcept {
|
||||
int fd = s.fd_;
|
||||
s.fd_ = -1;
|
||||
reset(fd, &s);
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[clang::reinitializes]] void reset(int new_value = -1) { reset(new_value, nullptr); }
|
||||
|
||||
int get() const { return fd_; }
|
||||
|
||||
#if !defined(ANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION)
|
||||
// unique_fd's operator int is dangerous, but we have way too much code that
|
||||
// depends on it, so make this opt-in at first.
|
||||
operator int() const { return get(); } // NOLINT
|
||||
#endif
|
||||
|
||||
bool operator>=(int rhs) const { return get() >= rhs; }
|
||||
bool operator<(int rhs) const { return get() < rhs; }
|
||||
bool operator==(int rhs) const { return get() == rhs; }
|
||||
bool operator!=(int rhs) const { return get() != rhs; }
|
||||
bool operator==(const unique_fd_impl& rhs) const { return get() == rhs.get(); }
|
||||
bool operator!=(const unique_fd_impl& rhs) const { return get() != rhs.get(); }
|
||||
|
||||
// Catch bogus error checks (i.e.: "!fd" instead of "fd != -1").
|
||||
bool operator!() const = delete;
|
||||
|
||||
bool ok() const { return get() >= 0; }
|
||||
|
||||
int release() __attribute__((warn_unused_result)) {
|
||||
tag(fd_, this, nullptr);
|
||||
int ret = fd_;
|
||||
fd_ = -1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
void reset(int new_value, void* previous_tag) {
|
||||
int previous_errno = errno;
|
||||
|
||||
if (fd_ != -1) {
|
||||
close(fd_, this);
|
||||
}
|
||||
|
||||
fd_ = new_value;
|
||||
if (new_value != -1) {
|
||||
tag(new_value, previous_tag, this);
|
||||
}
|
||||
|
||||
errno = previous_errno;
|
||||
}
|
||||
|
||||
int fd_ = -1;
|
||||
|
||||
// Template magic to use Closer::Tag if available, and do nothing if not.
|
||||
// If Closer::Tag exists, this implementation is preferred, because int is a better match.
|
||||
// If not, this implementation is SFINAEd away, and the no-op below is the only one that exists.
|
||||
template <typename T = Closer>
|
||||
static auto tag(int fd, void* old_tag, void* new_tag)
|
||||
-> decltype(T::Tag(fd, old_tag, new_tag), void()) {
|
||||
T::Tag(fd, old_tag, new_tag);
|
||||
}
|
||||
|
||||
template <typename T = Closer>
|
||||
static void tag(long, void*, void*) {
|
||||
// No-op.
|
||||
}
|
||||
|
||||
// Same as above, to select between Closer::Close(int) and Closer::Close(int, void*).
|
||||
template <typename T = Closer>
|
||||
static auto close(int fd, void* tag_value) -> decltype(T::Close(fd, tag_value), void()) {
|
||||
T::Close(fd, tag_value);
|
||||
}
|
||||
|
||||
template <typename T = Closer>
|
||||
static auto close(int fd, void*) -> decltype(T::Close(fd), void()) {
|
||||
T::Close(fd);
|
||||
}
|
||||
};
|
||||
|
||||
using unique_fd = unique_fd_impl<DefaultCloser>;
|
||||
|
||||
#if !defined(_WIN32)
|
||||
|
||||
// Inline functions, so that they can be used header-only.
|
||||
template <typename Closer>
|
||||
inline bool Pipe(unique_fd_impl<Closer>* read, unique_fd_impl<Closer>* write,
|
||||
int flags = O_CLOEXEC) {
|
||||
int pipefd[2];
|
||||
|
||||
#if defined(__linux__)
|
||||
if (pipe2(pipefd, flags) != 0) {
|
||||
return false;
|
||||
}
|
||||
#else // defined(__APPLE__)
|
||||
if (flags & ~(O_CLOEXEC | O_NONBLOCK)) {
|
||||
return false;
|
||||
}
|
||||
if (pipe(pipefd) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (flags & O_CLOEXEC) {
|
||||
if (fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) != 0 || fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) != 0) {
|
||||
close(pipefd[0]);
|
||||
close(pipefd[1]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (flags & O_NONBLOCK) {
|
||||
if (fcntl(pipefd[0], F_SETFL, O_NONBLOCK) != 0 || fcntl(pipefd[1], F_SETFL, O_NONBLOCK) != 0) {
|
||||
close(pipefd[0]);
|
||||
close(pipefd[1]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
read->reset(pipefd[0]);
|
||||
write->reset(pipefd[1]);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Closer>
|
||||
inline bool Socketpair(int domain, int type, int protocol, unique_fd_impl<Closer>* left,
|
||||
unique_fd_impl<Closer>* right) {
|
||||
int sockfd[2];
|
||||
if (socketpair(domain, type, protocol, sockfd) != 0) {
|
||||
return false;
|
||||
}
|
||||
left->reset(sockfd[0]);
|
||||
right->reset(sockfd[1]);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Closer>
|
||||
inline bool Socketpair(int type, unique_fd_impl<Closer>* left, unique_fd_impl<Closer>* right) {
|
||||
return Socketpair(AF_UNIX, type, 0, left, right);
|
||||
}
|
||||
|
||||
// Using fdopen with unique_fd correctly is more annoying than it should be,
|
||||
// because fdopen doesn't close the file descriptor received upon failure.
|
||||
inline FILE* Fdopen(unique_fd&& ufd, const char* mode) {
|
||||
int fd = ufd.release();
|
||||
FILE* file = fdopen(fd, mode);
|
||||
if (!file) {
|
||||
close(fd);
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
// Using fdopendir with unique_fd correctly is more annoying than it should be,
|
||||
// because fdopen doesn't close the file descriptor received upon failure.
|
||||
inline DIR* Fdopendir(unique_fd&& ufd) {
|
||||
int fd = ufd.release();
|
||||
DIR* dir = fdopendir(fd);
|
||||
if (dir == nullptr) {
|
||||
close(fd);
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
|
||||
#endif // !defined(_WIN32)
|
||||
|
||||
// A wrapper type that can be implicitly constructed from either int or unique_fd.
|
||||
struct borrowed_fd {
|
||||
/* implicit */ borrowed_fd(int fd) : fd_(fd) {} // NOLINT
|
||||
template <typename T>
|
||||
/* implicit */ borrowed_fd(const unique_fd_impl<T>& ufd) : fd_(ufd.get()) {} // NOLINT
|
||||
|
||||
int get() const { return fd_; }
|
||||
|
||||
bool operator>=(int rhs) const { return get() >= rhs; }
|
||||
bool operator<(int rhs) const { return get() < rhs; }
|
||||
bool operator==(int rhs) const { return get() == rhs; }
|
||||
bool operator!=(int rhs) const { return get() != rhs; }
|
||||
|
||||
private:
|
||||
int fd_ = -1;
|
||||
};
|
||||
} // namespace base
|
||||
} // namespace android
|
||||
|
||||
template <typename T>
|
||||
int close(const android::base::unique_fd_impl<T>&)
|
||||
__attribute__((__unavailable__("close called on unique_fd")));
|
||||
|
||||
template <typename T>
|
||||
FILE* fdopen(const android::base::unique_fd_impl<T>&, const char* mode)
|
||||
__attribute__((__unavailable__("fdopen takes ownership of the fd passed in; either dup the "
|
||||
"unique_fd, or use android::base::Fdopen to pass ownership")));
|
||||
|
||||
template <typename T>
|
||||
DIR* fdopendir(const android::base::unique_fd_impl<T>&) __attribute__((
|
||||
__unavailable__("fdopendir takes ownership of the fd passed in; either dup the "
|
||||
"unique_fd, or use android::base::Fdopendir to pass ownership")));
|
|
@ -1,104 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <sys/types.h>
|
||||
#include <string>
|
||||
#else
|
||||
// Bring in prototypes for standard APIs so that we can import them into the utf8 namespace.
|
||||
#include <fcntl.h> // open
|
||||
#include <stdio.h> // fopen
|
||||
#include <sys/stat.h> // mkdir
|
||||
#include <unistd.h> // unlink
|
||||
#endif
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
// Only available on Windows because this is only needed on Windows.
|
||||
#ifdef _WIN32
|
||||
// Convert size number of UTF-16 wchar_t's to UTF-8. Returns whether the
|
||||
// conversion was done successfully.
|
||||
bool WideToUTF8(const wchar_t* utf16, const size_t size, std::string* utf8);
|
||||
|
||||
// Convert a NULL-terminated string of UTF-16 characters to UTF-8. Returns
|
||||
// whether the conversion was done successfully.
|
||||
bool WideToUTF8(const wchar_t* utf16, std::string* utf8);
|
||||
|
||||
// Convert a UTF-16 std::wstring (including any embedded NULL characters) to
|
||||
// UTF-8. Returns whether the conversion was done successfully.
|
||||
bool WideToUTF8(const std::wstring& utf16, std::string* utf8);
|
||||
|
||||
// Convert size number of UTF-8 char's to UTF-16. Returns whether the conversion
|
||||
// was done successfully.
|
||||
bool UTF8ToWide(const char* utf8, const size_t size, std::wstring* utf16);
|
||||
|
||||
// Convert a NULL-terminated string of UTF-8 characters to UTF-16. Returns
|
||||
// whether the conversion was done successfully.
|
||||
bool UTF8ToWide(const char* utf8, std::wstring* utf16);
|
||||
|
||||
// Convert a UTF-8 std::string (including any embedded NULL characters) to
|
||||
// UTF-16. Returns whether the conversion was done successfully.
|
||||
bool UTF8ToWide(const std::string& utf8, std::wstring* utf16);
|
||||
|
||||
// Convert a file system path, represented as a NULL-terminated string of
|
||||
// UTF-8 characters, to a UTF-16 string representing the same file system
|
||||
// path using the Windows extended-lengh path representation.
|
||||
//
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#MAXPATH:
|
||||
// ```The Windows API has many functions that also have Unicode versions to
|
||||
// permit an extended-length path for a maximum total path length of 32,767
|
||||
// characters. To specify an extended-length path, use the "\\?\" prefix.
|
||||
// For example, "\\?\D:\very long path".```
|
||||
//
|
||||
// Returns whether the conversion was done successfully.
|
||||
bool UTF8PathToWindowsLongPath(const char* utf8, std::wstring* utf16);
|
||||
#endif
|
||||
|
||||
// The functions in the utf8 namespace take UTF-8 strings. For Windows, these
|
||||
// are wrappers, for non-Windows these just expose existing APIs. To call these
|
||||
// functions, use:
|
||||
//
|
||||
// // anonymous namespace to avoid conflict with existing open(), unlink(), etc.
|
||||
// namespace {
|
||||
// // Import functions into anonymous namespace.
|
||||
// using namespace android::base::utf8;
|
||||
//
|
||||
// void SomeFunction(const char* name) {
|
||||
// int fd = open(name, ...); // Calls android::base::utf8::open().
|
||||
// ...
|
||||
// unlink(name); // Calls android::base::utf8::unlink().
|
||||
// }
|
||||
// }
|
||||
namespace utf8 {
|
||||
|
||||
#ifdef _WIN32
|
||||
FILE* fopen(const char* name, const char* mode);
|
||||
int mkdir(const char* name, mode_t mode);
|
||||
int open(const char* name, int flags, ...);
|
||||
int unlink(const char* name);
|
||||
#else
|
||||
using ::fopen;
|
||||
using ::mkdir;
|
||||
using ::open;
|
||||
using ::unlink;
|
||||
#endif
|
||||
|
||||
} // namespace utf8
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,89 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "liblog_symbols.h"
|
||||
|
||||
#if defined(__ANDROID_SDK_VERSION__) && (__ANDROID_SDK_VERSION__ <= 29)
|
||||
#define USE_DLSYM
|
||||
#endif
|
||||
|
||||
#ifdef USE_DLSYM
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
#ifdef USE_DLSYM
|
||||
|
||||
const std::optional<LibLogFunctions>& GetLibLogFunctions() {
|
||||
static std::optional<LibLogFunctions> liblog_functions = []() -> std::optional<LibLogFunctions> {
|
||||
void* liblog_handle = dlopen("liblog.so", RTLD_NOW);
|
||||
if (liblog_handle == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
LibLogFunctions real_liblog_functions = {};
|
||||
|
||||
#define DLSYM(name) \
|
||||
real_liblog_functions.name = \
|
||||
reinterpret_cast<decltype(LibLogFunctions::name)>(dlsym(liblog_handle, #name)); \
|
||||
if (real_liblog_functions.name == nullptr) { \
|
||||
return {}; \
|
||||
}
|
||||
|
||||
DLSYM(__android_log_set_logger)
|
||||
DLSYM(__android_log_write_log_message)
|
||||
DLSYM(__android_log_logd_logger)
|
||||
DLSYM(__android_log_stderr_logger)
|
||||
DLSYM(__android_log_set_aborter)
|
||||
DLSYM(__android_log_call_aborter)
|
||||
DLSYM(__android_log_default_aborter)
|
||||
DLSYM(__android_log_set_minimum_priority);
|
||||
DLSYM(__android_log_get_minimum_priority);
|
||||
DLSYM(__android_log_set_default_tag);
|
||||
#undef DLSYM
|
||||
|
||||
return real_liblog_functions;
|
||||
}();
|
||||
|
||||
return liblog_functions;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
const std::optional<LibLogFunctions>& GetLibLogFunctions() {
|
||||
static std::optional<LibLogFunctions> liblog_functions = []() -> std::optional<LibLogFunctions> {
|
||||
return LibLogFunctions{
|
||||
.__android_log_set_logger = __android_log_set_logger,
|
||||
.__android_log_write_log_message = __android_log_write_log_message,
|
||||
.__android_log_logd_logger = __android_log_logd_logger,
|
||||
.__android_log_stderr_logger = __android_log_stderr_logger,
|
||||
.__android_log_set_aborter = __android_log_set_aborter,
|
||||
.__android_log_call_aborter = __android_log_call_aborter,
|
||||
.__android_log_default_aborter = __android_log_default_aborter,
|
||||
.__android_log_set_minimum_priority = __android_log_set_minimum_priority,
|
||||
.__android_log_get_minimum_priority = __android_log_get_minimum_priority,
|
||||
.__android_log_set_default_tag = __android_log_set_default_tag,
|
||||
};
|
||||
}();
|
||||
return liblog_functions;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,44 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <android/log.h>
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
struct LibLogFunctions {
|
||||
void (*__android_log_set_logger)(__android_logger_function logger);
|
||||
void (*__android_log_write_log_message)(struct __android_log_message* log_message);
|
||||
|
||||
void (*__android_log_logd_logger)(const struct __android_log_message* log_message);
|
||||
void (*__android_log_stderr_logger)(const struct __android_log_message* log_message);
|
||||
|
||||
void (*__android_log_set_aborter)(__android_aborter_function aborter);
|
||||
void (*__android_log_call_aborter)(const char* abort_message);
|
||||
void (*__android_log_default_aborter)(const char* abort_message);
|
||||
int32_t (*__android_log_set_minimum_priority)(int32_t priority);
|
||||
int32_t (*__android_log_get_minimum_priority)();
|
||||
void (*__android_log_set_default_tag)(const char* tag);
|
||||
};
|
||||
|
||||
const std::optional<LibLogFunctions>& GetLibLogFunctions();
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
585
base/logging.cpp
585
base/logging.cpp
|
@ -1,585 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "android-base/logging.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <libgen.h>
|
||||
#include <time.h>
|
||||
|
||||
// For getprogname(3) or program_invocation_short_name.
|
||||
#if defined(__ANDROID__) || defined(__APPLE__)
|
||||
#include <stdlib.h>
|
||||
#elif defined(__GLIBC__)
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <sys/uio.h>
|
||||
#endif
|
||||
|
||||
#include <atomic>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <android/log.h>
|
||||
#ifdef __ANDROID__
|
||||
#include <android/set_abort_message.h>
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/macros.h>
|
||||
#include <android-base/parseint.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <android-base/threads.h>
|
||||
|
||||
#include "liblog_symbols.h"
|
||||
#include "logging_splitters.h"
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
// BSD-based systems like Android/macOS have getprogname(). Others need us to provide one.
|
||||
#if defined(__GLIBC__) || defined(_WIN32)
|
||||
static const char* getprogname() {
|
||||
#if defined(__GLIBC__)
|
||||
return program_invocation_short_name;
|
||||
#elif defined(_WIN32)
|
||||
static bool first = true;
|
||||
static char progname[MAX_PATH] = {};
|
||||
|
||||
if (first) {
|
||||
snprintf(progname, sizeof(progname), "%s",
|
||||
android::base::Basename(android::base::GetExecutablePath()).c_str());
|
||||
first = false;
|
||||
}
|
||||
|
||||
return progname;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
static const char* GetFileBasename(const char* file) {
|
||||
// We can't use basename(3) even on Unix because the Mac doesn't
|
||||
// have a non-modifying basename.
|
||||
const char* last_slash = strrchr(file, '/');
|
||||
if (last_slash != nullptr) {
|
||||
return last_slash + 1;
|
||||
}
|
||||
#if defined(_WIN32)
|
||||
const char* last_backslash = strrchr(file, '\\');
|
||||
if (last_backslash != nullptr) {
|
||||
return last_backslash + 1;
|
||||
}
|
||||
#endif
|
||||
return file;
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
static int OpenKmsg() {
|
||||
#if defined(__ANDROID__)
|
||||
// pick up 'file w /dev/kmsg' environment from daemon's init rc file
|
||||
const auto val = getenv("ANDROID_FILE__dev_kmsg");
|
||||
if (val != nullptr) {
|
||||
int fd;
|
||||
if (android::base::ParseInt(val, &fd, 0)) {
|
||||
auto flags = fcntl(fd, F_GETFL);
|
||||
if ((flags != -1) && ((flags & O_ACCMODE) == O_WRONLY)) return fd;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return TEMP_FAILURE_RETRY(open("/dev/kmsg", O_WRONLY | O_CLOEXEC));
|
||||
}
|
||||
#endif
|
||||
|
||||
static LogId log_id_tToLogId(int32_t buffer_id) {
|
||||
switch (buffer_id) {
|
||||
case LOG_ID_MAIN:
|
||||
return MAIN;
|
||||
case LOG_ID_SYSTEM:
|
||||
return SYSTEM;
|
||||
case LOG_ID_RADIO:
|
||||
return RADIO;
|
||||
case LOG_ID_CRASH:
|
||||
return CRASH;
|
||||
case LOG_ID_DEFAULT:
|
||||
default:
|
||||
return DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t LogIdTolog_id_t(LogId log_id) {
|
||||
switch (log_id) {
|
||||
case MAIN:
|
||||
return LOG_ID_MAIN;
|
||||
case SYSTEM:
|
||||
return LOG_ID_SYSTEM;
|
||||
case RADIO:
|
||||
return LOG_ID_RADIO;
|
||||
case CRASH:
|
||||
return LOG_ID_CRASH;
|
||||
case DEFAULT:
|
||||
default:
|
||||
return LOG_ID_DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
static LogSeverity PriorityToLogSeverity(int priority) {
|
||||
switch (priority) {
|
||||
case ANDROID_LOG_DEFAULT:
|
||||
return INFO;
|
||||
case ANDROID_LOG_VERBOSE:
|
||||
return VERBOSE;
|
||||
case ANDROID_LOG_DEBUG:
|
||||
return DEBUG;
|
||||
case ANDROID_LOG_INFO:
|
||||
return INFO;
|
||||
case ANDROID_LOG_WARN:
|
||||
return WARNING;
|
||||
case ANDROID_LOG_ERROR:
|
||||
return ERROR;
|
||||
case ANDROID_LOG_FATAL:
|
||||
return FATAL;
|
||||
default:
|
||||
return FATAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t LogSeverityToPriority(LogSeverity severity) {
|
||||
switch (severity) {
|
||||
case VERBOSE:
|
||||
return ANDROID_LOG_VERBOSE;
|
||||
case DEBUG:
|
||||
return ANDROID_LOG_DEBUG;
|
||||
case INFO:
|
||||
return ANDROID_LOG_INFO;
|
||||
case WARNING:
|
||||
return ANDROID_LOG_WARN;
|
||||
case ERROR:
|
||||
return ANDROID_LOG_ERROR;
|
||||
case FATAL_WITHOUT_ABORT:
|
||||
case FATAL:
|
||||
default:
|
||||
return ANDROID_LOG_FATAL;
|
||||
}
|
||||
}
|
||||
|
||||
static LogFunction& Logger() {
|
||||
#ifdef __ANDROID__
|
||||
static auto& logger = *new LogFunction(LogdLogger());
|
||||
#else
|
||||
static auto& logger = *new LogFunction(StderrLogger);
|
||||
#endif
|
||||
return logger;
|
||||
}
|
||||
|
||||
static AbortFunction& Aborter() {
|
||||
static auto& aborter = *new AbortFunction(DefaultAborter);
|
||||
return aborter;
|
||||
}
|
||||
|
||||
// Only used for Q fallback.
|
||||
static std::recursive_mutex& TagLock() {
|
||||
static auto& tag_lock = *new std::recursive_mutex();
|
||||
return tag_lock;
|
||||
}
|
||||
// Only used for Q fallback.
|
||||
static std::string* gDefaultTag;
|
||||
|
||||
void SetDefaultTag(const std::string& tag) {
|
||||
static auto& liblog_functions = GetLibLogFunctions();
|
||||
if (liblog_functions) {
|
||||
liblog_functions->__android_log_set_default_tag(tag.c_str());
|
||||
} else {
|
||||
std::lock_guard<std::recursive_mutex> lock(TagLock());
|
||||
if (gDefaultTag != nullptr) {
|
||||
delete gDefaultTag;
|
||||
gDefaultTag = nullptr;
|
||||
}
|
||||
if (!tag.empty()) {
|
||||
gDefaultTag = new std::string(tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool gInitialized = false;
|
||||
|
||||
// Only used for Q fallback.
|
||||
static LogSeverity gMinimumLogSeverity = INFO;
|
||||
|
||||
#if defined(__linux__)
|
||||
static void KernelLogLine(const char* msg, int length, android::base::LogSeverity severity,
|
||||
const char* tag) {
|
||||
// clang-format off
|
||||
static constexpr int kLogSeverityToKernelLogLevel[] = {
|
||||
[android::base::VERBOSE] = 7, // KERN_DEBUG (there is no verbose kernel log
|
||||
// level)
|
||||
[android::base::DEBUG] = 7, // KERN_DEBUG
|
||||
[android::base::INFO] = 6, // KERN_INFO
|
||||
[android::base::WARNING] = 4, // KERN_WARNING
|
||||
[android::base::ERROR] = 3, // KERN_ERROR
|
||||
[android::base::FATAL_WITHOUT_ABORT] = 2, // KERN_CRIT
|
||||
[android::base::FATAL] = 2, // KERN_CRIT
|
||||
};
|
||||
// clang-format on
|
||||
static_assert(arraysize(kLogSeverityToKernelLogLevel) == android::base::FATAL + 1,
|
||||
"Mismatch in size of kLogSeverityToKernelLogLevel and values in LogSeverity");
|
||||
|
||||
static int klog_fd = OpenKmsg();
|
||||
if (klog_fd == -1) return;
|
||||
|
||||
int level = kLogSeverityToKernelLogLevel[severity];
|
||||
|
||||
// The kernel's printk buffer is only 1024 bytes.
|
||||
// TODO: should we automatically break up long lines into multiple lines?
|
||||
// Or we could log but with something like "..." at the end?
|
||||
char buf[1024] __attribute__((__uninitialized__));
|
||||
size_t size = snprintf(buf, sizeof(buf), "<%d>%s: %.*s\n", level, tag, length, msg);
|
||||
if (size > sizeof(buf)) {
|
||||
size = snprintf(buf, sizeof(buf), "<%d>%s: %zu-byte message too long for printk\n",
|
||||
level, tag, size);
|
||||
}
|
||||
|
||||
iovec iov[1];
|
||||
iov[0].iov_base = buf;
|
||||
iov[0].iov_len = size;
|
||||
TEMP_FAILURE_RETRY(writev(klog_fd, iov, 1));
|
||||
}
|
||||
|
||||
void KernelLogger(android::base::LogId, android::base::LogSeverity severity, const char* tag,
|
||||
const char*, unsigned int, const char* full_message) {
|
||||
SplitByLines(full_message, KernelLogLine, severity, tag);
|
||||
}
|
||||
#endif
|
||||
|
||||
void StderrLogger(LogId, LogSeverity severity, const char* tag, const char* file, unsigned int line,
|
||||
const char* message) {
|
||||
struct tm now;
|
||||
time_t t = time(nullptr);
|
||||
|
||||
#if defined(_WIN32)
|
||||
localtime_s(&now, &t);
|
||||
#else
|
||||
localtime_r(&t, &now);
|
||||
#endif
|
||||
auto output_string =
|
||||
StderrOutputGenerator(now, getpid(), GetThreadId(), severity, tag, file, line, message);
|
||||
|
||||
fputs(output_string.c_str(), stderr);
|
||||
}
|
||||
|
||||
void StdioLogger(LogId, LogSeverity severity, const char* /*tag*/, const char* /*file*/,
|
||||
unsigned int /*line*/, const char* message) {
|
||||
if (severity >= WARNING) {
|
||||
fflush(stdout);
|
||||
fprintf(stderr, "%s: %s\n", GetFileBasename(getprogname()), message);
|
||||
} else {
|
||||
fprintf(stdout, "%s\n", message);
|
||||
}
|
||||
}
|
||||
|
||||
void DefaultAborter(const char* abort_message) {
|
||||
#ifdef __ANDROID__
|
||||
android_set_abort_message(abort_message);
|
||||
#else
|
||||
UNUSED(abort_message);
|
||||
#endif
|
||||
abort();
|
||||
}
|
||||
|
||||
static void LogdLogChunk(LogId id, LogSeverity severity, const char* tag, const char* message) {
|
||||
int32_t lg_id = LogIdTolog_id_t(id);
|
||||
int32_t priority = LogSeverityToPriority(severity);
|
||||
|
||||
static auto& liblog_functions = GetLibLogFunctions();
|
||||
if (liblog_functions) {
|
||||
__android_log_message log_message = {sizeof(__android_log_message), lg_id, priority, tag,
|
||||
static_cast<const char*>(nullptr), 0, message};
|
||||
liblog_functions->__android_log_logd_logger(&log_message);
|
||||
} else {
|
||||
__android_log_buf_print(lg_id, priority, tag, "%s", message);
|
||||
}
|
||||
}
|
||||
|
||||
LogdLogger::LogdLogger(LogId default_log_id) : default_log_id_(default_log_id) {}
|
||||
|
||||
void LogdLogger::operator()(LogId id, LogSeverity severity, const char* tag, const char* file,
|
||||
unsigned int line, const char* message) {
|
||||
if (id == DEFAULT) {
|
||||
id = default_log_id_;
|
||||
}
|
||||
|
||||
SplitByLogdChunks(id, severity, tag, file, line, message, LogdLogChunk);
|
||||
}
|
||||
|
||||
void InitLogging(char* argv[], LogFunction&& logger, AbortFunction&& aborter) {
|
||||
SetLogger(std::forward<LogFunction>(logger));
|
||||
SetAborter(std::forward<AbortFunction>(aborter));
|
||||
|
||||
if (gInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
gInitialized = true;
|
||||
|
||||
// Stash the command line for later use. We can use /proc/self/cmdline on
|
||||
// Linux to recover this, but we don't have that luxury on the Mac/Windows,
|
||||
// and there are a couple of argv[0] variants that are commonly used.
|
||||
if (argv != nullptr) {
|
||||
SetDefaultTag(basename(argv[0]));
|
||||
}
|
||||
|
||||
const char* tags = getenv("ANDROID_LOG_TAGS");
|
||||
if (tags == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::string> specs = Split(tags, " ");
|
||||
for (size_t i = 0; i < specs.size(); ++i) {
|
||||
// "tag-pattern:[vdiwefs]"
|
||||
std::string spec(specs[i]);
|
||||
if (spec.size() == 3 && StartsWith(spec, "*:")) {
|
||||
switch (spec[2]) {
|
||||
case 'v':
|
||||
SetMinimumLogSeverity(VERBOSE);
|
||||
continue;
|
||||
case 'd':
|
||||
SetMinimumLogSeverity(DEBUG);
|
||||
continue;
|
||||
case 'i':
|
||||
SetMinimumLogSeverity(INFO);
|
||||
continue;
|
||||
case 'w':
|
||||
SetMinimumLogSeverity(WARNING);
|
||||
continue;
|
||||
case 'e':
|
||||
SetMinimumLogSeverity(ERROR);
|
||||
continue;
|
||||
case 'f':
|
||||
SetMinimumLogSeverity(FATAL_WITHOUT_ABORT);
|
||||
continue;
|
||||
// liblog will even suppress FATAL if you say 's' for silent, but that's
|
||||
// crazy!
|
||||
case 's':
|
||||
SetMinimumLogSeverity(FATAL_WITHOUT_ABORT);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
LOG(FATAL) << "unsupported '" << spec << "' in ANDROID_LOG_TAGS (" << tags
|
||||
<< ")";
|
||||
}
|
||||
}
|
||||
|
||||
void SetLogger(LogFunction&& logger) {
|
||||
Logger() = std::move(logger);
|
||||
|
||||
static auto& liblog_functions = GetLibLogFunctions();
|
||||
if (liblog_functions) {
|
||||
liblog_functions->__android_log_set_logger([](const struct __android_log_message* log_message) {
|
||||
auto log_id = log_id_tToLogId(log_message->buffer_id);
|
||||
auto severity = PriorityToLogSeverity(log_message->priority);
|
||||
|
||||
Logger()(log_id, severity, log_message->tag, log_message->file, log_message->line,
|
||||
log_message->message);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void SetAborter(AbortFunction&& aborter) {
|
||||
Aborter() = std::move(aborter);
|
||||
|
||||
static auto& liblog_functions = GetLibLogFunctions();
|
||||
if (liblog_functions) {
|
||||
liblog_functions->__android_log_set_aborter(
|
||||
[](const char* abort_message) { Aborter()(abort_message); });
|
||||
}
|
||||
}
|
||||
|
||||
// This indirection greatly reduces the stack impact of having lots of
|
||||
// checks/logging in a function.
|
||||
class LogMessageData {
|
||||
public:
|
||||
LogMessageData(const char* file, unsigned int line, LogSeverity severity, const char* tag,
|
||||
int error)
|
||||
: file_(GetFileBasename(file)),
|
||||
line_number_(line),
|
||||
severity_(severity),
|
||||
tag_(tag),
|
||||
error_(error) {}
|
||||
|
||||
const char* GetFile() const {
|
||||
return file_;
|
||||
}
|
||||
|
||||
unsigned int GetLineNumber() const {
|
||||
return line_number_;
|
||||
}
|
||||
|
||||
LogSeverity GetSeverity() const {
|
||||
return severity_;
|
||||
}
|
||||
|
||||
const char* GetTag() const { return tag_; }
|
||||
|
||||
int GetError() const {
|
||||
return error_;
|
||||
}
|
||||
|
||||
std::ostream& GetBuffer() {
|
||||
return buffer_;
|
||||
}
|
||||
|
||||
std::string ToString() const {
|
||||
return buffer_.str();
|
||||
}
|
||||
|
||||
private:
|
||||
std::ostringstream buffer_;
|
||||
const char* const file_;
|
||||
const unsigned int line_number_;
|
||||
const LogSeverity severity_;
|
||||
const char* const tag_;
|
||||
const int error_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(LogMessageData);
|
||||
};
|
||||
|
||||
LogMessage::LogMessage(const char* file, unsigned int line, LogId, LogSeverity severity,
|
||||
const char* tag, int error)
|
||||
: LogMessage(file, line, severity, tag, error) {}
|
||||
|
||||
LogMessage::LogMessage(const char* file, unsigned int line, LogSeverity severity, const char* tag,
|
||||
int error)
|
||||
: data_(new LogMessageData(file, line, severity, tag, error)) {}
|
||||
|
||||
LogMessage::~LogMessage() {
|
||||
// Check severity again. This is duplicate work wrt/ LOG macros, but not LOG_STREAM.
|
||||
if (!WOULD_LOG(data_->GetSeverity())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Finish constructing the message.
|
||||
if (data_->GetError() != -1) {
|
||||
data_->GetBuffer() << ": " << strerror(data_->GetError());
|
||||
}
|
||||
std::string msg(data_->ToString());
|
||||
|
||||
if (data_->GetSeverity() == FATAL) {
|
||||
#ifdef __ANDROID__
|
||||
// Set the bionic abort message early to avoid liblog doing it
|
||||
// with the individual lines, so that we get the whole message.
|
||||
android_set_abort_message(msg.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), data_->GetTag(),
|
||||
msg.c_str());
|
||||
|
||||
// Abort if necessary.
|
||||
if (data_->GetSeverity() == FATAL) {
|
||||
static auto& liblog_functions = GetLibLogFunctions();
|
||||
if (liblog_functions) {
|
||||
liblog_functions->__android_log_call_aborter(msg.c_str());
|
||||
} else {
|
||||
Aborter()(msg.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream& LogMessage::stream() {
|
||||
return data_->GetBuffer();
|
||||
}
|
||||
|
||||
void LogMessage::LogLine(const char* file, unsigned int line, LogSeverity severity, const char* tag,
|
||||
const char* message) {
|
||||
static auto& liblog_functions = GetLibLogFunctions();
|
||||
int32_t priority = LogSeverityToPriority(severity);
|
||||
if (liblog_functions) {
|
||||
__android_log_message log_message = {
|
||||
sizeof(__android_log_message), LOG_ID_DEFAULT, priority, tag, file, line, message};
|
||||
liblog_functions->__android_log_write_log_message(&log_message);
|
||||
} else {
|
||||
if (tag == nullptr) {
|
||||
std::lock_guard<std::recursive_mutex> lock(TagLock());
|
||||
if (gDefaultTag == nullptr) {
|
||||
gDefaultTag = new std::string(getprogname());
|
||||
}
|
||||
|
||||
Logger()(DEFAULT, severity, gDefaultTag->c_str(), file, line, message);
|
||||
} else {
|
||||
Logger()(DEFAULT, severity, tag, file, line, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LogSeverity GetMinimumLogSeverity() {
|
||||
static auto& liblog_functions = GetLibLogFunctions();
|
||||
if (liblog_functions) {
|
||||
return PriorityToLogSeverity(liblog_functions->__android_log_get_minimum_priority());
|
||||
} else {
|
||||
return gMinimumLogSeverity;
|
||||
}
|
||||
}
|
||||
|
||||
bool ShouldLog(LogSeverity severity, const char* tag) {
|
||||
static auto& liblog_functions = GetLibLogFunctions();
|
||||
// Even though we're not using the R liblog functions in this function, if we're running on Q,
|
||||
// we need to fall back to using gMinimumLogSeverity, since __android_log_is_loggable() will not
|
||||
// take into consideration the value from SetMinimumLogSeverity().
|
||||
if (liblog_functions) {
|
||||
int32_t priority = LogSeverityToPriority(severity);
|
||||
return __android_log_is_loggable(priority, tag, ANDROID_LOG_INFO);
|
||||
} else {
|
||||
return severity >= gMinimumLogSeverity;
|
||||
}
|
||||
}
|
||||
|
||||
LogSeverity SetMinimumLogSeverity(LogSeverity new_severity) {
|
||||
static auto& liblog_functions = GetLibLogFunctions();
|
||||
if (liblog_functions) {
|
||||
int32_t priority = LogSeverityToPriority(new_severity);
|
||||
return PriorityToLogSeverity(liblog_functions->__android_log_set_minimum_priority(priority));
|
||||
} else {
|
||||
LogSeverity old_severity = gMinimumLogSeverity;
|
||||
gMinimumLogSeverity = new_severity;
|
||||
return old_severity;
|
||||
}
|
||||
}
|
||||
|
||||
ScopedLogSeverity::ScopedLogSeverity(LogSeverity new_severity) {
|
||||
old_ = SetMinimumLogSeverity(new_severity);
|
||||
}
|
||||
|
||||
ScopedLogSeverity::~ScopedLogSeverity() {
|
||||
SetMinimumLogSeverity(old_);
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,185 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
|
||||
#define LOGGER_ENTRY_MAX_PAYLOAD 4068 // This constant is not in the NDK.
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
// This splits the message up line by line, by calling log_function with a pointer to the start of
|
||||
// each line and the size up to the newline character. It sends size = -1 for the final line.
|
||||
template <typename F, typename... Args>
|
||||
static void SplitByLines(const char* msg, const F& log_function, Args&&... args) {
|
||||
const char* newline = strchr(msg, '\n');
|
||||
while (newline != nullptr) {
|
||||
log_function(msg, newline - msg, args...);
|
||||
msg = newline + 1;
|
||||
newline = strchr(msg, '\n');
|
||||
}
|
||||
|
||||
log_function(msg, -1, args...);
|
||||
}
|
||||
|
||||
// This splits the message up into chunks that logs can process delimited by new lines. It calls
|
||||
// log_function with the exact null terminated message that should be sent to logd.
|
||||
// Note, despite the loops and snprintf's, if severity is not fatal and there are no new lines,
|
||||
// this function simply calls log_function with msg without any extra overhead.
|
||||
template <typename F>
|
||||
static void SplitByLogdChunks(LogId log_id, LogSeverity severity, const char* tag, const char* file,
|
||||
unsigned int line, const char* msg, const F& log_function) {
|
||||
// The maximum size of a payload, after the log header that logd will accept is
|
||||
// LOGGER_ENTRY_MAX_PAYLOAD, so subtract the other elements in the payload to find the size of
|
||||
// the string that we can log in each pass.
|
||||
// The protocol is documented in liblog/README.protocol.md.
|
||||
// Specifically we subtract a byte for the priority, the length of the tag + its null terminator,
|
||||
// and an additional byte for the null terminator on the payload. We subtract an additional 32
|
||||
// bytes for slack, similar to java/android/util/Log.java.
|
||||
ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - strlen(tag) - 35;
|
||||
if (max_size <= 0) {
|
||||
abort();
|
||||
}
|
||||
// If we're logging a fatal message, we'll append the file and line numbers.
|
||||
bool add_file = file != nullptr && (severity == FATAL || severity == FATAL_WITHOUT_ABORT);
|
||||
|
||||
std::string file_header;
|
||||
if (add_file) {
|
||||
file_header = StringPrintf("%s:%u] ", file, line);
|
||||
}
|
||||
int file_header_size = file_header.size();
|
||||
|
||||
__attribute__((uninitialized)) char logd_chunk[max_size + 1];
|
||||
ptrdiff_t chunk_position = 0;
|
||||
|
||||
auto call_log_function = [&]() {
|
||||
log_function(log_id, severity, tag, logd_chunk);
|
||||
chunk_position = 0;
|
||||
};
|
||||
|
||||
auto write_to_logd_chunk = [&](const char* message, int length) {
|
||||
int size_written = 0;
|
||||
const char* new_line = chunk_position > 0 ? "\n" : "";
|
||||
if (add_file) {
|
||||
size_written = snprintf(logd_chunk + chunk_position, sizeof(logd_chunk) - chunk_position,
|
||||
"%s%s%.*s", new_line, file_header.c_str(), length, message);
|
||||
} else {
|
||||
size_written = snprintf(logd_chunk + chunk_position, sizeof(logd_chunk) - chunk_position,
|
||||
"%s%.*s", new_line, length, message);
|
||||
}
|
||||
|
||||
// This should never fail, if it does and we set size_written to 0, which will skip this line
|
||||
// and move to the next one.
|
||||
if (size_written < 0) {
|
||||
size_written = 0;
|
||||
}
|
||||
chunk_position += size_written;
|
||||
};
|
||||
|
||||
const char* newline = strchr(msg, '\n');
|
||||
while (newline != nullptr) {
|
||||
// If we have data in the buffer and this next line doesn't fit, write the buffer.
|
||||
if (chunk_position != 0 && chunk_position + (newline - msg) + 1 + file_header_size > max_size) {
|
||||
call_log_function();
|
||||
}
|
||||
|
||||
// Otherwise, either the next line fits or we have any empty buffer and too large of a line to
|
||||
// ever fit, in both cases, we add it to the buffer and continue.
|
||||
write_to_logd_chunk(msg, newline - msg);
|
||||
|
||||
msg = newline + 1;
|
||||
newline = strchr(msg, '\n');
|
||||
}
|
||||
|
||||
// If we have left over data in the buffer and we can fit the rest of msg, add it to the buffer
|
||||
// then write the buffer.
|
||||
if (chunk_position != 0 &&
|
||||
chunk_position + static_cast<int>(strlen(msg)) + 1 + file_header_size <= max_size) {
|
||||
write_to_logd_chunk(msg, -1);
|
||||
call_log_function();
|
||||
} else {
|
||||
// If the buffer is not empty and we can't fit the rest of msg into it, write its contents.
|
||||
if (chunk_position != 0) {
|
||||
call_log_function();
|
||||
}
|
||||
// Then write the rest of the msg.
|
||||
if (add_file) {
|
||||
snprintf(logd_chunk, sizeof(logd_chunk), "%s%s", file_header.c_str(), msg);
|
||||
log_function(log_id, severity, tag, logd_chunk);
|
||||
} else {
|
||||
log_function(log_id, severity, tag, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static std::pair<int, int> CountSizeAndNewLines(const char* message) {
|
||||
int size = 0;
|
||||
int new_lines = 0;
|
||||
while (*message != '\0') {
|
||||
size++;
|
||||
if (*message == '\n') {
|
||||
++new_lines;
|
||||
}
|
||||
++message;
|
||||
}
|
||||
return {size, new_lines};
|
||||
}
|
||||
|
||||
// This adds the log header to each line of message and returns it as a string intended to be
|
||||
// written to stderr.
|
||||
static std::string StderrOutputGenerator(const struct tm& now, int pid, uint64_t tid,
|
||||
LogSeverity severity, const char* tag, const char* file,
|
||||
unsigned int line, const char* message) {
|
||||
char timestamp[32];
|
||||
strftime(timestamp, sizeof(timestamp), "%m-%d %H:%M:%S", &now);
|
||||
|
||||
static const char log_characters[] = "VDIWEFF";
|
||||
static_assert(arraysize(log_characters) - 1 == FATAL + 1,
|
||||
"Mismatch in size of log_characters and values in LogSeverity");
|
||||
char severity_char = log_characters[severity];
|
||||
std::string line_prefix;
|
||||
if (file != nullptr) {
|
||||
line_prefix = StringPrintf("%s %c %s %5d %5" PRIu64 " %s:%u] ", tag ? tag : "nullptr",
|
||||
severity_char, timestamp, pid, tid, file, line);
|
||||
} else {
|
||||
line_prefix = StringPrintf("%s %c %s %5d %5" PRIu64 " ", tag ? tag : "nullptr", severity_char,
|
||||
timestamp, pid, tid);
|
||||
}
|
||||
|
||||
auto [size, new_lines] = CountSizeAndNewLines(message);
|
||||
std::string output_string;
|
||||
output_string.reserve(size + new_lines * line_prefix.size() + 1);
|
||||
|
||||
auto concat_lines = [&](const char* message, int size) {
|
||||
output_string.append(line_prefix);
|
||||
if (size == -1) {
|
||||
output_string.append(message);
|
||||
} else {
|
||||
output_string.append(message, size);
|
||||
}
|
||||
output_string.append("\n");
|
||||
};
|
||||
SplitByLines(message, concat_lines);
|
||||
return output_string;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,325 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "logging_splitters.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/strings.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
void TestNewlineSplitter(const std::string& input,
|
||||
const std::vector<std::string>& expected_output) {
|
||||
std::vector<std::string> output;
|
||||
auto logger_function = [&](const char* msg, int length) {
|
||||
if (length == -1) {
|
||||
output.push_back(msg);
|
||||
} else {
|
||||
output.push_back(std::string(msg, length));
|
||||
}
|
||||
};
|
||||
SplitByLines(input.c_str(), logger_function);
|
||||
|
||||
EXPECT_EQ(expected_output, output);
|
||||
}
|
||||
|
||||
TEST(logging_splitters, NewlineSplitter_EmptyString) {
|
||||
TestNewlineSplitter("", std::vector<std::string>{""});
|
||||
}
|
||||
|
||||
TEST(logging_splitters, NewlineSplitter_BasicString) {
|
||||
TestNewlineSplitter("normal string", std::vector<std::string>{"normal string"});
|
||||
}
|
||||
|
||||
TEST(logging_splitters, NewlineSplitter_ormalBasicStringTrailingNewline) {
|
||||
TestNewlineSplitter("normal string\n", std::vector<std::string>{"normal string", ""});
|
||||
}
|
||||
|
||||
TEST(logging_splitters, NewlineSplitter_MultilineTrailing) {
|
||||
TestNewlineSplitter("normal string\nsecond string\nthirdstring",
|
||||
std::vector<std::string>{"normal string", "second string", "thirdstring"});
|
||||
}
|
||||
|
||||
TEST(logging_splitters, NewlineSplitter_MultilineTrailingNewline) {
|
||||
TestNewlineSplitter(
|
||||
"normal string\nsecond string\nthirdstring\n",
|
||||
std::vector<std::string>{"normal string", "second string", "thirdstring", ""});
|
||||
}
|
||||
|
||||
TEST(logging_splitters, NewlineSplitter_MultilineEmbeddedNewlines) {
|
||||
TestNewlineSplitter(
|
||||
"normal string\n\n\nsecond string\n\nthirdstring\n",
|
||||
std::vector<std::string>{"normal string", "", "", "second string", "", "thirdstring", ""});
|
||||
}
|
||||
|
||||
void TestLogdChunkSplitter(const std::string& tag, const std::string& file,
|
||||
const std::string& input,
|
||||
const std::vector<std::string>& expected_output) {
|
||||
std::vector<std::string> output;
|
||||
auto logger_function = [&](LogId, LogSeverity, const char*, const char* msg) {
|
||||
output.push_back(msg);
|
||||
};
|
||||
|
||||
SplitByLogdChunks(MAIN, FATAL, tag.c_str(), file.empty() ? nullptr : file.c_str(), 1000,
|
||||
input.c_str(), logger_function);
|
||||
|
||||
auto return_lengths = [&] {
|
||||
std::string sizes;
|
||||
sizes += "expected_output sizes:";
|
||||
for (const auto& string : expected_output) {
|
||||
sizes += " " + std::to_string(string.size());
|
||||
}
|
||||
sizes += "\noutput sizes:";
|
||||
for (const auto& string : output) {
|
||||
sizes += " " + std::to_string(string.size());
|
||||
}
|
||||
return sizes;
|
||||
};
|
||||
|
||||
EXPECT_EQ(expected_output, output) << return_lengths();
|
||||
}
|
||||
|
||||
TEST(logging_splitters, LogdChunkSplitter_EmptyString) {
|
||||
TestLogdChunkSplitter("tag", "", "", std::vector<std::string>{""});
|
||||
}
|
||||
|
||||
TEST(logging_splitters, LogdChunkSplitter_BasicString) {
|
||||
TestLogdChunkSplitter("tag", "", "normal string", std::vector<std::string>{"normal string"});
|
||||
}
|
||||
|
||||
TEST(logging_splitters, LogdChunkSplitter_NormalBasicStringTrailingNewline) {
|
||||
TestLogdChunkSplitter("tag", "", "normal string\n", std::vector<std::string>{"normal string\n"});
|
||||
}
|
||||
|
||||
TEST(logging_splitters, LogdChunkSplitter_MultilineTrailing) {
|
||||
TestLogdChunkSplitter("tag", "", "normal string\nsecond string\nthirdstring",
|
||||
std::vector<std::string>{"normal string\nsecond string\nthirdstring"});
|
||||
}
|
||||
|
||||
TEST(logging_splitters, LogdChunkSplitter_MultilineTrailingNewline) {
|
||||
TestLogdChunkSplitter("tag", "", "normal string\nsecond string\nthirdstring\n",
|
||||
std::vector<std::string>{"normal string\nsecond string\nthirdstring\n"});
|
||||
}
|
||||
|
||||
TEST(logging_splitters, LogdChunkSplitter_MultilineEmbeddedNewlines) {
|
||||
TestLogdChunkSplitter(
|
||||
"tag", "", "normal string\n\n\nsecond string\n\nthirdstring\n",
|
||||
std::vector<std::string>{"normal string\n\n\nsecond string\n\nthirdstring\n"});
|
||||
}
|
||||
|
||||
// This test should return the same string, the logd logger itself will truncate down to size.
|
||||
// This has historically been the behavior both in libbase and liblog.
|
||||
TEST(logging_splitters, LogdChunkSplitter_HugeLineNoNewline) {
|
||||
auto long_string = std::string(LOGGER_ENTRY_MAX_PAYLOAD, 'x');
|
||||
ASSERT_EQ(LOGGER_ENTRY_MAX_PAYLOAD, static_cast<int>(long_string.size()));
|
||||
|
||||
TestLogdChunkSplitter("tag", "", long_string, std::vector{long_string});
|
||||
}
|
||||
|
||||
std::string ReduceToMaxSize(const std::string& tag, const std::string& string) {
|
||||
return string.substr(0, LOGGER_ENTRY_MAX_PAYLOAD - tag.size() - 35);
|
||||
}
|
||||
|
||||
TEST(logging_splitters, LogdChunkSplitter_MultipleHugeLineNoNewline) {
|
||||
auto long_string_x = std::string(LOGGER_ENTRY_MAX_PAYLOAD, 'x');
|
||||
auto long_string_y = std::string(LOGGER_ENTRY_MAX_PAYLOAD, 'y');
|
||||
auto long_string_z = std::string(LOGGER_ENTRY_MAX_PAYLOAD, 'z');
|
||||
|
||||
auto long_strings = long_string_x + '\n' + long_string_y + '\n' + long_string_z;
|
||||
|
||||
std::string tag = "tag";
|
||||
std::vector expected = {ReduceToMaxSize(tag, long_string_x), ReduceToMaxSize(tag, long_string_y),
|
||||
long_string_z};
|
||||
|
||||
TestLogdChunkSplitter(tag, "", long_strings, expected);
|
||||
}
|
||||
|
||||
// With a ~4k buffer, we should print 2 long strings per logger call.
|
||||
TEST(logging_splitters, LogdChunkSplitter_Multiple2kLines) {
|
||||
std::vector expected = {
|
||||
std::string(2000, 'a') + '\n' + std::string(2000, 'b'),
|
||||
std::string(2000, 'c') + '\n' + std::string(2000, 'd'),
|
||||
std::string(2000, 'e') + '\n' + std::string(2000, 'f'),
|
||||
};
|
||||
|
||||
auto long_strings = Join(expected, '\n');
|
||||
|
||||
TestLogdChunkSplitter("tag", "", long_strings, expected);
|
||||
}
|
||||
|
||||
TEST(logging_splitters, LogdChunkSplitter_ExactSizedLines) {
|
||||
const char* tag = "tag";
|
||||
ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - strlen(tag) - 35;
|
||||
auto long_string_a = std::string(max_size, 'a');
|
||||
auto long_string_b = std::string(max_size, 'b');
|
||||
auto long_string_c = std::string(max_size, 'c');
|
||||
|
||||
auto long_strings = long_string_a + '\n' + long_string_b + '\n' + long_string_c;
|
||||
|
||||
TestLogdChunkSplitter(tag, "", long_strings,
|
||||
std::vector{long_string_a, long_string_b, long_string_c});
|
||||
}
|
||||
|
||||
TEST(logging_splitters, LogdChunkSplitter_UnderEqualOver) {
|
||||
std::string tag = "tag";
|
||||
ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - tag.size() - 35;
|
||||
|
||||
auto first_string_size = 1000;
|
||||
auto first_string = std::string(first_string_size, 'a');
|
||||
auto second_string_size = max_size - first_string_size - 1;
|
||||
auto second_string = std::string(second_string_size, 'b');
|
||||
|
||||
auto exact_string = std::string(max_size, 'c');
|
||||
|
||||
auto large_string = std::string(max_size + 50, 'd');
|
||||
|
||||
auto final_string = std::string("final string!\n\nfinal \n \n final \n");
|
||||
|
||||
std::vector expected = {first_string + '\n' + second_string, exact_string,
|
||||
ReduceToMaxSize(tag, large_string), final_string};
|
||||
|
||||
std::vector input_strings = {first_string + '\n' + second_string, exact_string, large_string,
|
||||
final_string};
|
||||
auto long_strings = Join(input_strings, '\n');
|
||||
|
||||
TestLogdChunkSplitter(tag, "", long_strings, expected);
|
||||
}
|
||||
|
||||
TEST(logging_splitters, LogdChunkSplitter_WithFile) {
|
||||
std::string tag = "tag";
|
||||
std::string file = "/path/to/myfile.cpp";
|
||||
int line = 1000;
|
||||
auto file_header = StringPrintf("%s:%d] ", file.c_str(), line);
|
||||
ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - tag.size() - 35;
|
||||
|
||||
auto first_string_size = 1000;
|
||||
auto first_string = std::string(first_string_size, 'a');
|
||||
auto second_string_size = max_size - first_string_size - 1 - 2 * file_header.size();
|
||||
auto second_string = std::string(second_string_size, 'b');
|
||||
|
||||
auto exact_string = std::string(max_size - file_header.size(), 'c');
|
||||
|
||||
auto large_string = std::string(max_size + 50, 'd');
|
||||
|
||||
auto final_string = std::string("final string!");
|
||||
|
||||
std::vector expected = {
|
||||
file_header + first_string + '\n' + file_header + second_string, file_header + exact_string,
|
||||
file_header + ReduceToMaxSize(file_header + tag, large_string), file_header + final_string};
|
||||
|
||||
std::vector input_strings = {first_string + '\n' + second_string, exact_string, large_string,
|
||||
final_string};
|
||||
auto long_strings = Join(input_strings, '\n');
|
||||
|
||||
TestLogdChunkSplitter(tag, file, long_strings, expected);
|
||||
}
|
||||
|
||||
// We set max_size based off of tag, so if it's too large, the buffer will be sized wrong.
|
||||
// We could recover from this, but it's certainly an error for someone to attempt to use a tag this
|
||||
// large, so we abort instead.
|
||||
TEST(logging_splitters, LogdChunkSplitter_TooLongTag) {
|
||||
auto long_tag = std::string(5000, 'x');
|
||||
auto logger_function = [](LogId, LogSeverity, const char*, const char*) {};
|
||||
ASSERT_DEATH(
|
||||
SplitByLogdChunks(MAIN, ERROR, long_tag.c_str(), nullptr, 0, "message", logger_function), "");
|
||||
}
|
||||
|
||||
// We do handle excessively large file names correctly however.
|
||||
TEST(logging_splitters, LogdChunkSplitter_TooLongFile) {
|
||||
auto long_file = std::string(5000, 'x');
|
||||
std::string tag = "tag";
|
||||
|
||||
std::vector expected = {ReduceToMaxSize(tag, long_file), ReduceToMaxSize(tag, long_file)};
|
||||
|
||||
TestLogdChunkSplitter(tag, long_file, "can't see me\nor me", expected);
|
||||
}
|
||||
|
||||
void TestStderrOutputGenerator(const char* tag, const char* file, int line, const char* message,
|
||||
const std::string& expected) {
|
||||
// All log messages will show "01-01 00:00:00"
|
||||
struct tm now = {
|
||||
.tm_sec = 0,
|
||||
.tm_min = 0,
|
||||
.tm_hour = 0,
|
||||
.tm_mday = 1,
|
||||
.tm_mon = 0,
|
||||
.tm_year = 1970,
|
||||
};
|
||||
|
||||
int pid = 1234; // All log messages will have 1234 for their PID.
|
||||
uint64_t tid = 4321; // All log messages will have 4321 for their TID.
|
||||
|
||||
auto result = StderrOutputGenerator(now, pid, tid, ERROR, tag, file, line, message);
|
||||
EXPECT_EQ(expected, result);
|
||||
}
|
||||
|
||||
TEST(logging_splitters, StderrOutputGenerator_Basic) {
|
||||
TestStderrOutputGenerator(nullptr, nullptr, 0, "simple message",
|
||||
"nullptr E 01-01 00:00:00 1234 4321 simple message\n");
|
||||
TestStderrOutputGenerator("tag", nullptr, 0, "simple message",
|
||||
"tag E 01-01 00:00:00 1234 4321 simple message\n");
|
||||
TestStderrOutputGenerator(
|
||||
"tag", "/path/to/some/file", 0, "simple message",
|
||||
"tag E 01-01 00:00:00 1234 4321 /path/to/some/file:0] simple message\n");
|
||||
}
|
||||
|
||||
TEST(logging_splitters, StderrOutputGenerator_NewlineTagAndFile) {
|
||||
TestStderrOutputGenerator("tag\n\n", nullptr, 0, "simple message",
|
||||
"tag\n\n E 01-01 00:00:00 1234 4321 simple message\n");
|
||||
TestStderrOutputGenerator(
|
||||
"tag", "/path/to/some/file\n\n", 0, "simple message",
|
||||
"tag E 01-01 00:00:00 1234 4321 /path/to/some/file\n\n:0] simple message\n");
|
||||
}
|
||||
|
||||
TEST(logging_splitters, StderrOutputGenerator_TrailingNewLine) {
|
||||
TestStderrOutputGenerator(
|
||||
"tag", nullptr, 0, "simple message\n",
|
||||
"tag E 01-01 00:00:00 1234 4321 simple message\ntag E 01-01 00:00:00 1234 4321 \n");
|
||||
}
|
||||
|
||||
TEST(logging_splitters, StderrOutputGenerator_MultiLine) {
|
||||
const char* expected_result =
|
||||
"tag E 01-01 00:00:00 1234 4321 simple message\n"
|
||||
"tag E 01-01 00:00:00 1234 4321 \n"
|
||||
"tag E 01-01 00:00:00 1234 4321 \n"
|
||||
"tag E 01-01 00:00:00 1234 4321 another message \n"
|
||||
"tag E 01-01 00:00:00 1234 4321 \n"
|
||||
"tag E 01-01 00:00:00 1234 4321 final message \n"
|
||||
"tag E 01-01 00:00:00 1234 4321 \n"
|
||||
"tag E 01-01 00:00:00 1234 4321 \n"
|
||||
"tag E 01-01 00:00:00 1234 4321 \n";
|
||||
|
||||
TestStderrOutputGenerator("tag", nullptr, 0,
|
||||
"simple message\n\n\nanother message \n\n final message \n\n\n",
|
||||
expected_result);
|
||||
}
|
||||
|
||||
TEST(logging_splitters, StderrOutputGenerator_MultiLineLong) {
|
||||
auto long_string_a = std::string(4000, 'a');
|
||||
auto long_string_b = std::string(4000, 'b');
|
||||
|
||||
auto message = long_string_a + '\n' + long_string_b;
|
||||
auto expected_result = "tag E 01-01 00:00:00 1234 4321 " + long_string_a + '\n' +
|
||||
"tag E 01-01 00:00:00 1234 4321 " + long_string_b + '\n';
|
||||
TestStderrOutputGenerator("tag", nullptr, 0, message.c_str(), expected_result);
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,673 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "android-base/logging.h"
|
||||
|
||||
#include <libgen.h>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <signal.h>
|
||||
#endif
|
||||
|
||||
#include <regex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include "android-base/file.h"
|
||||
#include "android-base/scopeguard.h"
|
||||
#include "android-base/stringprintf.h"
|
||||
#include "android-base/test_utils.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#define HOST_TEST(suite, name) TEST(suite, DISABLED_ ## name)
|
||||
#else
|
||||
#define HOST_TEST(suite, name) TEST(suite, name)
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
static void ExitSignalAbortHandler(int) {
|
||||
_exit(3);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void SuppressAbortUI() {
|
||||
#if defined(_WIN32)
|
||||
// We really just want to call _set_abort_behavior(0, _CALL_REPORTFAULT) to
|
||||
// suppress the Windows Error Reporting dialog box, but that API is not
|
||||
// available in the OS-supplied C Runtime, msvcrt.dll, that we currently
|
||||
// use (it is available in the Visual Studio C runtime).
|
||||
//
|
||||
// Instead, we setup a SIGABRT handler, which is called in abort() right
|
||||
// before calling Windows Error Reporting. In the handler, we exit the
|
||||
// process just like abort() does.
|
||||
ASSERT_NE(SIG_ERR, signal(SIGABRT, ExitSignalAbortHandler));
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(logging, CHECK) {
|
||||
ASSERT_DEATH({SuppressAbortUI(); CHECK(false);}, "Check failed: false ");
|
||||
CHECK(true);
|
||||
|
||||
ASSERT_DEATH({SuppressAbortUI(); CHECK_EQ(0, 1);}, "Check failed: 0 == 1 ");
|
||||
CHECK_EQ(0, 0);
|
||||
|
||||
ASSERT_DEATH({SuppressAbortUI(); CHECK_STREQ("foo", "bar");},
|
||||
R"(Check failed: "foo" == "bar")");
|
||||
CHECK_STREQ("foo", "foo");
|
||||
|
||||
// Test whether CHECK() and CHECK_STREQ() have a dangling if with no else.
|
||||
bool flag = false;
|
||||
if (true)
|
||||
CHECK(true);
|
||||
else
|
||||
flag = true;
|
||||
EXPECT_FALSE(flag) << "CHECK macro probably has a dangling if with no else";
|
||||
|
||||
flag = false;
|
||||
if (true)
|
||||
CHECK_STREQ("foo", "foo");
|
||||
else
|
||||
flag = true;
|
||||
EXPECT_FALSE(flag) << "CHECK_STREQ probably has a dangling if with no else";
|
||||
}
|
||||
|
||||
TEST(logging, DCHECK) {
|
||||
if (android::base::kEnableDChecks) {
|
||||
ASSERT_DEATH({SuppressAbortUI(); DCHECK(false);}, "DCheck failed: false ");
|
||||
}
|
||||
DCHECK(true);
|
||||
|
||||
if (android::base::kEnableDChecks) {
|
||||
ASSERT_DEATH({SuppressAbortUI(); DCHECK_EQ(0, 1);}, "DCheck failed: 0 == 1 ");
|
||||
}
|
||||
DCHECK_EQ(0, 0);
|
||||
|
||||
if (android::base::kEnableDChecks) {
|
||||
ASSERT_DEATH({SuppressAbortUI(); DCHECK_STREQ("foo", "bar");},
|
||||
R"(DCheck failed: "foo" == "bar")");
|
||||
}
|
||||
DCHECK_STREQ("foo", "foo");
|
||||
|
||||
// No testing whether we have a dangling else, possibly. That's inherent to the if (constexpr)
|
||||
// setup we intentionally chose to force type-checks of debug code even in release builds (so
|
||||
// we don't get more bit-rot).
|
||||
}
|
||||
|
||||
|
||||
#define CHECK_WOULD_LOG_DISABLED(severity) \
|
||||
static_assert(android::base::severity < android::base::FATAL, "Bad input"); \
|
||||
for (size_t i = static_cast<size_t>(android::base::severity) + 1; \
|
||||
i <= static_cast<size_t>(android::base::FATAL); \
|
||||
++i) { \
|
||||
{ \
|
||||
android::base::ScopedLogSeverity sls2(static_cast<android::base::LogSeverity>(i)); \
|
||||
EXPECT_FALSE(WOULD_LOG(severity)) << i; \
|
||||
} \
|
||||
{ \
|
||||
android::base::ScopedLogSeverity sls2(static_cast<android::base::LogSeverity>(i)); \
|
||||
EXPECT_FALSE(WOULD_LOG(::android::base::severity)) << i; \
|
||||
} \
|
||||
} \
|
||||
|
||||
#define CHECK_WOULD_LOG_ENABLED(severity) \
|
||||
for (size_t i = static_cast<size_t>(android::base::VERBOSE); \
|
||||
i <= static_cast<size_t>(android::base::severity); \
|
||||
++i) { \
|
||||
{ \
|
||||
android::base::ScopedLogSeverity sls2(static_cast<android::base::LogSeverity>(i)); \
|
||||
EXPECT_TRUE(WOULD_LOG(severity)) << i; \
|
||||
} \
|
||||
{ \
|
||||
android::base::ScopedLogSeverity sls2(static_cast<android::base::LogSeverity>(i)); \
|
||||
EXPECT_TRUE(WOULD_LOG(::android::base::severity)) << i; \
|
||||
} \
|
||||
} \
|
||||
|
||||
TEST(logging, WOULD_LOG_FATAL) {
|
||||
CHECK_WOULD_LOG_ENABLED(FATAL);
|
||||
}
|
||||
|
||||
TEST(logging, WOULD_LOG_FATAL_WITHOUT_ABORT_enabled) {
|
||||
CHECK_WOULD_LOG_ENABLED(FATAL_WITHOUT_ABORT);
|
||||
}
|
||||
|
||||
TEST(logging, WOULD_LOG_ERROR_disabled) {
|
||||
CHECK_WOULD_LOG_DISABLED(ERROR);
|
||||
}
|
||||
|
||||
TEST(logging, WOULD_LOG_ERROR_enabled) {
|
||||
CHECK_WOULD_LOG_ENABLED(ERROR);
|
||||
}
|
||||
|
||||
TEST(logging, WOULD_LOG_WARNING_disabled) {
|
||||
CHECK_WOULD_LOG_DISABLED(WARNING);
|
||||
}
|
||||
|
||||
TEST(logging, WOULD_LOG_WARNING_enabled) {
|
||||
CHECK_WOULD_LOG_ENABLED(WARNING);
|
||||
}
|
||||
|
||||
TEST(logging, WOULD_LOG_INFO_disabled) {
|
||||
CHECK_WOULD_LOG_DISABLED(INFO);
|
||||
}
|
||||
|
||||
TEST(logging, WOULD_LOG_INFO_enabled) {
|
||||
CHECK_WOULD_LOG_ENABLED(INFO);
|
||||
}
|
||||
|
||||
TEST(logging, WOULD_LOG_DEBUG_disabled) {
|
||||
CHECK_WOULD_LOG_DISABLED(DEBUG);
|
||||
}
|
||||
|
||||
TEST(logging, WOULD_LOG_DEBUG_enabled) {
|
||||
CHECK_WOULD_LOG_ENABLED(DEBUG);
|
||||
}
|
||||
|
||||
TEST(logging, WOULD_LOG_VERBOSE_disabled) {
|
||||
CHECK_WOULD_LOG_DISABLED(VERBOSE);
|
||||
}
|
||||
|
||||
TEST(logging, WOULD_LOG_VERBOSE_enabled) {
|
||||
CHECK_WOULD_LOG_ENABLED(VERBOSE);
|
||||
}
|
||||
|
||||
#undef CHECK_WOULD_LOG_DISABLED
|
||||
#undef CHECK_WOULD_LOG_ENABLED
|
||||
|
||||
|
||||
#if !defined(_WIN32)
|
||||
static std::string make_log_pattern(android::base::LogSeverity severity,
|
||||
const char* message) {
|
||||
static const char log_characters[] = "VDIWEFF";
|
||||
static_assert(arraysize(log_characters) - 1 == android::base::FATAL + 1,
|
||||
"Mismatch in size of log_characters and values in LogSeverity");
|
||||
char log_char = log_characters[severity];
|
||||
std::string holder(__FILE__);
|
||||
return android::base::StringPrintf(
|
||||
"%c \\d+-\\d+ \\d+:\\d+:\\d+ \\s*\\d+ \\s*\\d+ %s:\\d+] %s",
|
||||
log_char, basename(&holder[0]), message);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void CheckMessage(const std::string& output, android::base::LogSeverity severity,
|
||||
const char* expected, const char* expected_tag = nullptr) {
|
||||
// We can't usefully check the output of any of these on Windows because we
|
||||
// don't have std::regex, but we can at least make sure we printed at least as
|
||||
// many characters are in the log message.
|
||||
ASSERT_GT(output.length(), strlen(expected));
|
||||
ASSERT_NE(nullptr, strstr(output.c_str(), expected)) << output;
|
||||
if (expected_tag != nullptr) {
|
||||
ASSERT_NE(nullptr, strstr(output.c_str(), expected_tag)) << output;
|
||||
}
|
||||
|
||||
#if !defined(_WIN32)
|
||||
std::string regex_str;
|
||||
if (expected_tag != nullptr) {
|
||||
regex_str.append(expected_tag);
|
||||
regex_str.append(" ");
|
||||
}
|
||||
regex_str.append(make_log_pattern(severity, expected));
|
||||
std::regex message_regex(regex_str);
|
||||
ASSERT_TRUE(std::regex_search(output, message_regex)) << output;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void CheckMessage(CapturedStderr& cap, android::base::LogSeverity severity,
|
||||
const char* expected, const char* expected_tag = nullptr) {
|
||||
cap.Stop();
|
||||
std::string output = cap.str();
|
||||
return CheckMessage(output, severity, expected, expected_tag);
|
||||
}
|
||||
|
||||
#define CHECK_LOG_STREAM_DISABLED(severity) \
|
||||
{ \
|
||||
android::base::ScopedLogSeverity sls1(android::base::FATAL); \
|
||||
CapturedStderr cap1; \
|
||||
LOG_STREAM(severity) << "foo bar"; \
|
||||
cap1.Stop(); \
|
||||
ASSERT_EQ("", cap1.str()); \
|
||||
} \
|
||||
{ \
|
||||
android::base::ScopedLogSeverity sls1(android::base::FATAL); \
|
||||
CapturedStderr cap1; \
|
||||
LOG_STREAM(::android::base::severity) << "foo bar"; \
|
||||
cap1.Stop(); \
|
||||
ASSERT_EQ("", cap1.str()); \
|
||||
}
|
||||
|
||||
#define CHECK_LOG_STREAM_ENABLED(severity) \
|
||||
{ \
|
||||
android::base::ScopedLogSeverity sls2(android::base::severity); \
|
||||
CapturedStderr cap2; \
|
||||
LOG_STREAM(severity) << "foobar"; \
|
||||
CheckMessage(cap2, android::base::severity, "foobar"); \
|
||||
} \
|
||||
{ \
|
||||
android::base::ScopedLogSeverity sls2(android::base::severity); \
|
||||
CapturedStderr cap2; \
|
||||
LOG_STREAM(::android::base::severity) << "foobar"; \
|
||||
CheckMessage(cap2, android::base::severity, "foobar"); \
|
||||
} \
|
||||
|
||||
TEST(logging, LOG_STREAM_FATAL_WITHOUT_ABORT_enabled) {
|
||||
ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(FATAL_WITHOUT_ABORT));
|
||||
}
|
||||
|
||||
TEST(logging, LOG_STREAM_ERROR_disabled) {
|
||||
CHECK_LOG_STREAM_DISABLED(ERROR);
|
||||
}
|
||||
|
||||
TEST(logging, LOG_STREAM_ERROR_enabled) {
|
||||
ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(ERROR));
|
||||
}
|
||||
|
||||
TEST(logging, LOG_STREAM_WARNING_disabled) {
|
||||
CHECK_LOG_STREAM_DISABLED(WARNING);
|
||||
}
|
||||
|
||||
TEST(logging, LOG_STREAM_WARNING_enabled) {
|
||||
ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(WARNING));
|
||||
}
|
||||
|
||||
TEST(logging, LOG_STREAM_INFO_disabled) {
|
||||
CHECK_LOG_STREAM_DISABLED(INFO);
|
||||
}
|
||||
|
||||
TEST(logging, LOG_STREAM_INFO_enabled) {
|
||||
ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(INFO));
|
||||
}
|
||||
|
||||
TEST(logging, LOG_STREAM_DEBUG_disabled) {
|
||||
CHECK_LOG_STREAM_DISABLED(DEBUG);
|
||||
}
|
||||
|
||||
TEST(logging, LOG_STREAM_DEBUG_enabled) {
|
||||
ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(DEBUG));
|
||||
}
|
||||
|
||||
TEST(logging, LOG_STREAM_VERBOSE_disabled) {
|
||||
CHECK_LOG_STREAM_DISABLED(VERBOSE);
|
||||
}
|
||||
|
||||
TEST(logging, LOG_STREAM_VERBOSE_enabled) {
|
||||
ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(VERBOSE));
|
||||
}
|
||||
|
||||
#undef CHECK_LOG_STREAM_DISABLED
|
||||
#undef CHECK_LOG_STREAM_ENABLED
|
||||
|
||||
#define CHECK_LOG_DISABLED(severity) \
|
||||
{ \
|
||||
android::base::ScopedLogSeverity sls1(android::base::FATAL); \
|
||||
CapturedStderr cap1; \
|
||||
LOG(severity) << "foo bar"; \
|
||||
cap1.Stop(); \
|
||||
ASSERT_EQ("", cap1.str()); \
|
||||
} \
|
||||
{ \
|
||||
android::base::ScopedLogSeverity sls1(android::base::FATAL); \
|
||||
CapturedStderr cap1; \
|
||||
LOG(::android::base::severity) << "foo bar"; \
|
||||
cap1.Stop(); \
|
||||
ASSERT_EQ("", cap1.str()); \
|
||||
}
|
||||
|
||||
#define CHECK_LOG_ENABLED(severity) \
|
||||
{ \
|
||||
android::base::ScopedLogSeverity sls2(android::base::severity); \
|
||||
CapturedStderr cap2; \
|
||||
LOG(severity) << "foobar"; \
|
||||
CheckMessage(cap2, android::base::severity, "foobar"); \
|
||||
} \
|
||||
{ \
|
||||
android::base::ScopedLogSeverity sls2(android::base::severity); \
|
||||
CapturedStderr cap2; \
|
||||
LOG(::android::base::severity) << "foobar"; \
|
||||
CheckMessage(cap2, android::base::severity, "foobar"); \
|
||||
} \
|
||||
|
||||
TEST(logging, LOG_FATAL) {
|
||||
ASSERT_DEATH({SuppressAbortUI(); LOG(FATAL) << "foobar";}, "foobar");
|
||||
ASSERT_DEATH({SuppressAbortUI(); LOG(::android::base::FATAL) << "foobar";}, "foobar");
|
||||
}
|
||||
|
||||
TEST(logging, LOG_FATAL_WITHOUT_ABORT_enabled) {
|
||||
ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(FATAL_WITHOUT_ABORT));
|
||||
}
|
||||
|
||||
TEST(logging, LOG_ERROR_disabled) {
|
||||
CHECK_LOG_DISABLED(ERROR);
|
||||
}
|
||||
|
||||
TEST(logging, LOG_ERROR_enabled) {
|
||||
ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(ERROR));
|
||||
}
|
||||
|
||||
TEST(logging, LOG_WARNING_disabled) {
|
||||
CHECK_LOG_DISABLED(WARNING);
|
||||
}
|
||||
|
||||
TEST(logging, LOG_WARNING_enabled) {
|
||||
ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(WARNING));
|
||||
}
|
||||
|
||||
TEST(logging, LOG_INFO_disabled) {
|
||||
CHECK_LOG_DISABLED(INFO);
|
||||
}
|
||||
|
||||
TEST(logging, LOG_INFO_enabled) {
|
||||
ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(INFO));
|
||||
}
|
||||
|
||||
TEST(logging, LOG_DEBUG_disabled) {
|
||||
CHECK_LOG_DISABLED(DEBUG);
|
||||
}
|
||||
|
||||
TEST(logging, LOG_DEBUG_enabled) {
|
||||
ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(DEBUG));
|
||||
}
|
||||
|
||||
TEST(logging, LOG_VERBOSE_disabled) {
|
||||
CHECK_LOG_DISABLED(VERBOSE);
|
||||
}
|
||||
|
||||
TEST(logging, LOG_VERBOSE_enabled) {
|
||||
ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(VERBOSE));
|
||||
}
|
||||
|
||||
#undef CHECK_LOG_DISABLED
|
||||
#undef CHECK_LOG_ENABLED
|
||||
|
||||
TEST(logging, LOG_complex_param) {
|
||||
#define CHECK_LOG_COMBINATION(use_scoped_log_severity_info, use_logging_severity_info) \
|
||||
{ \
|
||||
android::base::ScopedLogSeverity sls( \
|
||||
(use_scoped_log_severity_info) ? ::android::base::INFO : ::android::base::WARNING); \
|
||||
CapturedStderr cap; \
|
||||
LOG((use_logging_severity_info) ? ::android::base::INFO : ::android::base::WARNING) \
|
||||
<< "foobar"; \
|
||||
if ((use_scoped_log_severity_info) || !(use_logging_severity_info)) { \
|
||||
ASSERT_NO_FATAL_FAILURE(CheckMessage( \
|
||||
cap, (use_logging_severity_info) ? ::android::base::INFO : ::android::base::WARNING, \
|
||||
"foobar")); \
|
||||
} else { \
|
||||
cap.Stop(); \
|
||||
ASSERT_EQ("", cap.str()); \
|
||||
} \
|
||||
}
|
||||
|
||||
CHECK_LOG_COMBINATION(false,false);
|
||||
CHECK_LOG_COMBINATION(false,true);
|
||||
CHECK_LOG_COMBINATION(true,false);
|
||||
CHECK_LOG_COMBINATION(true,true);
|
||||
|
||||
#undef CHECK_LOG_COMBINATION
|
||||
}
|
||||
|
||||
|
||||
TEST(logging, LOG_does_not_clobber_errno) {
|
||||
CapturedStderr cap;
|
||||
errno = 12345;
|
||||
LOG(INFO) << (errno = 67890);
|
||||
EXPECT_EQ(12345, errno) << "errno was not restored";
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(CheckMessage(cap, android::base::INFO, "67890"));
|
||||
}
|
||||
|
||||
TEST(logging, PLOG_does_not_clobber_errno) {
|
||||
CapturedStderr cap;
|
||||
errno = 12345;
|
||||
PLOG(INFO) << (errno = 67890);
|
||||
EXPECT_EQ(12345, errno) << "errno was not restored";
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(CheckMessage(cap, android::base::INFO, "67890"));
|
||||
}
|
||||
|
||||
TEST(logging, LOG_does_not_have_dangling_if) {
|
||||
CapturedStderr cap; // So the logging below has no side-effects.
|
||||
|
||||
// Do the test two ways: once where we hypothesize that LOG()'s if
|
||||
// will evaluate to true (when severity is high enough) and once when we
|
||||
// expect it to evaluate to false (when severity is not high enough).
|
||||
bool flag = false;
|
||||
if (true)
|
||||
LOG(INFO) << "foobar";
|
||||
else
|
||||
flag = true;
|
||||
|
||||
EXPECT_FALSE(flag) << "LOG macro probably has a dangling if with no else";
|
||||
|
||||
flag = false;
|
||||
if (true)
|
||||
LOG(VERBOSE) << "foobar";
|
||||
else
|
||||
flag = true;
|
||||
|
||||
EXPECT_FALSE(flag) << "LOG macro probably has a dangling if with no else";
|
||||
}
|
||||
|
||||
#define CHECK_PLOG_DISABLED(severity) \
|
||||
{ \
|
||||
android::base::ScopedLogSeverity sls1(android::base::FATAL); \
|
||||
CapturedStderr cap1; \
|
||||
PLOG(severity) << "foo bar"; \
|
||||
cap1.Stop(); \
|
||||
ASSERT_EQ("", cap1.str()); \
|
||||
} \
|
||||
{ \
|
||||
android::base::ScopedLogSeverity sls1(android::base::FATAL); \
|
||||
CapturedStderr cap1; \
|
||||
PLOG(severity) << "foo bar"; \
|
||||
cap1.Stop(); \
|
||||
ASSERT_EQ("", cap1.str()); \
|
||||
}
|
||||
|
||||
#define CHECK_PLOG_ENABLED(severity) \
|
||||
{ \
|
||||
android::base::ScopedLogSeverity sls2(android::base::severity); \
|
||||
CapturedStderr cap2; \
|
||||
errno = ENOENT; \
|
||||
PLOG(severity) << "foobar"; \
|
||||
CheckMessage(cap2, android::base::severity, "foobar: No such file or directory"); \
|
||||
} \
|
||||
{ \
|
||||
android::base::ScopedLogSeverity sls2(android::base::severity); \
|
||||
CapturedStderr cap2; \
|
||||
errno = ENOENT; \
|
||||
PLOG(severity) << "foobar"; \
|
||||
CheckMessage(cap2, android::base::severity, "foobar: No such file or directory"); \
|
||||
} \
|
||||
|
||||
TEST(logging, PLOG_FATAL) {
|
||||
ASSERT_DEATH({SuppressAbortUI(); PLOG(FATAL) << "foobar";}, "foobar");
|
||||
ASSERT_DEATH({SuppressAbortUI(); PLOG(::android::base::FATAL) << "foobar";}, "foobar");
|
||||
}
|
||||
|
||||
TEST(logging, PLOG_FATAL_WITHOUT_ABORT_enabled) {
|
||||
ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(FATAL_WITHOUT_ABORT));
|
||||
}
|
||||
|
||||
TEST(logging, PLOG_ERROR_disabled) {
|
||||
CHECK_PLOG_DISABLED(ERROR);
|
||||
}
|
||||
|
||||
TEST(logging, PLOG_ERROR_enabled) {
|
||||
ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(ERROR));
|
||||
}
|
||||
|
||||
TEST(logging, PLOG_WARNING_disabled) {
|
||||
CHECK_PLOG_DISABLED(WARNING);
|
||||
}
|
||||
|
||||
TEST(logging, PLOG_WARNING_enabled) {
|
||||
ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(WARNING));
|
||||
}
|
||||
|
||||
TEST(logging, PLOG_INFO_disabled) {
|
||||
CHECK_PLOG_DISABLED(INFO);
|
||||
}
|
||||
|
||||
TEST(logging, PLOG_INFO_enabled) {
|
||||
ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(INFO));
|
||||
}
|
||||
|
||||
TEST(logging, PLOG_DEBUG_disabled) {
|
||||
CHECK_PLOG_DISABLED(DEBUG);
|
||||
}
|
||||
|
||||
TEST(logging, PLOG_DEBUG_enabled) {
|
||||
ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(DEBUG));
|
||||
}
|
||||
|
||||
TEST(logging, PLOG_VERBOSE_disabled) {
|
||||
CHECK_PLOG_DISABLED(VERBOSE);
|
||||
}
|
||||
|
||||
TEST(logging, PLOG_VERBOSE_enabled) {
|
||||
ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(VERBOSE));
|
||||
}
|
||||
|
||||
#undef CHECK_PLOG_DISABLED
|
||||
#undef CHECK_PLOG_ENABLED
|
||||
|
||||
|
||||
TEST(logging, UNIMPLEMENTED) {
|
||||
std::string expected = android::base::StringPrintf("%s unimplemented ", __PRETTY_FUNCTION__);
|
||||
|
||||
CapturedStderr cap;
|
||||
errno = ENOENT;
|
||||
UNIMPLEMENTED(ERROR);
|
||||
ASSERT_NO_FATAL_FAILURE(CheckMessage(cap, android::base::ERROR, expected.c_str()));
|
||||
}
|
||||
|
||||
static void NoopAborter(const char* msg ATTRIBUTE_UNUSED) {
|
||||
LOG(ERROR) << "called noop";
|
||||
}
|
||||
|
||||
TEST(logging, LOG_FATAL_NOOP_ABORTER) {
|
||||
CapturedStderr cap;
|
||||
{
|
||||
android::base::SetAborter(NoopAborter);
|
||||
|
||||
android::base::ScopedLogSeverity sls(android::base::ERROR);
|
||||
LOG(FATAL) << "foobar";
|
||||
cap.Stop();
|
||||
|
||||
android::base::SetAborter(android::base::DefaultAborter);
|
||||
}
|
||||
std::string output = cap.str();
|
||||
ASSERT_NO_FATAL_FAILURE(CheckMessage(output, android::base::FATAL, "foobar"));
|
||||
ASSERT_NO_FATAL_FAILURE(CheckMessage(output, android::base::ERROR, "called noop"));
|
||||
|
||||
ASSERT_DEATH({SuppressAbortUI(); LOG(FATAL) << "foobar";}, "foobar");
|
||||
}
|
||||
|
||||
struct CountLineAborter {
|
||||
static void CountLineAborterFunction(const char* msg) {
|
||||
while (*msg != 0) {
|
||||
if (*msg == '\n') {
|
||||
newline_count++;
|
||||
}
|
||||
msg++;
|
||||
}
|
||||
}
|
||||
static size_t newline_count;
|
||||
};
|
||||
size_t CountLineAborter::newline_count = 0;
|
||||
|
||||
TEST(logging, LOG_FATAL_ABORTER_MESSAGE) {
|
||||
CountLineAborter::newline_count = 0;
|
||||
android::base::SetAborter(CountLineAborter::CountLineAborterFunction);
|
||||
|
||||
android::base::ScopedLogSeverity sls(android::base::ERROR);
|
||||
CapturedStderr cap;
|
||||
LOG(FATAL) << "foo\nbar";
|
||||
|
||||
EXPECT_EQ(CountLineAborter::newline_count, 1U);
|
||||
}
|
||||
|
||||
__attribute__((constructor)) void TestLoggingInConstructor() {
|
||||
LOG(ERROR) << "foobar";
|
||||
}
|
||||
|
||||
TEST(logging, StdioLogger) {
|
||||
CapturedStderr cap_err;
|
||||
CapturedStdout cap_out;
|
||||
android::base::SetLogger(android::base::StdioLogger);
|
||||
LOG(INFO) << "out";
|
||||
LOG(ERROR) << "err";
|
||||
cap_err.Stop();
|
||||
cap_out.Stop();
|
||||
|
||||
// For INFO we expect just the literal "out\n".
|
||||
ASSERT_EQ("out\n", cap_out.str());
|
||||
// Whereas ERROR logging includes the program name.
|
||||
ASSERT_EQ(android::base::Basename(android::base::GetExecutablePath()) + ": err\n", cap_err.str());
|
||||
}
|
||||
|
||||
TEST(logging, ForkSafe) {
|
||||
#if !defined(_WIN32)
|
||||
using namespace android::base;
|
||||
SetLogger(
|
||||
[&](LogId, LogSeverity, const char*, const char*, unsigned int, const char*) { sleep(3); });
|
||||
|
||||
auto guard = make_scope_guard([&] {
|
||||
#ifdef __ANDROID__
|
||||
SetLogger(LogdLogger());
|
||||
#else
|
||||
SetLogger(StderrLogger);
|
||||
#endif
|
||||
});
|
||||
|
||||
auto thread = std::thread([] {
|
||||
LOG(ERROR) << "This should sleep for 3 seconds, long enough to fork another process, if there "
|
||||
"is no intervention";
|
||||
});
|
||||
thread.detach();
|
||||
|
||||
auto pid = fork();
|
||||
ASSERT_NE(-1, pid);
|
||||
|
||||
if (pid == 0) {
|
||||
// Reset the logger, so the next message doesn't sleep().
|
||||
SetLogger([](LogId, LogSeverity, const char*, const char*, unsigned int, const char*) {});
|
||||
LOG(ERROR) << "This should succeed in the child, only if libbase is forksafe.";
|
||||
_exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
// Wait for up to 3 seconds for the child to exit.
|
||||
int tries = 3;
|
||||
bool found_child = false;
|
||||
while (tries-- > 0) {
|
||||
auto result = waitpid(pid, nullptr, WNOHANG);
|
||||
EXPECT_NE(-1, result);
|
||||
if (result == pid) {
|
||||
found_child = true;
|
||||
break;
|
||||
}
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
EXPECT_TRUE(found_child);
|
||||
|
||||
// Kill the child if it did not exit.
|
||||
if (!found_child) {
|
||||
kill(pid, SIGKILL);
|
||||
}
|
||||
#endif
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "android-base/macros.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
TEST(macros, SIZEOF_MEMBER_macro) {
|
||||
struct S {
|
||||
int32_t i32;
|
||||
double d;
|
||||
};
|
||||
ASSERT_EQ(4U, SIZEOF_MEMBER(S, i32));
|
||||
ASSERT_EQ(8U, SIZEOF_MEMBER(S, d));
|
||||
}
|
|
@ -1,128 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "android-base/mapped_file.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
static constexpr char kEmptyBuffer[] = {'0'};
|
||||
|
||||
static off64_t InitPageSize() {
|
||||
#if defined(_WIN32)
|
||||
SYSTEM_INFO si;
|
||||
GetSystemInfo(&si);
|
||||
return si.dwAllocationGranularity;
|
||||
#else
|
||||
return sysconf(_SC_PAGE_SIZE);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::unique_ptr<MappedFile> MappedFile::FromFd(borrowed_fd fd, off64_t offset, size_t length,
|
||||
int prot) {
|
||||
#if defined(_WIN32)
|
||||
return FromOsHandle(reinterpret_cast<HANDLE>(_get_osfhandle(fd.get())), offset, length, prot);
|
||||
#else
|
||||
return FromOsHandle(fd.get(), offset, length, prot);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::unique_ptr<MappedFile> MappedFile::FromOsHandle(os_handle h, off64_t offset, size_t length,
|
||||
int prot) {
|
||||
static const off64_t page_size = InitPageSize();
|
||||
size_t slop = offset % page_size;
|
||||
off64_t file_offset = offset - slop;
|
||||
off64_t file_length = length + slop;
|
||||
|
||||
#if defined(_WIN32)
|
||||
HANDLE handle = CreateFileMappingW(
|
||||
h, nullptr, (prot & PROT_WRITE) ? PAGE_READWRITE : PAGE_READONLY, 0, 0, nullptr);
|
||||
if (handle == nullptr) {
|
||||
// http://b/119818070 "app crashes when reading asset of zero length".
|
||||
// Return a MappedFile that's only valid for reading the size.
|
||||
if (length == 0 && ::GetLastError() == ERROR_FILE_INVALID) {
|
||||
return std::unique_ptr<MappedFile>(
|
||||
new MappedFile(const_cast<char*>(kEmptyBuffer), 0, 0, nullptr));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
void* base = MapViewOfFile(handle, (prot & PROT_WRITE) ? FILE_MAP_ALL_ACCESS : FILE_MAP_READ, 0,
|
||||
file_offset, file_length);
|
||||
if (base == nullptr) {
|
||||
CloseHandle(handle);
|
||||
return nullptr;
|
||||
}
|
||||
return std::unique_ptr<MappedFile>(
|
||||
new MappedFile(static_cast<char*>(base), length, slop, handle));
|
||||
#else
|
||||
void* base = mmap(nullptr, file_length, prot, MAP_SHARED, h, file_offset);
|
||||
if (base == MAP_FAILED) {
|
||||
// http://b/119818070 "app crashes when reading asset of zero length".
|
||||
// mmap fails with EINVAL for a zero length region.
|
||||
if (errno == EINVAL && length == 0) {
|
||||
return std::unique_ptr<MappedFile>(new MappedFile(const_cast<char*>(kEmptyBuffer), 0, 0));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
return std::unique_ptr<MappedFile>(new MappedFile(static_cast<char*>(base), length, slop));
|
||||
#endif
|
||||
}
|
||||
|
||||
MappedFile::MappedFile(MappedFile&& other)
|
||||
: base_(std::exchange(other.base_, nullptr)),
|
||||
size_(std::exchange(other.size_, 0)),
|
||||
offset_(std::exchange(other.offset_, 0))
|
||||
#ifdef _WIN32
|
||||
,
|
||||
handle_(std::exchange(other.handle_, nullptr))
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
MappedFile& MappedFile::operator=(MappedFile&& other) {
|
||||
Close();
|
||||
base_ = std::exchange(other.base_, nullptr);
|
||||
size_ = std::exchange(other.size_, 0);
|
||||
offset_ = std::exchange(other.offset_, 0);
|
||||
#ifdef _WIN32
|
||||
handle_ = std::exchange(other.handle_, nullptr);
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
MappedFile::~MappedFile() {
|
||||
Close();
|
||||
}
|
||||
|
||||
void MappedFile::Close() {
|
||||
#if defined(_WIN32)
|
||||
if (base_ != nullptr && size_ != 0) UnmapViewOfFile(base_);
|
||||
if (handle_ != nullptr) CloseHandle(handle_);
|
||||
handle_ = nullptr;
|
||||
#else
|
||||
if (base_ != nullptr && size_ != 0) munmap(base_, size_ + offset_);
|
||||
#endif
|
||||
|
||||
base_ = nullptr;
|
||||
offset_ = size_ = 0;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "android-base/mapped_file.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "android-base/file.h"
|
||||
|
||||
TEST(mapped_file, smoke) {
|
||||
TemporaryFile tf;
|
||||
ASSERT_TRUE(tf.fd != -1);
|
||||
ASSERT_TRUE(android::base::WriteStringToFd("hello world", tf.fd));
|
||||
|
||||
auto m = android::base::MappedFile::FromFd(tf.fd, 3, 2, PROT_READ);
|
||||
ASSERT_EQ(2u, m->size());
|
||||
ASSERT_EQ('l', m->data()[0]);
|
||||
ASSERT_EQ('o', m->data()[1]);
|
||||
}
|
||||
|
||||
TEST(mapped_file, zero_length_mapping) {
|
||||
// http://b/119818070 "app crashes when reading asset of zero length".
|
||||
// mmap fails with EINVAL for a zero length region.
|
||||
TemporaryFile tf;
|
||||
ASSERT_TRUE(tf.fd != -1);
|
||||
|
||||
auto m = android::base::MappedFile::FromFd(tf.fd, 4096, 0, PROT_READ);
|
||||
EXPECT_EQ(0u, m->size());
|
||||
EXPECT_NE(nullptr, m->data());
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "android-base/no_destructor.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
struct __attribute__((packed)) Bomb {
|
||||
Bomb() : magic_(123) {}
|
||||
|
||||
~Bomb() { exit(42); }
|
||||
|
||||
int get() const { return magic_; }
|
||||
|
||||
private:
|
||||
[[maybe_unused]] char padding_;
|
||||
int magic_;
|
||||
};
|
||||
|
||||
TEST(no_destructor, bomb) {
|
||||
ASSERT_EXIT(({
|
||||
{
|
||||
Bomb b;
|
||||
if (b.get() != 123) exit(1);
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}),
|
||||
::testing::ExitedWithCode(42), "");
|
||||
}
|
||||
|
||||
TEST(no_destructor, defused) {
|
||||
ASSERT_EXIT(({
|
||||
{
|
||||
android::base::NoDestructor<Bomb> b;
|
||||
if (b->get() != 123) exit(1);
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}),
|
||||
::testing::ExitedWithCode(0), "");
|
||||
}
|
||||
|
||||
TEST(no_destructor, operators) {
|
||||
android::base::NoDestructor<Bomb> b;
|
||||
const android::base::NoDestructor<Bomb>& c = b;
|
||||
ASSERT_EQ(123, b.get()->get());
|
||||
ASSERT_EQ(123, b->get());
|
||||
ASSERT_EQ(123, (*b).get());
|
||||
ASSERT_EQ(123, c.get()->get());
|
||||
ASSERT_EQ(123, c->get());
|
||||
ASSERT_EQ(123, (*c).get());
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "android-base/parsebool.h"
|
||||
#include <errno.h>
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
ParseBoolResult ParseBool(std::string_view s) {
|
||||
if (s == "1" || s == "y" || s == "yes" || s == "on" || s == "true") {
|
||||
return ParseBoolResult::kTrue;
|
||||
}
|
||||
if (s == "0" || s == "n" || s == "no" || s == "off" || s == "false") {
|
||||
return ParseBoolResult::kFalse;
|
||||
}
|
||||
return ParseBoolResult::kError;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,48 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "android-base/parsebool.h"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <string_view>
|
||||
|
||||
using android::base::ParseBool;
|
||||
using android::base::ParseBoolResult;
|
||||
|
||||
TEST(parsebool, true_) {
|
||||
static const char* yes[] = {
|
||||
"1", "on", "true", "y", "yes",
|
||||
};
|
||||
for (const char* s : yes) {
|
||||
ASSERT_EQ(ParseBoolResult::kTrue, ParseBool(s));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(parsebool, false_) {
|
||||
static const char* no[] = {
|
||||
"0", "false", "n", "no", "off",
|
||||
};
|
||||
for (const char* s : no) {
|
||||
ASSERT_EQ(ParseBoolResult::kFalse, ParseBool(s));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(parsebool, invalid) {
|
||||
ASSERT_EQ(ParseBoolResult::kError, ParseBool("blarg"));
|
||||
ASSERT_EQ(ParseBoolResult::kError, ParseBool(""));
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "android-base/parsedouble.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
TEST(parsedouble, double_smoke) {
|
||||
double d;
|
||||
ASSERT_FALSE(android::base::ParseDouble("", &d));
|
||||
ASSERT_FALSE(android::base::ParseDouble("x", &d));
|
||||
ASSERT_FALSE(android::base::ParseDouble("123.4x", &d));
|
||||
|
||||
ASSERT_TRUE(android::base::ParseDouble("123.4", &d));
|
||||
ASSERT_DOUBLE_EQ(123.4, d);
|
||||
ASSERT_TRUE(android::base::ParseDouble("-123.4", &d));
|
||||
ASSERT_DOUBLE_EQ(-123.4, d);
|
||||
|
||||
ASSERT_TRUE(android::base::ParseDouble("0", &d, 0.0));
|
||||
ASSERT_DOUBLE_EQ(0.0, d);
|
||||
ASSERT_FALSE(android::base::ParseDouble("0", &d, 1e-9));
|
||||
ASSERT_FALSE(android::base::ParseDouble("3.0", &d, -1.0, 2.0));
|
||||
ASSERT_TRUE(android::base::ParseDouble("1.0", &d, 0.0, 2.0));
|
||||
ASSERT_DOUBLE_EQ(1.0, d);
|
||||
|
||||
ASSERT_FALSE(android::base::ParseDouble("123.4x", nullptr));
|
||||
ASSERT_TRUE(android::base::ParseDouble("-123.4", nullptr));
|
||||
ASSERT_FALSE(android::base::ParseDouble("3.0", nullptr, -1.0, 2.0));
|
||||
ASSERT_TRUE(android::base::ParseDouble("1.0", nullptr, 0.0, 2.0));
|
||||
}
|
||||
|
||||
TEST(parsedouble, float_smoke) {
|
||||
float f;
|
||||
ASSERT_FALSE(android::base::ParseFloat("", &f));
|
||||
ASSERT_FALSE(android::base::ParseFloat("x", &f));
|
||||
ASSERT_FALSE(android::base::ParseFloat("123.4x", &f));
|
||||
|
||||
ASSERT_TRUE(android::base::ParseFloat("123.4", &f));
|
||||
ASSERT_FLOAT_EQ(123.4, f);
|
||||
ASSERT_TRUE(android::base::ParseFloat("-123.4", &f));
|
||||
ASSERT_FLOAT_EQ(-123.4, f);
|
||||
|
||||
ASSERT_TRUE(android::base::ParseFloat("0", &f, 0.0));
|
||||
ASSERT_FLOAT_EQ(0.0, f);
|
||||
ASSERT_FALSE(android::base::ParseFloat("0", &f, 1e-9));
|
||||
ASSERT_FALSE(android::base::ParseFloat("3.0", &f, -1.0, 2.0));
|
||||
ASSERT_TRUE(android::base::ParseFloat("1.0", &f, 0.0, 2.0));
|
||||
ASSERT_FLOAT_EQ(1.0, f);
|
||||
|
||||
ASSERT_FALSE(android::base::ParseFloat("123.4x", nullptr));
|
||||
ASSERT_TRUE(android::base::ParseFloat("-123.4", nullptr));
|
||||
ASSERT_FALSE(android::base::ParseFloat("3.0", nullptr, -1.0, 2.0));
|
||||
ASSERT_TRUE(android::base::ParseFloat("1.0", nullptr, 0.0, 2.0));
|
||||
}
|
|
@ -1,203 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "android-base/parseint.h"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
TEST(parseint, signed_smoke) {
|
||||
errno = 0;
|
||||
int i = 0;
|
||||
ASSERT_FALSE(android::base::ParseInt("x", &i));
|
||||
ASSERT_EQ(EINVAL, errno);
|
||||
errno = 0;
|
||||
ASSERT_FALSE(android::base::ParseInt("123x", &i));
|
||||
ASSERT_EQ(EINVAL, errno);
|
||||
|
||||
ASSERT_TRUE(android::base::ParseInt("123", &i));
|
||||
ASSERT_EQ(123, i);
|
||||
ASSERT_EQ(0, errno);
|
||||
i = 0;
|
||||
EXPECT_TRUE(android::base::ParseInt(" 123", &i));
|
||||
EXPECT_EQ(123, i);
|
||||
ASSERT_TRUE(android::base::ParseInt("-123", &i));
|
||||
ASSERT_EQ(-123, i);
|
||||
i = 0;
|
||||
EXPECT_TRUE(android::base::ParseInt(" -123", &i));
|
||||
EXPECT_EQ(-123, i);
|
||||
|
||||
short s = 0;
|
||||
ASSERT_TRUE(android::base::ParseInt("1234", &s));
|
||||
ASSERT_EQ(1234, s);
|
||||
|
||||
ASSERT_TRUE(android::base::ParseInt("12", &i, 0, 15));
|
||||
ASSERT_EQ(12, i);
|
||||
errno = 0;
|
||||
ASSERT_FALSE(android::base::ParseInt("-12", &i, 0, 15));
|
||||
ASSERT_EQ(ERANGE, errno);
|
||||
errno = 0;
|
||||
ASSERT_FALSE(android::base::ParseInt("16", &i, 0, 15));
|
||||
ASSERT_EQ(ERANGE, errno);
|
||||
|
||||
errno = 0;
|
||||
ASSERT_FALSE(android::base::ParseInt<int>("x", nullptr));
|
||||
ASSERT_EQ(EINVAL, errno);
|
||||
errno = 0;
|
||||
ASSERT_FALSE(android::base::ParseInt<int>("123x", nullptr));
|
||||
ASSERT_EQ(EINVAL, errno);
|
||||
ASSERT_TRUE(android::base::ParseInt<int>("1234", nullptr));
|
||||
}
|
||||
|
||||
TEST(parseint, unsigned_smoke) {
|
||||
errno = 0;
|
||||
unsigned int i = 0u;
|
||||
ASSERT_FALSE(android::base::ParseUint("x", &i));
|
||||
ASSERT_EQ(EINVAL, errno);
|
||||
errno = 0;
|
||||
ASSERT_FALSE(android::base::ParseUint("123x", &i));
|
||||
ASSERT_EQ(EINVAL, errno);
|
||||
|
||||
ASSERT_TRUE(android::base::ParseUint("123", &i));
|
||||
ASSERT_EQ(123u, i);
|
||||
ASSERT_EQ(0, errno);
|
||||
i = 0u;
|
||||
EXPECT_TRUE(android::base::ParseUint(" 123", &i));
|
||||
EXPECT_EQ(123u, i);
|
||||
errno = 0;
|
||||
ASSERT_FALSE(android::base::ParseUint("-123", &i));
|
||||
EXPECT_EQ(EINVAL, errno);
|
||||
errno = 0;
|
||||
EXPECT_FALSE(android::base::ParseUint(" -123", &i));
|
||||
EXPECT_EQ(EINVAL, errno);
|
||||
|
||||
unsigned short s = 0u;
|
||||
ASSERT_TRUE(android::base::ParseUint("1234", &s));
|
||||
ASSERT_EQ(1234u, s);
|
||||
|
||||
ASSERT_TRUE(android::base::ParseUint("12", &i, 15u));
|
||||
ASSERT_EQ(12u, i);
|
||||
errno = 0;
|
||||
ASSERT_FALSE(android::base::ParseUint("-12", &i, 15u));
|
||||
ASSERT_EQ(EINVAL, errno);
|
||||
errno = 0;
|
||||
ASSERT_FALSE(android::base::ParseUint("16", &i, 15u));
|
||||
ASSERT_EQ(ERANGE, errno);
|
||||
|
||||
errno = 0;
|
||||
ASSERT_FALSE(android::base::ParseUint<unsigned short>("x", nullptr));
|
||||
ASSERT_EQ(EINVAL, errno);
|
||||
errno = 0;
|
||||
ASSERT_FALSE(android::base::ParseUint<unsigned short>("123x", nullptr));
|
||||
ASSERT_EQ(EINVAL, errno);
|
||||
ASSERT_TRUE(android::base::ParseUint<unsigned short>("1234", nullptr));
|
||||
|
||||
errno = 0;
|
||||
unsigned long long int lli;
|
||||
EXPECT_FALSE(android::base::ParseUint("-123", &lli));
|
||||
EXPECT_EQ(EINVAL, errno);
|
||||
errno = 0;
|
||||
EXPECT_FALSE(android::base::ParseUint(" -123", &lli));
|
||||
EXPECT_EQ(EINVAL, errno);
|
||||
}
|
||||
|
||||
TEST(parseint, no_implicit_octal) {
|
||||
int i = 0;
|
||||
ASSERT_TRUE(android::base::ParseInt("0123", &i));
|
||||
ASSERT_EQ(123, i);
|
||||
|
||||
unsigned int u = 0u;
|
||||
ASSERT_TRUE(android::base::ParseUint("0123", &u));
|
||||
ASSERT_EQ(123u, u);
|
||||
}
|
||||
|
||||
TEST(parseint, explicit_hex) {
|
||||
int i = 0;
|
||||
ASSERT_TRUE(android::base::ParseInt("0x123", &i));
|
||||
ASSERT_EQ(0x123, i);
|
||||
i = 0;
|
||||
EXPECT_TRUE(android::base::ParseInt(" 0x123", &i));
|
||||
EXPECT_EQ(0x123, i);
|
||||
|
||||
unsigned int u = 0u;
|
||||
ASSERT_TRUE(android::base::ParseUint("0x123", &u));
|
||||
ASSERT_EQ(0x123u, u);
|
||||
u = 0u;
|
||||
EXPECT_TRUE(android::base::ParseUint(" 0x123", &u));
|
||||
EXPECT_EQ(0x123u, u);
|
||||
}
|
||||
|
||||
TEST(parseint, string) {
|
||||
int i = 0;
|
||||
ASSERT_TRUE(android::base::ParseInt(std::string("123"), &i));
|
||||
ASSERT_EQ(123, i);
|
||||
|
||||
unsigned int u = 0u;
|
||||
ASSERT_TRUE(android::base::ParseUint(std::string("123"), &u));
|
||||
ASSERT_EQ(123u, u);
|
||||
}
|
||||
|
||||
TEST(parseint, untouched_on_failure) {
|
||||
int i = 123;
|
||||
ASSERT_FALSE(android::base::ParseInt("456x", &i));
|
||||
ASSERT_EQ(123, i);
|
||||
|
||||
unsigned int u = 123u;
|
||||
ASSERT_FALSE(android::base::ParseUint("456x", &u));
|
||||
ASSERT_EQ(123u, u);
|
||||
}
|
||||
|
||||
TEST(parseint, ParseByteCount) {
|
||||
uint64_t i = 0;
|
||||
ASSERT_TRUE(android::base::ParseByteCount("123b", &i));
|
||||
ASSERT_EQ(123ULL, i);
|
||||
|
||||
ASSERT_TRUE(android::base::ParseByteCount("8k", &i));
|
||||
ASSERT_EQ(8ULL * 1024, i);
|
||||
|
||||
ASSERT_TRUE(android::base::ParseByteCount("8M", &i));
|
||||
ASSERT_EQ(8ULL * 1024 * 1024, i);
|
||||
|
||||
ASSERT_TRUE(android::base::ParseByteCount("6g", &i));
|
||||
ASSERT_EQ(6ULL * 1024 * 1024 * 1024, i);
|
||||
|
||||
ASSERT_TRUE(android::base::ParseByteCount("1T", &i));
|
||||
ASSERT_EQ(1ULL * 1024 * 1024 * 1024 * 1024, i);
|
||||
|
||||
ASSERT_TRUE(android::base::ParseByteCount("2p", &i));
|
||||
ASSERT_EQ(2ULL * 1024 * 1024 * 1024 * 1024 * 1024, i);
|
||||
|
||||
ASSERT_TRUE(android::base::ParseByteCount("4e", &i));
|
||||
ASSERT_EQ(4ULL * 1024 * 1024 * 1024 * 1024 * 1024 * 1024, i);
|
||||
}
|
||||
|
||||
TEST(parseint, ParseByteCount_invalid_suffix) {
|
||||
unsigned u;
|
||||
ASSERT_FALSE(android::base::ParseByteCount("1x", &u));
|
||||
}
|
||||
|
||||
TEST(parseint, ParseByteCount_overflow) {
|
||||
uint64_t u64;
|
||||
ASSERT_FALSE(android::base::ParseByteCount("4294967295E", &u64));
|
||||
|
||||
uint16_t u16;
|
||||
ASSERT_TRUE(android::base::ParseByteCount("63k", &u16));
|
||||
ASSERT_EQ(63U * 1024, u16);
|
||||
ASSERT_TRUE(android::base::ParseByteCount("65535b", &u16));
|
||||
ASSERT_EQ(65535U, u16);
|
||||
ASSERT_FALSE(android::base::ParseByteCount("65k", &u16));
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "android-base/parsenetaddress.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "android-base/stringprintf.h"
|
||||
#include "android-base/strings.h"
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
bool ParseNetAddress(const std::string& address, std::string* host, int* port,
|
||||
std::string* canonical_address, std::string* error) {
|
||||
host->clear();
|
||||
|
||||
bool ipv6 = true;
|
||||
bool saw_port = false;
|
||||
size_t colons = std::count(address.begin(), address.end(), ':');
|
||||
size_t dots = std::count(address.begin(), address.end(), '.');
|
||||
std::string port_str;
|
||||
if (address[0] == '[') {
|
||||
// [::1]:123
|
||||
if (address.rfind("]:") == std::string::npos) {
|
||||
*error = StringPrintf("bad IPv6 address '%s'", address.c_str());
|
||||
return false;
|
||||
}
|
||||
*host = address.substr(1, (address.find("]:") - 1));
|
||||
port_str = address.substr(address.rfind("]:") + 2);
|
||||
saw_port = true;
|
||||
} else if (dots == 0 && colons >= 2 && colons <= 7) {
|
||||
// ::1
|
||||
*host = address;
|
||||
} else if (colons <= 1) {
|
||||
// 1.2.3.4 or some.accidental.domain.com
|
||||
ipv6 = false;
|
||||
std::vector<std::string> pieces = Split(address, ":");
|
||||
*host = pieces[0];
|
||||
if (pieces.size() > 1) {
|
||||
port_str = pieces[1];
|
||||
saw_port = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (host->empty()) {
|
||||
*error = StringPrintf("no host in '%s'", address.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (saw_port) {
|
||||
if (sscanf(port_str.c_str(), "%d", port) != 1 || *port <= 0 ||
|
||||
*port > 65535) {
|
||||
*error = StringPrintf("bad port number '%s' in '%s'", port_str.c_str(),
|
||||
address.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (canonical_address != nullptr) {
|
||||
*canonical_address =
|
||||
StringPrintf(ipv6 ? "[%s]:%d" : "%s:%d", host->c_str(), *port);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,127 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "android-base/parsenetaddress.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using android::base::ParseNetAddress;
|
||||
|
||||
TEST(ParseNetAddressTest, TestUrl) {
|
||||
std::string canonical, host, error;
|
||||
int port = 123;
|
||||
|
||||
EXPECT_TRUE(
|
||||
ParseNetAddress("www.google.com", &host, &port, &canonical, &error));
|
||||
EXPECT_EQ("www.google.com:123", canonical);
|
||||
EXPECT_EQ("www.google.com", host);
|
||||
EXPECT_EQ(123, port);
|
||||
|
||||
EXPECT_TRUE(
|
||||
ParseNetAddress("www.google.com:666", &host, &port, &canonical, &error));
|
||||
EXPECT_EQ("www.google.com:666", canonical);
|
||||
EXPECT_EQ("www.google.com", host);
|
||||
EXPECT_EQ(666, port);
|
||||
}
|
||||
|
||||
TEST(ParseNetAddressTest, TestIpv4) {
|
||||
std::string canonical, host, error;
|
||||
int port = 123;
|
||||
|
||||
EXPECT_TRUE(ParseNetAddress("1.2.3.4", &host, &port, &canonical, &error));
|
||||
EXPECT_EQ("1.2.3.4:123", canonical);
|
||||
EXPECT_EQ("1.2.3.4", host);
|
||||
EXPECT_EQ(123, port);
|
||||
|
||||
EXPECT_TRUE(ParseNetAddress("1.2.3.4:666", &host, &port, &canonical, &error));
|
||||
EXPECT_EQ("1.2.3.4:666", canonical);
|
||||
EXPECT_EQ("1.2.3.4", host);
|
||||
EXPECT_EQ(666, port);
|
||||
}
|
||||
|
||||
TEST(ParseNetAddressTest, TestIpv6) {
|
||||
std::string canonical, host, error;
|
||||
int port = 123;
|
||||
|
||||
EXPECT_TRUE(ParseNetAddress("::1", &host, &port, &canonical, &error));
|
||||
EXPECT_EQ("[::1]:123", canonical);
|
||||
EXPECT_EQ("::1", host);
|
||||
EXPECT_EQ(123, port);
|
||||
|
||||
EXPECT_TRUE(ParseNetAddress("fe80::200:5aee:feaa:20a2", &host, &port,
|
||||
&canonical, &error));
|
||||
EXPECT_EQ("[fe80::200:5aee:feaa:20a2]:123", canonical);
|
||||
EXPECT_EQ("fe80::200:5aee:feaa:20a2", host);
|
||||
EXPECT_EQ(123, port);
|
||||
|
||||
EXPECT_TRUE(ParseNetAddress("[::1]:666", &host, &port, &canonical, &error));
|
||||
EXPECT_EQ("[::1]:666", canonical);
|
||||
EXPECT_EQ("::1", host);
|
||||
EXPECT_EQ(666, port);
|
||||
|
||||
EXPECT_TRUE(ParseNetAddress("[fe80::200:5aee:feaa:20a2]:666", &host, &port,
|
||||
&canonical, &error));
|
||||
EXPECT_EQ("[fe80::200:5aee:feaa:20a2]:666", canonical);
|
||||
EXPECT_EQ("fe80::200:5aee:feaa:20a2", host);
|
||||
EXPECT_EQ(666, port);
|
||||
}
|
||||
|
||||
TEST(ParseNetAddressTest, TestInvalidAddress) {
|
||||
std::string canonical, host;
|
||||
int port;
|
||||
|
||||
std::string failure_cases[] = {
|
||||
// Invalid IPv4.
|
||||
"1.2.3.4:",
|
||||
"1.2.3.4::",
|
||||
":123",
|
||||
|
||||
// Invalid IPv6.
|
||||
":1",
|
||||
"::::::::1",
|
||||
"[::1",
|
||||
"[::1]",
|
||||
"[::1]:",
|
||||
"[::1]::",
|
||||
|
||||
// Invalid port.
|
||||
"1.2.3.4:-1",
|
||||
"1.2.3.4:0",
|
||||
"1.2.3.4:65536"
|
||||
"1.2.3.4:hello",
|
||||
"[::1]:-1",
|
||||
"[::1]:0",
|
||||
"[::1]:65536",
|
||||
"[::1]:hello",
|
||||
};
|
||||
|
||||
for (const auto& address : failure_cases) {
|
||||
// Failure should give some non-empty error string.
|
||||
std::string error;
|
||||
EXPECT_FALSE(ParseNetAddress(address, &host, &port, &canonical, &error));
|
||||
EXPECT_NE("", error);
|
||||
}
|
||||
}
|
||||
|
||||
// Null canonical address argument.
|
||||
TEST(ParseNetAddressTest, TestNullCanonicalAddress) {
|
||||
std::string host, error;
|
||||
int port = 42;
|
||||
|
||||
EXPECT_TRUE(ParseNetAddress("www.google.com", &host, &port, nullptr, &error));
|
||||
EXPECT_TRUE(ParseNetAddress("1.2.3.4", &host, &port, nullptr, &error));
|
||||
EXPECT_TRUE(ParseNetAddress("::1", &host, &port, nullptr, &error));
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "android-base/process.h"
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
void AllPids::PidIterator::Increment() {
|
||||
if (!dir_) {
|
||||
return;
|
||||
}
|
||||
|
||||
dirent* de;
|
||||
while ((de = readdir(dir_.get())) != nullptr) {
|
||||
pid_t pid = atoi(de->d_name);
|
||||
if (pid != 0) {
|
||||
pid_ = pid;
|
||||
return;
|
||||
}
|
||||
}
|
||||
pid_ = -1;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,35 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "android-base/process.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
TEST(process, find_ourselves) {
|
||||
#if defined(__linux__)
|
||||
bool found_our_pid = false;
|
||||
for (const auto& pid : android::base::AllPids{}) {
|
||||
if (pid == getpid()) {
|
||||
found_our_pid = true;
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_TRUE(found_our_pid);
|
||||
|
||||
#endif
|
||||
}
|
|
@ -1,257 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "android-base/properties.h"
|
||||
|
||||
#if defined(__BIONIC__)
|
||||
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
|
||||
#include <sys/system_properties.h>
|
||||
#include <sys/_system_properties.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include <android-base/parsebool.h>
|
||||
#include <android-base/parseint.h>
|
||||
#include <android-base/strings.h>
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
bool GetBoolProperty(const std::string& key, bool default_value) {
|
||||
switch (ParseBool(GetProperty(key, ""))) {
|
||||
case ParseBoolResult::kError:
|
||||
return default_value;
|
||||
case ParseBoolResult::kFalse:
|
||||
return false;
|
||||
case ParseBoolResult::kTrue:
|
||||
return true;
|
||||
}
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T GetIntProperty(const std::string& key, T default_value, T min, T max) {
|
||||
T result;
|
||||
std::string value = GetProperty(key, "");
|
||||
if (!value.empty() && android::base::ParseInt(value, &result, min, max)) return result;
|
||||
return default_value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T GetUintProperty(const std::string& key, T default_value, T max) {
|
||||
T result;
|
||||
std::string value = GetProperty(key, "");
|
||||
if (!value.empty() && android::base::ParseUint(value, &result, max)) return result;
|
||||
return default_value;
|
||||
}
|
||||
|
||||
template int8_t GetIntProperty(const std::string&, int8_t, int8_t, int8_t);
|
||||
template int16_t GetIntProperty(const std::string&, int16_t, int16_t, int16_t);
|
||||
template int32_t GetIntProperty(const std::string&, int32_t, int32_t, int32_t);
|
||||
template int64_t GetIntProperty(const std::string&, int64_t, int64_t, int64_t);
|
||||
|
||||
template uint8_t GetUintProperty(const std::string&, uint8_t, uint8_t);
|
||||
template uint16_t GetUintProperty(const std::string&, uint16_t, uint16_t);
|
||||
template uint32_t GetUintProperty(const std::string&, uint32_t, uint32_t);
|
||||
template uint64_t GetUintProperty(const std::string&, uint64_t, uint64_t);
|
||||
|
||||
#if !defined(__BIONIC__)
|
||||
static std::map<std::string, std::string>& g_properties = *new std::map<std::string, std::string>;
|
||||
static int __system_property_set(const char* key, const char* value) {
|
||||
g_properties[key] = value;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string GetProperty(const std::string& key, const std::string& default_value) {
|
||||
std::string property_value;
|
||||
#if defined(__BIONIC__)
|
||||
const prop_info* pi = __system_property_find(key.c_str());
|
||||
if (pi == nullptr) return default_value;
|
||||
|
||||
__system_property_read_callback(pi,
|
||||
[](void* cookie, const char*, const char* value, unsigned) {
|
||||
auto property_value = reinterpret_cast<std::string*>(cookie);
|
||||
*property_value = value;
|
||||
},
|
||||
&property_value);
|
||||
#else
|
||||
auto it = g_properties.find(key);
|
||||
if (it == g_properties.end()) return default_value;
|
||||
property_value = it->second;
|
||||
#endif
|
||||
// If the property exists but is empty, also return the default value.
|
||||
// Since we can't remove system properties, "empty" is traditionally
|
||||
// the same as "missing" (this was true for cutils' property_get).
|
||||
return property_value.empty() ? default_value : property_value;
|
||||
}
|
||||
|
||||
bool SetProperty(const std::string& key, const std::string& value) {
|
||||
return (__system_property_set(key.c_str(), value.c_str()) == 0);
|
||||
}
|
||||
|
||||
#if defined(__BIONIC__)
|
||||
|
||||
struct WaitForPropertyData {
|
||||
bool done;
|
||||
const std::string* expected_value;
|
||||
unsigned last_read_serial;
|
||||
};
|
||||
|
||||
static void WaitForPropertyCallback(void* data_ptr, const char*, const char* value, unsigned serial) {
|
||||
WaitForPropertyData* data = reinterpret_cast<WaitForPropertyData*>(data_ptr);
|
||||
if (*data->expected_value == value) {
|
||||
data->done = true;
|
||||
} else {
|
||||
data->last_read_serial = serial;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: chrono_utils?
|
||||
static void DurationToTimeSpec(timespec& ts, const std::chrono::milliseconds d) {
|
||||
auto s = std::chrono::duration_cast<std::chrono::seconds>(d);
|
||||
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(d - s);
|
||||
ts.tv_sec = std::min<std::chrono::seconds::rep>(s.count(), std::numeric_limits<time_t>::max());
|
||||
ts.tv_nsec = ns.count();
|
||||
}
|
||||
|
||||
using AbsTime = std::chrono::time_point<std::chrono::steady_clock>;
|
||||
|
||||
static void UpdateTimeSpec(timespec& ts, std::chrono::milliseconds relative_timeout,
|
||||
const AbsTime& start_time) {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
|
||||
if (time_elapsed >= relative_timeout) {
|
||||
ts = { 0, 0 };
|
||||
} else {
|
||||
auto remaining_timeout = relative_timeout - time_elapsed;
|
||||
DurationToTimeSpec(ts, remaining_timeout);
|
||||
}
|
||||
}
|
||||
|
||||
// Waits for the system property `key` to be created.
|
||||
// Times out after `relative_timeout`.
|
||||
// Sets absolute_timeout which represents absolute time for the timeout.
|
||||
// Returns nullptr on timeout.
|
||||
static const prop_info* WaitForPropertyCreation(const std::string& key,
|
||||
const std::chrono::milliseconds& relative_timeout,
|
||||
const AbsTime& start_time) {
|
||||
// Find the property's prop_info*.
|
||||
const prop_info* pi;
|
||||
unsigned global_serial = 0;
|
||||
while ((pi = __system_property_find(key.c_str())) == nullptr) {
|
||||
// The property doesn't even exist yet.
|
||||
// Wait for a global change and then look again.
|
||||
timespec ts;
|
||||
UpdateTimeSpec(ts, relative_timeout, start_time);
|
||||
if (!__system_property_wait(nullptr, global_serial, &global_serial, &ts)) return nullptr;
|
||||
}
|
||||
return pi;
|
||||
}
|
||||
|
||||
bool WaitForProperty(const std::string& key, const std::string& expected_value,
|
||||
std::chrono::milliseconds relative_timeout) {
|
||||
auto start_time = std::chrono::steady_clock::now();
|
||||
const prop_info* pi = WaitForPropertyCreation(key, relative_timeout, start_time);
|
||||
if (pi == nullptr) return false;
|
||||
|
||||
WaitForPropertyData data;
|
||||
data.expected_value = &expected_value;
|
||||
data.done = false;
|
||||
while (true) {
|
||||
timespec ts;
|
||||
// Check whether the property has the value we're looking for?
|
||||
__system_property_read_callback(pi, WaitForPropertyCallback, &data);
|
||||
if (data.done) return true;
|
||||
|
||||
// It didn't, so wait for the property to change before checking again.
|
||||
UpdateTimeSpec(ts, relative_timeout, start_time);
|
||||
uint32_t unused;
|
||||
if (!__system_property_wait(pi, data.last_read_serial, &unused, &ts)) return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool WaitForPropertyCreation(const std::string& key,
|
||||
std::chrono::milliseconds relative_timeout) {
|
||||
auto start_time = std::chrono::steady_clock::now();
|
||||
return (WaitForPropertyCreation(key, relative_timeout, start_time) != nullptr);
|
||||
}
|
||||
|
||||
CachedProperty::CachedProperty(const char* property_name)
|
||||
: property_name_(property_name),
|
||||
prop_info_(nullptr),
|
||||
cached_area_serial_(0),
|
||||
cached_property_serial_(0),
|
||||
is_read_only_(android::base::StartsWith(property_name, "ro.")),
|
||||
read_only_property_(nullptr) {
|
||||
static_assert(sizeof(cached_value_) == PROP_VALUE_MAX);
|
||||
}
|
||||
|
||||
const char* CachedProperty::Get(bool* changed) {
|
||||
std::optional<uint32_t> initial_property_serial_ = cached_property_serial_;
|
||||
|
||||
// Do we have a `struct prop_info` yet?
|
||||
if (prop_info_ == nullptr) {
|
||||
// `__system_property_find` is expensive, so only retry if a property
|
||||
// has been created since last time we checked.
|
||||
uint32_t property_area_serial = __system_property_area_serial();
|
||||
if (property_area_serial != cached_area_serial_) {
|
||||
prop_info_ = __system_property_find(property_name_.c_str());
|
||||
cached_area_serial_ = property_area_serial;
|
||||
}
|
||||
}
|
||||
|
||||
if (prop_info_ != nullptr) {
|
||||
// Only bother re-reading the property if it's actually changed since last time.
|
||||
uint32_t property_serial = __system_property_serial(prop_info_);
|
||||
if (property_serial != cached_property_serial_) {
|
||||
__system_property_read_callback(
|
||||
prop_info_,
|
||||
[](void* data, const char*, const char* value, uint32_t serial) {
|
||||
CachedProperty* instance = reinterpret_cast<CachedProperty*>(data);
|
||||
instance->cached_property_serial_ = serial;
|
||||
// Read only properties can be larger than PROP_VALUE_MAX, but also never change value
|
||||
// or location, thus we return the pointer from the shared memory directly.
|
||||
if (instance->is_read_only_) {
|
||||
instance->read_only_property_ = value;
|
||||
} else {
|
||||
strlcpy(instance->cached_value_, value, PROP_VALUE_MAX);
|
||||
}
|
||||
},
|
||||
this);
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
*changed = cached_property_serial_ != initial_property_serial_;
|
||||
}
|
||||
|
||||
if (is_read_only_) {
|
||||
return read_only_property_;
|
||||
} else {
|
||||
return cached_value_;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,257 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "android-base/properties.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#if !defined(_WIN32)
|
||||
using namespace std::literals;
|
||||
#endif
|
||||
|
||||
TEST(properties, smoke) {
|
||||
android::base::SetProperty("debug.libbase.property_test", "hello");
|
||||
|
||||
std::string s = android::base::GetProperty("debug.libbase.property_test", "");
|
||||
ASSERT_EQ("hello", s);
|
||||
|
||||
android::base::SetProperty("debug.libbase.property_test", "world");
|
||||
s = android::base::GetProperty("debug.libbase.property_test", "");
|
||||
ASSERT_EQ("world", s);
|
||||
|
||||
s = android::base::GetProperty("this.property.does.not.exist", "");
|
||||
ASSERT_EQ("", s);
|
||||
|
||||
s = android::base::GetProperty("this.property.does.not.exist", "default");
|
||||
ASSERT_EQ("default", s);
|
||||
}
|
||||
|
||||
TEST(properties, empty) {
|
||||
// Because you can't delete a property, people "delete" them by
|
||||
// setting them to the empty string. In that case we'd want to
|
||||
// keep the default value (like cutils' property_get did).
|
||||
android::base::SetProperty("debug.libbase.property_test", "");
|
||||
std::string s = android::base::GetProperty("debug.libbase.property_test", "default");
|
||||
ASSERT_EQ("default", s);
|
||||
}
|
||||
|
||||
static void CheckGetBoolProperty(bool expected, const std::string& value, bool default_value) {
|
||||
android::base::SetProperty("debug.libbase.property_test", value.c_str());
|
||||
ASSERT_EQ(expected, android::base::GetBoolProperty("debug.libbase.property_test", default_value));
|
||||
}
|
||||
|
||||
TEST(properties, GetBoolProperty_true) {
|
||||
CheckGetBoolProperty(true, "1", false);
|
||||
CheckGetBoolProperty(true, "y", false);
|
||||
CheckGetBoolProperty(true, "yes", false);
|
||||
CheckGetBoolProperty(true, "on", false);
|
||||
CheckGetBoolProperty(true, "true", false);
|
||||
}
|
||||
|
||||
TEST(properties, GetBoolProperty_false) {
|
||||
CheckGetBoolProperty(false, "0", true);
|
||||
CheckGetBoolProperty(false, "n", true);
|
||||
CheckGetBoolProperty(false, "no", true);
|
||||
CheckGetBoolProperty(false, "off", true);
|
||||
CheckGetBoolProperty(false, "false", true);
|
||||
}
|
||||
|
||||
TEST(properties, GetBoolProperty_default) {
|
||||
CheckGetBoolProperty(true, "burp", true);
|
||||
CheckGetBoolProperty(false, "burp", false);
|
||||
}
|
||||
|
||||
template <typename T> void CheckGetIntProperty() {
|
||||
// Positive and negative.
|
||||
android::base::SetProperty("debug.libbase.property_test", "-12");
|
||||
EXPECT_EQ(T(-12), android::base::GetIntProperty<T>("debug.libbase.property_test", 45));
|
||||
android::base::SetProperty("debug.libbase.property_test", "12");
|
||||
EXPECT_EQ(T(12), android::base::GetIntProperty<T>("debug.libbase.property_test", 45));
|
||||
|
||||
// Default value.
|
||||
android::base::SetProperty("debug.libbase.property_test", "");
|
||||
EXPECT_EQ(T(45), android::base::GetIntProperty<T>("debug.libbase.property_test", 45));
|
||||
|
||||
// Bounds checks.
|
||||
android::base::SetProperty("debug.libbase.property_test", "0");
|
||||
EXPECT_EQ(T(45), android::base::GetIntProperty<T>("debug.libbase.property_test", 45, 1, 2));
|
||||
android::base::SetProperty("debug.libbase.property_test", "1");
|
||||
EXPECT_EQ(T(1), android::base::GetIntProperty<T>("debug.libbase.property_test", 45, 1, 2));
|
||||
android::base::SetProperty("debug.libbase.property_test", "2");
|
||||
EXPECT_EQ(T(2), android::base::GetIntProperty<T>("debug.libbase.property_test", 45, 1, 2));
|
||||
android::base::SetProperty("debug.libbase.property_test", "3");
|
||||
EXPECT_EQ(T(45), android::base::GetIntProperty<T>("debug.libbase.property_test", 45, 1, 2));
|
||||
}
|
||||
|
||||
template <typename T> void CheckGetUintProperty() {
|
||||
// Positive.
|
||||
android::base::SetProperty("debug.libbase.property_test", "12");
|
||||
EXPECT_EQ(T(12), android::base::GetUintProperty<T>("debug.libbase.property_test", 45));
|
||||
|
||||
// Default value.
|
||||
android::base::SetProperty("debug.libbase.property_test", "");
|
||||
EXPECT_EQ(T(45), android::base::GetUintProperty<T>("debug.libbase.property_test", 45));
|
||||
|
||||
// Bounds checks.
|
||||
android::base::SetProperty("debug.libbase.property_test", "12");
|
||||
EXPECT_EQ(T(12), android::base::GetUintProperty<T>("debug.libbase.property_test", 33, 22));
|
||||
android::base::SetProperty("debug.libbase.property_test", "12");
|
||||
EXPECT_EQ(T(5), android::base::GetUintProperty<T>("debug.libbase.property_test", 5, 10));
|
||||
}
|
||||
|
||||
TEST(properties, GetIntProperty_int8_t) { CheckGetIntProperty<int8_t>(); }
|
||||
TEST(properties, GetIntProperty_int16_t) { CheckGetIntProperty<int16_t>(); }
|
||||
TEST(properties, GetIntProperty_int32_t) { CheckGetIntProperty<int32_t>(); }
|
||||
TEST(properties, GetIntProperty_int64_t) { CheckGetIntProperty<int64_t>(); }
|
||||
|
||||
TEST(properties, GetUintProperty_uint8_t) { CheckGetUintProperty<uint8_t>(); }
|
||||
TEST(properties, GetUintProperty_uint16_t) { CheckGetUintProperty<uint16_t>(); }
|
||||
TEST(properties, GetUintProperty_uint32_t) { CheckGetUintProperty<uint32_t>(); }
|
||||
TEST(properties, GetUintProperty_uint64_t) { CheckGetUintProperty<uint64_t>(); }
|
||||
|
||||
TEST(properties, WaitForProperty) {
|
||||
#if defined(__BIONIC__)
|
||||
std::atomic<bool> flag{false};
|
||||
std::thread thread([&]() {
|
||||
std::this_thread::sleep_for(100ms);
|
||||
android::base::SetProperty("debug.libbase.WaitForProperty_test", "a");
|
||||
while (!flag) std::this_thread::yield();
|
||||
android::base::SetProperty("debug.libbase.WaitForProperty_test", "b");
|
||||
});
|
||||
|
||||
ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "a", 1s));
|
||||
flag = true;
|
||||
ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b", 1s));
|
||||
thread.join();
|
||||
#else
|
||||
GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(properties, WaitForProperty_timeout) {
|
||||
#if defined(__BIONIC__)
|
||||
auto t0 = std::chrono::steady_clock::now();
|
||||
ASSERT_FALSE(android::base::WaitForProperty("debug.libbase.WaitForProperty_timeout_test", "a",
|
||||
200ms));
|
||||
auto t1 = std::chrono::steady_clock::now();
|
||||
|
||||
ASSERT_GE(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 200ms);
|
||||
// Upper bounds on timing are inherently flaky, but let's try...
|
||||
ASSERT_LT(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 600ms);
|
||||
#else
|
||||
GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(properties, WaitForProperty_MaxTimeout) {
|
||||
#if defined(__BIONIC__)
|
||||
std::atomic<bool> flag{false};
|
||||
std::thread thread([&]() {
|
||||
android::base::SetProperty("debug.libbase.WaitForProperty_test", "a");
|
||||
while (!flag) std::this_thread::yield();
|
||||
std::this_thread::sleep_for(500ms);
|
||||
android::base::SetProperty("debug.libbase.WaitForProperty_test", "b");
|
||||
});
|
||||
|
||||
ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "a", 1s));
|
||||
flag = true;
|
||||
// Test that this does not immediately return false due to overflow issues with the timeout.
|
||||
ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b"));
|
||||
thread.join();
|
||||
#else
|
||||
GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(properties, WaitForProperty_NegativeTimeout) {
|
||||
#if defined(__BIONIC__)
|
||||
std::atomic<bool> flag{false};
|
||||
std::thread thread([&]() {
|
||||
android::base::SetProperty("debug.libbase.WaitForProperty_test", "a");
|
||||
while (!flag) std::this_thread::yield();
|
||||
std::this_thread::sleep_for(500ms);
|
||||
android::base::SetProperty("debug.libbase.WaitForProperty_test", "b");
|
||||
});
|
||||
|
||||
ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "a", 1s));
|
||||
flag = true;
|
||||
// Assert that this immediately returns with a negative timeout
|
||||
ASSERT_FALSE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b", -100ms));
|
||||
thread.join();
|
||||
#else
|
||||
GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(properties, WaitForPropertyCreation) {
|
||||
#if defined(__BIONIC__)
|
||||
std::thread thread([&]() {
|
||||
std::this_thread::sleep_for(100ms);
|
||||
android::base::SetProperty("debug.libbase.WaitForPropertyCreation_test", "a");
|
||||
});
|
||||
|
||||
ASSERT_TRUE(android::base::WaitForPropertyCreation(
|
||||
"debug.libbase.WaitForPropertyCreation_test", 1s));
|
||||
thread.join();
|
||||
#else
|
||||
GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(properties, WaitForPropertyCreation_timeout) {
|
||||
#if defined(__BIONIC__)
|
||||
auto t0 = std::chrono::steady_clock::now();
|
||||
ASSERT_FALSE(android::base::WaitForPropertyCreation(
|
||||
"debug.libbase.WaitForPropertyCreation_timeout_test", 200ms));
|
||||
auto t1 = std::chrono::steady_clock::now();
|
||||
|
||||
ASSERT_GE(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 200ms);
|
||||
// Upper bounds on timing are inherently flaky, but let's try...
|
||||
ASSERT_LT(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 600ms);
|
||||
#else
|
||||
GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(properties, CachedProperty) {
|
||||
#if defined(__BIONIC__)
|
||||
android::base::CachedProperty cached_property("debug.libbase.CachedProperty_test");
|
||||
bool changed;
|
||||
cached_property.Get(&changed);
|
||||
|
||||
android::base::SetProperty("debug.libbase.CachedProperty_test", "foo");
|
||||
ASSERT_STREQ("foo", cached_property.Get(&changed));
|
||||
ASSERT_TRUE(changed);
|
||||
|
||||
ASSERT_STREQ("foo", cached_property.Get(&changed));
|
||||
ASSERT_FALSE(changed);
|
||||
|
||||
android::base::SetProperty("debug.libbase.CachedProperty_test", "bar");
|
||||
ASSERT_STREQ("bar", cached_property.Get(&changed));
|
||||
ASSERT_TRUE(changed);
|
||||
|
||||
ASSERT_STREQ("bar", cached_property.Get(&changed));
|
||||
ASSERT_FALSE(changed);
|
||||
|
||||
#else
|
||||
GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
|
||||
#endif
|
||||
}
|
|
@ -1,422 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "android-base/result.h"
|
||||
|
||||
#include "errno.h"
|
||||
|
||||
#include <istream>
|
||||
#include <string>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace std::string_literals;
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
TEST(result, result_accessors) {
|
||||
Result<std::string> result = "success";
|
||||
ASSERT_RESULT_OK(result);
|
||||
ASSERT_TRUE(result.has_value());
|
||||
|
||||
EXPECT_EQ("success", *result);
|
||||
EXPECT_EQ("success", result.value());
|
||||
|
||||
EXPECT_EQ('s', result->data()[0]);
|
||||
}
|
||||
|
||||
TEST(result, result_accessors_rvalue) {
|
||||
ASSERT_TRUE(Result<std::string>("success").ok());
|
||||
ASSERT_TRUE(Result<std::string>("success").has_value());
|
||||
|
||||
EXPECT_EQ("success", *Result<std::string>("success"));
|
||||
EXPECT_EQ("success", Result<std::string>("success").value());
|
||||
|
||||
EXPECT_EQ('s', Result<std::string>("success")->data()[0]);
|
||||
}
|
||||
|
||||
TEST(result, result_void) {
|
||||
Result<void> ok = {};
|
||||
EXPECT_RESULT_OK(ok);
|
||||
ok.value(); // should not crash
|
||||
ASSERT_DEATH(ok.error(), "");
|
||||
|
||||
Result<void> fail = Error() << "failure" << 1;
|
||||
EXPECT_FALSE(fail.ok());
|
||||
EXPECT_EQ("failure1", fail.error().message());
|
||||
EXPECT_EQ(0, fail.error().code());
|
||||
EXPECT_TRUE(ok != fail);
|
||||
ASSERT_DEATH(fail.value(), "");
|
||||
|
||||
auto test = [](bool ok) -> Result<void> {
|
||||
if (ok) return {};
|
||||
else return Error() << "failure" << 1;
|
||||
};
|
||||
EXPECT_TRUE(test(true).ok());
|
||||
EXPECT_FALSE(test(false).ok());
|
||||
test(true).value(); // should not crash
|
||||
ASSERT_DEATH(test(true).error(), "");
|
||||
ASSERT_DEATH(test(false).value(), "");
|
||||
EXPECT_EQ("failure1", test(false).error().message());
|
||||
}
|
||||
|
||||
TEST(result, result_error) {
|
||||
Result<void> result = Error() << "failure" << 1;
|
||||
ASSERT_FALSE(result.ok());
|
||||
ASSERT_FALSE(result.has_value());
|
||||
|
||||
EXPECT_EQ(0, result.error().code());
|
||||
EXPECT_EQ("failure1", result.error().message());
|
||||
}
|
||||
|
||||
TEST(result, result_error_empty) {
|
||||
Result<void> result = Error();
|
||||
ASSERT_FALSE(result.ok());
|
||||
ASSERT_FALSE(result.has_value());
|
||||
|
||||
EXPECT_EQ(0, result.error().code());
|
||||
EXPECT_EQ("", result.error().message());
|
||||
}
|
||||
|
||||
TEST(result, result_error_rvalue) {
|
||||
// Error() and ErrnoError() aren't actually used to create a Result<T> object.
|
||||
// Under the hood, they are an intermediate class that can be implicitly constructed into a
|
||||
// Result<T>. This is needed both to create the ostream and because Error() itself, by
|
||||
// definition will not know what the type, T, of the underlying Result<T> object that it would
|
||||
// create is.
|
||||
|
||||
auto MakeRvalueErrorResult = []() -> Result<void> { return Error() << "failure" << 1; };
|
||||
ASSERT_FALSE(MakeRvalueErrorResult().ok());
|
||||
ASSERT_FALSE(MakeRvalueErrorResult().has_value());
|
||||
|
||||
EXPECT_EQ(0, MakeRvalueErrorResult().error().code());
|
||||
EXPECT_EQ("failure1", MakeRvalueErrorResult().error().message());
|
||||
}
|
||||
|
||||
TEST(result, result_errno_error) {
|
||||
constexpr int test_errno = 6;
|
||||
errno = test_errno;
|
||||
Result<void> result = ErrnoError() << "failure" << 1;
|
||||
|
||||
ASSERT_FALSE(result.ok());
|
||||
ASSERT_FALSE(result.has_value());
|
||||
|
||||
EXPECT_EQ(test_errno, result.error().code());
|
||||
EXPECT_EQ("failure1: "s + strerror(test_errno), result.error().message());
|
||||
}
|
||||
|
||||
TEST(result, result_errno_error_no_text) {
|
||||
constexpr int test_errno = 6;
|
||||
errno = test_errno;
|
||||
Result<void> result = ErrnoError();
|
||||
|
||||
ASSERT_FALSE(result.ok());
|
||||
ASSERT_FALSE(result.has_value());
|
||||
|
||||
EXPECT_EQ(test_errno, result.error().code());
|
||||
EXPECT_EQ(strerror(test_errno), result.error().message());
|
||||
}
|
||||
|
||||
TEST(result, result_error_from_other_result) {
|
||||
auto error_text = "test error"s;
|
||||
Result<void> result = Error() << error_text;
|
||||
|
||||
ASSERT_FALSE(result.ok());
|
||||
ASSERT_FALSE(result.has_value());
|
||||
|
||||
Result<std::string> result2 = result.error();
|
||||
|
||||
ASSERT_FALSE(result2.ok());
|
||||
ASSERT_FALSE(result2.has_value());
|
||||
|
||||
EXPECT_EQ(0, result2.error().code());
|
||||
EXPECT_EQ(error_text, result2.error().message());
|
||||
}
|
||||
|
||||
TEST(result, result_error_through_ostream) {
|
||||
auto error_text = "test error"s;
|
||||
Result<void> result = Error() << error_text;
|
||||
|
||||
ASSERT_FALSE(result.ok());
|
||||
ASSERT_FALSE(result.has_value());
|
||||
|
||||
Result<std::string> result2 = Error() << result.error();
|
||||
|
||||
ASSERT_FALSE(result2.ok());
|
||||
ASSERT_FALSE(result2.has_value());
|
||||
|
||||
EXPECT_EQ(0, result2.error().code());
|
||||
EXPECT_EQ(error_text, result2.error().message());
|
||||
}
|
||||
|
||||
TEST(result, result_errno_error_through_ostream) {
|
||||
auto error_text = "test error"s;
|
||||
constexpr int test_errno = 6;
|
||||
errno = 6;
|
||||
Result<void> result = ErrnoError() << error_text;
|
||||
|
||||
errno = 0;
|
||||
|
||||
ASSERT_FALSE(result.ok());
|
||||
ASSERT_FALSE(result.has_value());
|
||||
|
||||
Result<std::string> result2 = Error() << result.error();
|
||||
|
||||
ASSERT_FALSE(result2.ok());
|
||||
ASSERT_FALSE(result2.has_value());
|
||||
|
||||
EXPECT_EQ(test_errno, result2.error().code());
|
||||
EXPECT_EQ(error_text + ": " + strerror(test_errno), result2.error().message());
|
||||
}
|
||||
|
||||
TEST(result, constructor_forwarding) {
|
||||
auto result = Result<std::string>(std::in_place, 5, 'a');
|
||||
|
||||
ASSERT_RESULT_OK(result);
|
||||
ASSERT_TRUE(result.has_value());
|
||||
|
||||
EXPECT_EQ("aaaaa", *result);
|
||||
}
|
||||
|
||||
struct ConstructorTracker {
|
||||
static size_t constructor_called;
|
||||
static size_t copy_constructor_called;
|
||||
static size_t move_constructor_called;
|
||||
static size_t copy_assignment_called;
|
||||
static size_t move_assignment_called;
|
||||
|
||||
template <typename T>
|
||||
ConstructorTracker(T&& string) : string(string) {
|
||||
++constructor_called;
|
||||
}
|
||||
|
||||
ConstructorTracker(const ConstructorTracker& ct) {
|
||||
++copy_constructor_called;
|
||||
string = ct.string;
|
||||
}
|
||||
ConstructorTracker(ConstructorTracker&& ct) noexcept {
|
||||
++move_constructor_called;
|
||||
string = std::move(ct.string);
|
||||
}
|
||||
ConstructorTracker& operator=(const ConstructorTracker& ct) {
|
||||
++copy_assignment_called;
|
||||
string = ct.string;
|
||||
return *this;
|
||||
}
|
||||
ConstructorTracker& operator=(ConstructorTracker&& ct) noexcept {
|
||||
++move_assignment_called;
|
||||
string = std::move(ct.string);
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::string string;
|
||||
};
|
||||
|
||||
size_t ConstructorTracker::constructor_called = 0;
|
||||
size_t ConstructorTracker::copy_constructor_called = 0;
|
||||
size_t ConstructorTracker::move_constructor_called = 0;
|
||||
size_t ConstructorTracker::copy_assignment_called = 0;
|
||||
size_t ConstructorTracker::move_assignment_called = 0;
|
||||
|
||||
Result<ConstructorTracker> ReturnConstructorTracker(const std::string& in) {
|
||||
if (in.empty()) {
|
||||
return "literal string";
|
||||
}
|
||||
if (in == "test2") {
|
||||
return ConstructorTracker(in + in + "2");
|
||||
}
|
||||
ConstructorTracker result(in + " " + in);
|
||||
return result;
|
||||
};
|
||||
|
||||
TEST(result, no_copy_on_return) {
|
||||
// If returning parameters that may be used to implicitly construct the type T of Result<T>,
|
||||
// then those parameters are forwarded to the construction of Result<T>.
|
||||
|
||||
// If returning an prvalue or xvalue, it will be move constructed during the construction of
|
||||
// Result<T>.
|
||||
|
||||
// This check ensures that that is the case, and particularly that no copy constructors
|
||||
// are called.
|
||||
|
||||
auto result1 = ReturnConstructorTracker("");
|
||||
ASSERT_RESULT_OK(result1);
|
||||
EXPECT_EQ("literal string", result1->string);
|
||||
EXPECT_EQ(1U, ConstructorTracker::constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
|
||||
|
||||
auto result2 = ReturnConstructorTracker("test2");
|
||||
ASSERT_RESULT_OK(result2);
|
||||
EXPECT_EQ("test2test22", result2->string);
|
||||
EXPECT_EQ(2U, ConstructorTracker::constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
|
||||
EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
|
||||
|
||||
auto result3 = ReturnConstructorTracker("test3");
|
||||
ASSERT_RESULT_OK(result3);
|
||||
EXPECT_EQ("test3 test3", result3->string);
|
||||
EXPECT_EQ(3U, ConstructorTracker::constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
|
||||
EXPECT_EQ(2U, ConstructorTracker::move_constructor_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
|
||||
EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
|
||||
}
|
||||
|
||||
// Below two tests require that we do not hide the move constructor with our forwarding reference
|
||||
// constructor. This is done with by disabling the forwarding reference constructor if its first
|
||||
// and only type is Result<T>.
|
||||
TEST(result, result_result_with_success) {
|
||||
auto return_result_result_with_success = []() -> Result<Result<void>> { return Result<void>(); };
|
||||
auto result = return_result_result_with_success();
|
||||
ASSERT_RESULT_OK(result);
|
||||
ASSERT_RESULT_OK(*result);
|
||||
|
||||
auto inner_result = result.value();
|
||||
ASSERT_RESULT_OK(inner_result);
|
||||
}
|
||||
|
||||
TEST(result, result_result_with_failure) {
|
||||
auto return_result_result_with_error = []() -> Result<Result<void>> {
|
||||
return Result<void>(ResultError("failure string", 6));
|
||||
};
|
||||
auto result = return_result_result_with_error();
|
||||
ASSERT_RESULT_OK(result);
|
||||
ASSERT_FALSE(result->ok());
|
||||
EXPECT_EQ("failure string", (*result).error().message());
|
||||
EXPECT_EQ(6, (*result).error().code());
|
||||
}
|
||||
|
||||
// This test requires that we disable the forwarding reference constructor if Result<T> is the
|
||||
// *only* type that we are forwarding. In otherwords, if we are forwarding Result<T>, int to
|
||||
// construct a Result<T>, then we still need the constructor.
|
||||
TEST(result, result_two_parameter_constructor_same_type) {
|
||||
struct TestStruct {
|
||||
TestStruct(int value) : value_(value) {}
|
||||
TestStruct(Result<TestStruct> result, int value) : value_(result->value_ * value) {}
|
||||
int value_;
|
||||
};
|
||||
|
||||
auto return_test_struct = []() -> Result<TestStruct> {
|
||||
return Result<TestStruct>(std::in_place, Result<TestStruct>(std::in_place, 6), 6);
|
||||
};
|
||||
|
||||
auto result = return_test_struct();
|
||||
ASSERT_RESULT_OK(result);
|
||||
EXPECT_EQ(36, result->value_);
|
||||
}
|
||||
|
||||
TEST(result, die_on_access_failed_result) {
|
||||
Result<std::string> result = Error();
|
||||
ASSERT_DEATH(*result, "");
|
||||
}
|
||||
|
||||
TEST(result, die_on_get_error_succesful_result) {
|
||||
Result<std::string> result = "success";
|
||||
ASSERT_DEATH(result.error(), "");
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
std::basic_ostream<CharT>& SetErrnoToTwo(std::basic_ostream<CharT>& ss) {
|
||||
errno = 2;
|
||||
return ss;
|
||||
}
|
||||
|
||||
TEST(result, preserve_errno) {
|
||||
errno = 1;
|
||||
int old_errno = errno;
|
||||
Result<int> result = Error() << "Failed" << SetErrnoToTwo<char>;
|
||||
ASSERT_FALSE(result.ok());
|
||||
EXPECT_EQ(old_errno, errno);
|
||||
|
||||
errno = 1;
|
||||
old_errno = errno;
|
||||
Result<int> result2 = ErrnoError() << "Failed" << SetErrnoToTwo<char>;
|
||||
ASSERT_FALSE(result2.ok());
|
||||
EXPECT_EQ(old_errno, errno);
|
||||
EXPECT_EQ(old_errno, result2.error().code());
|
||||
}
|
||||
|
||||
TEST(result, error_with_fmt) {
|
||||
Result<int> result = Errorf("{} {}!", "hello", "world");
|
||||
EXPECT_EQ("hello world!", result.error().message());
|
||||
|
||||
result = Errorf("{} {}!", std::string("hello"), std::string("world"));
|
||||
EXPECT_EQ("hello world!", result.error().message());
|
||||
|
||||
result = Errorf("{1} {0}!", "world", "hello");
|
||||
EXPECT_EQ("hello world!", result.error().message());
|
||||
|
||||
result = Errorf("hello world!");
|
||||
EXPECT_EQ("hello world!", result.error().message());
|
||||
|
||||
Result<int> result2 = Errorf("error occurred with {}", result.error());
|
||||
EXPECT_EQ("error occurred with hello world!", result2.error().message());
|
||||
|
||||
constexpr int test_errno = 6;
|
||||
errno = test_errno;
|
||||
result = ErrnoErrorf("{} {}!", "hello", "world");
|
||||
EXPECT_EQ(test_errno, result.error().code());
|
||||
EXPECT_EQ("hello world!: "s + strerror(test_errno), result.error().message());
|
||||
}
|
||||
|
||||
TEST(result, error_with_fmt_carries_errno) {
|
||||
constexpr int inner_errno = 6;
|
||||
errno = inner_errno;
|
||||
Result<int> inner_result = ErrnoErrorf("inner failure");
|
||||
errno = 0;
|
||||
EXPECT_EQ(inner_errno, inner_result.error().code());
|
||||
|
||||
// outer_result is created with Errorf, but its error code is got from inner_result.
|
||||
Result<int> outer_result = Errorf("outer failure caused by {}", inner_result.error());
|
||||
EXPECT_EQ(inner_errno, outer_result.error().code());
|
||||
EXPECT_EQ("outer failure caused by inner failure: "s + strerror(inner_errno),
|
||||
outer_result.error().message());
|
||||
|
||||
// now both result objects are created with ErrnoErrorf. errno from the inner_result
|
||||
// is not passed to outer_result.
|
||||
constexpr int outer_errno = 10;
|
||||
errno = outer_errno;
|
||||
outer_result = ErrnoErrorf("outer failure caused by {}", inner_result.error());
|
||||
EXPECT_EQ(outer_errno, outer_result.error().code());
|
||||
EXPECT_EQ("outer failure caused by inner failure: "s + strerror(inner_errno) + ": "s +
|
||||
strerror(outer_errno),
|
||||
outer_result.error().message());
|
||||
}
|
||||
|
||||
TEST(result, errno_chaining_multiple) {
|
||||
constexpr int errno1 = 6;
|
||||
errno = errno1;
|
||||
Result<int> inner1 = ErrnoErrorf("error1");
|
||||
|
||||
constexpr int errno2 = 10;
|
||||
errno = errno2;
|
||||
Result<int> inner2 = ErrnoErrorf("error2");
|
||||
|
||||
// takes the error code of inner2 since its the last one.
|
||||
Result<int> outer = Errorf("two errors: {}, {}", inner1.error(), inner2.error());
|
||||
EXPECT_EQ(errno2, outer.error().code());
|
||||
EXPECT_EQ("two errors: error1: "s + strerror(errno1) + ", error2: "s + strerror(errno2),
|
||||
outer.error().message());
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,59 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "android-base/scopeguard.h"
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
TEST(scopeguard, normal) {
|
||||
bool guarded_var = true;
|
||||
{
|
||||
auto scopeguard = android::base::make_scope_guard([&guarded_var] { guarded_var = false; });
|
||||
}
|
||||
ASSERT_FALSE(guarded_var);
|
||||
}
|
||||
|
||||
TEST(scopeguard, disabled) {
|
||||
bool guarded_var = true;
|
||||
{
|
||||
auto scopeguard = android::base::make_scope_guard([&guarded_var] { guarded_var = false; });
|
||||
scopeguard.Disable();
|
||||
}
|
||||
ASSERT_TRUE(guarded_var);
|
||||
}
|
||||
|
||||
TEST(scopeguard, moved) {
|
||||
int guarded_var = true;
|
||||
auto scopeguard = android::base::make_scope_guard([&guarded_var] { guarded_var = false; });
|
||||
{ decltype(scopeguard) new_guard(std::move(scopeguard)); }
|
||||
EXPECT_FALSE(scopeguard.active());
|
||||
ASSERT_FALSE(guarded_var);
|
||||
}
|
||||
|
||||
TEST(scopeguard, vector) {
|
||||
int guarded_var = 0;
|
||||
{
|
||||
std::vector<android::base::ScopeGuard<std::function<void()>>> scopeguards;
|
||||
scopeguards.emplace_back(android::base::make_scope_guard(
|
||||
std::bind([](int& guarded_var) { guarded_var++; }, std::ref(guarded_var))));
|
||||
scopeguards.emplace_back(android::base::make_scope_guard(
|
||||
std::bind([](int& guarded_var) { guarded_var++; }, std::ref(guarded_var))));
|
||||
}
|
||||
ASSERT_EQ(guarded_var, 2);
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "android-base/stringprintf.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
void StringAppendV(std::string* dst, const char* format, va_list ap) {
|
||||
// First try with a small fixed size buffer
|
||||
char space[1024] __attribute__((__uninitialized__));
|
||||
|
||||
// It's possible for methods that use a va_list to invalidate
|
||||
// the data in it upon use. The fix is to make a copy
|
||||
// of the structure before using it and use that copy instead.
|
||||
va_list backup_ap;
|
||||
va_copy(backup_ap, ap);
|
||||
int result = vsnprintf(space, sizeof(space), format, backup_ap);
|
||||
va_end(backup_ap);
|
||||
|
||||
if (result < static_cast<int>(sizeof(space))) {
|
||||
if (result >= 0) {
|
||||
// Normal case -- everything fit.
|
||||
dst->append(space, result);
|
||||
return;
|
||||
}
|
||||
|
||||
if (result < 0) {
|
||||
// Just an error.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Increase the buffer size to the size requested by vsnprintf,
|
||||
// plus one for the closing \0.
|
||||
int length = result + 1;
|
||||
char* buf = new char[length];
|
||||
|
||||
// Restore the va_list before we use it again
|
||||
va_copy(backup_ap, ap);
|
||||
result = vsnprintf(buf, length, format, backup_ap);
|
||||
va_end(backup_ap);
|
||||
|
||||
if (result >= 0 && result < length) {
|
||||
// It fit
|
||||
dst->append(buf, result);
|
||||
}
|
||||
delete[] buf;
|
||||
}
|
||||
|
||||
std::string StringPrintf(const char* fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
std::string result;
|
||||
StringAppendV(&result, fmt, ap);
|
||||
va_end(ap);
|
||||
return result;
|
||||
}
|
||||
|
||||
void StringAppendF(std::string* dst, const char* format, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
StringAppendV(dst, format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,60 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "android-base/stringprintf.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
TEST(StringPrintfTest, HexSizeT) {
|
||||
size_t size = 0x00107e59;
|
||||
EXPECT_EQ("00107e59", android::base::StringPrintf("%08zx", size));
|
||||
EXPECT_EQ("0x00107e59", android::base::StringPrintf("0x%08zx", size));
|
||||
}
|
||||
|
||||
TEST(StringPrintfTest, StringAppendF) {
|
||||
std::string s("a");
|
||||
android::base::StringAppendF(&s, "b");
|
||||
EXPECT_EQ("ab", s);
|
||||
}
|
||||
|
||||
TEST(StringPrintfTest, Errno) {
|
||||
errno = 123;
|
||||
android::base::StringPrintf("hello %s", "world");
|
||||
EXPECT_EQ(123, errno);
|
||||
}
|
||||
|
||||
void TestN(size_t n) {
|
||||
char* buf = new char[n + 1];
|
||||
memset(buf, 'x', n);
|
||||
buf[n] = '\0';
|
||||
std::string s(android::base::StringPrintf("%s", buf));
|
||||
EXPECT_EQ(buf, s);
|
||||
delete[] buf;
|
||||
}
|
||||
|
||||
TEST(StringPrintfTest, At1023) {
|
||||
TestN(1023);
|
||||
}
|
||||
|
||||
TEST(StringPrintfTest, At1024) {
|
||||
TestN(1024);
|
||||
}
|
||||
|
||||
TEST(StringPrintfTest, At1025) {
|
||||
TestN(1025);
|
||||
}
|
139
base/strings.cpp
139
base/strings.cpp
|
@ -1,139 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "android-base/strings.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
#define CHECK_NE(a, b) \
|
||||
if ((a) == (b)) abort();
|
||||
|
||||
std::vector<std::string> Split(const std::string& s,
|
||||
const std::string& delimiters) {
|
||||
CHECK_NE(delimiters.size(), 0U);
|
||||
|
||||
std::vector<std::string> result;
|
||||
|
||||
size_t base = 0;
|
||||
size_t found;
|
||||
while (true) {
|
||||
found = s.find_first_of(delimiters, base);
|
||||
result.push_back(s.substr(base, found - base));
|
||||
if (found == s.npos) break;
|
||||
base = found + 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string Trim(const std::string& s) {
|
||||
std::string result;
|
||||
|
||||
if (s.size() == 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t start_index = 0;
|
||||
size_t end_index = s.size() - 1;
|
||||
|
||||
// Skip initial whitespace.
|
||||
while (start_index < s.size()) {
|
||||
if (!isspace(s[start_index])) {
|
||||
break;
|
||||
}
|
||||
start_index++;
|
||||
}
|
||||
|
||||
// Skip terminating whitespace.
|
||||
while (end_index >= start_index) {
|
||||
if (!isspace(s[end_index])) {
|
||||
break;
|
||||
}
|
||||
end_index--;
|
||||
}
|
||||
|
||||
// All spaces, no beef.
|
||||
if (end_index < start_index) {
|
||||
return "";
|
||||
}
|
||||
// Start_index is the first non-space, end_index is the last one.
|
||||
return s.substr(start_index, end_index - start_index + 1);
|
||||
}
|
||||
|
||||
// These cases are probably the norm, so we mark them extern in the header to
|
||||
// aid compile time and binary size.
|
||||
template std::string Join(const std::vector<std::string>&, char);
|
||||
template std::string Join(const std::vector<const char*>&, char);
|
||||
template std::string Join(const std::vector<std::string>&, const std::string&);
|
||||
template std::string Join(const std::vector<const char*>&, const std::string&);
|
||||
|
||||
bool StartsWith(std::string_view s, std::string_view prefix) {
|
||||
return s.substr(0, prefix.size()) == prefix;
|
||||
}
|
||||
|
||||
bool StartsWith(std::string_view s, char prefix) {
|
||||
return !s.empty() && s.front() == prefix;
|
||||
}
|
||||
|
||||
bool StartsWithIgnoreCase(std::string_view s, std::string_view prefix) {
|
||||
return s.size() >= prefix.size() && strncasecmp(s.data(), prefix.data(), prefix.size()) == 0;
|
||||
}
|
||||
|
||||
bool EndsWith(std::string_view s, std::string_view suffix) {
|
||||
return s.size() >= suffix.size() && s.substr(s.size() - suffix.size(), suffix.size()) == suffix;
|
||||
}
|
||||
|
||||
bool EndsWith(std::string_view s, char suffix) {
|
||||
return !s.empty() && s.back() == suffix;
|
||||
}
|
||||
|
||||
bool EndsWithIgnoreCase(std::string_view s, std::string_view suffix) {
|
||||
return s.size() >= suffix.size() &&
|
||||
strncasecmp(s.data() + (s.size() - suffix.size()), suffix.data(), suffix.size()) == 0;
|
||||
}
|
||||
|
||||
bool EqualsIgnoreCase(std::string_view lhs, std::string_view rhs) {
|
||||
return lhs.size() == rhs.size() && strncasecmp(lhs.data(), rhs.data(), lhs.size()) == 0;
|
||||
}
|
||||
|
||||
std::string StringReplace(std::string_view s, std::string_view from, std::string_view to,
|
||||
bool all) {
|
||||
if (from.empty()) return std::string(s);
|
||||
|
||||
std::string result;
|
||||
std::string_view::size_type start_pos = 0;
|
||||
do {
|
||||
std::string_view::size_type pos = s.find(from, start_pos);
|
||||
if (pos == std::string_view::npos) break;
|
||||
|
||||
result.append(s.data() + start_pos, pos - start_pos);
|
||||
result.append(to.data(), to.size());
|
||||
|
||||
start_pos = pos + from.size();
|
||||
} while (all);
|
||||
result.append(s.data() + start_pos, s.size() - start_pos);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,356 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "android-base/strings.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
|
||||
TEST(strings, split_empty) {
|
||||
std::vector<std::string> parts = android::base::Split("", ",");
|
||||
ASSERT_EQ(1U, parts.size());
|
||||
ASSERT_EQ("", parts[0]);
|
||||
}
|
||||
|
||||
TEST(strings, split_single) {
|
||||
std::vector<std::string> parts = android::base::Split("foo", ",");
|
||||
ASSERT_EQ(1U, parts.size());
|
||||
ASSERT_EQ("foo", parts[0]);
|
||||
}
|
||||
|
||||
TEST(strings, split_simple) {
|
||||
std::vector<std::string> parts = android::base::Split("foo,bar,baz", ",");
|
||||
ASSERT_EQ(3U, parts.size());
|
||||
ASSERT_EQ("foo", parts[0]);
|
||||
ASSERT_EQ("bar", parts[1]);
|
||||
ASSERT_EQ("baz", parts[2]);
|
||||
}
|
||||
|
||||
TEST(strings, split_with_empty_part) {
|
||||
std::vector<std::string> parts = android::base::Split("foo,,bar", ",");
|
||||
ASSERT_EQ(3U, parts.size());
|
||||
ASSERT_EQ("foo", parts[0]);
|
||||
ASSERT_EQ("", parts[1]);
|
||||
ASSERT_EQ("bar", parts[2]);
|
||||
}
|
||||
|
||||
TEST(strings, split_with_trailing_empty_part) {
|
||||
std::vector<std::string> parts = android::base::Split("foo,bar,", ",");
|
||||
ASSERT_EQ(3U, parts.size());
|
||||
ASSERT_EQ("foo", parts[0]);
|
||||
ASSERT_EQ("bar", parts[1]);
|
||||
ASSERT_EQ("", parts[2]);
|
||||
}
|
||||
|
||||
TEST(strings, split_null_char) {
|
||||
std::vector<std::string> parts =
|
||||
android::base::Split(std::string("foo\0bar", 7), std::string("\0", 1));
|
||||
ASSERT_EQ(2U, parts.size());
|
||||
ASSERT_EQ("foo", parts[0]);
|
||||
ASSERT_EQ("bar", parts[1]);
|
||||
}
|
||||
|
||||
TEST(strings, split_any) {
|
||||
std::vector<std::string> parts = android::base::Split("foo:bar,baz", ",:");
|
||||
ASSERT_EQ(3U, parts.size());
|
||||
ASSERT_EQ("foo", parts[0]);
|
||||
ASSERT_EQ("bar", parts[1]);
|
||||
ASSERT_EQ("baz", parts[2]);
|
||||
}
|
||||
|
||||
TEST(strings, split_any_with_empty_part) {
|
||||
std::vector<std::string> parts = android::base::Split("foo:,bar", ",:");
|
||||
ASSERT_EQ(3U, parts.size());
|
||||
ASSERT_EQ("foo", parts[0]);
|
||||
ASSERT_EQ("", parts[1]);
|
||||
ASSERT_EQ("bar", parts[2]);
|
||||
}
|
||||
|
||||
TEST(strings, trim_empty) {
|
||||
ASSERT_EQ("", android::base::Trim(""));
|
||||
}
|
||||
|
||||
TEST(strings, trim_already_trimmed) {
|
||||
ASSERT_EQ("foo", android::base::Trim("foo"));
|
||||
}
|
||||
|
||||
TEST(strings, trim_left) {
|
||||
ASSERT_EQ("foo", android::base::Trim(" foo"));
|
||||
}
|
||||
|
||||
TEST(strings, trim_right) {
|
||||
ASSERT_EQ("foo", android::base::Trim("foo "));
|
||||
}
|
||||
|
||||
TEST(strings, trim_both) {
|
||||
ASSERT_EQ("foo", android::base::Trim(" foo "));
|
||||
}
|
||||
|
||||
TEST(strings, trim_no_trim_middle) {
|
||||
ASSERT_EQ("foo bar", android::base::Trim("foo bar"));
|
||||
}
|
||||
|
||||
TEST(strings, trim_other_whitespace) {
|
||||
ASSERT_EQ("foo", android::base::Trim("\v\tfoo\n\f"));
|
||||
}
|
||||
|
||||
TEST(strings, join_nothing) {
|
||||
std::vector<std::string> list = {};
|
||||
ASSERT_EQ("", android::base::Join(list, ','));
|
||||
}
|
||||
|
||||
TEST(strings, join_single) {
|
||||
std::vector<std::string> list = {"foo"};
|
||||
ASSERT_EQ("foo", android::base::Join(list, ','));
|
||||
}
|
||||
|
||||
TEST(strings, join_simple) {
|
||||
std::vector<std::string> list = {"foo", "bar", "baz"};
|
||||
ASSERT_EQ("foo,bar,baz", android::base::Join(list, ','));
|
||||
}
|
||||
|
||||
TEST(strings, join_separator_in_vector) {
|
||||
std::vector<std::string> list = {",", ","};
|
||||
ASSERT_EQ(",,,", android::base::Join(list, ','));
|
||||
}
|
||||
|
||||
TEST(strings, join_simple_ints) {
|
||||
std::set<int> list = {1, 2, 3};
|
||||
ASSERT_EQ("1,2,3", android::base::Join(list, ','));
|
||||
}
|
||||
|
||||
TEST(strings, join_unordered_set) {
|
||||
std::unordered_set<int> list = {1, 2};
|
||||
ASSERT_TRUE("1,2" == android::base::Join(list, ',') ||
|
||||
"2,1" == android::base::Join(list, ','));
|
||||
}
|
||||
|
||||
TEST(strings, StartsWith_empty) {
|
||||
ASSERT_FALSE(android::base::StartsWith("", "foo"));
|
||||
ASSERT_TRUE(android::base::StartsWith("", ""));
|
||||
}
|
||||
|
||||
TEST(strings, StartsWithIgnoreCase_empty) {
|
||||
ASSERT_FALSE(android::base::StartsWithIgnoreCase("", "foo"));
|
||||
ASSERT_TRUE(android::base::StartsWithIgnoreCase("", ""));
|
||||
}
|
||||
|
||||
TEST(strings, StartsWith_simple) {
|
||||
ASSERT_TRUE(android::base::StartsWith("foo", ""));
|
||||
ASSERT_TRUE(android::base::StartsWith("foo", "f"));
|
||||
ASSERT_TRUE(android::base::StartsWith("foo", "fo"));
|
||||
ASSERT_TRUE(android::base::StartsWith("foo", "foo"));
|
||||
}
|
||||
|
||||
TEST(strings, StartsWithIgnoreCase_simple) {
|
||||
ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", ""));
|
||||
ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "f"));
|
||||
ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "F"));
|
||||
ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "fo"));
|
||||
ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "fO"));
|
||||
ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "Fo"));
|
||||
ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "FO"));
|
||||
ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "foo"));
|
||||
ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "foO"));
|
||||
ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "fOo"));
|
||||
ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "fOO"));
|
||||
ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "Foo"));
|
||||
ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "FoO"));
|
||||
ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "FOo"));
|
||||
ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "FOO"));
|
||||
}
|
||||
|
||||
TEST(strings, StartsWith_prefix_too_long) {
|
||||
ASSERT_FALSE(android::base::StartsWith("foo", "foobar"));
|
||||
}
|
||||
|
||||
TEST(strings, StartsWithIgnoreCase_prefix_too_long) {
|
||||
ASSERT_FALSE(android::base::StartsWithIgnoreCase("foo", "foobar"));
|
||||
ASSERT_FALSE(android::base::StartsWithIgnoreCase("foo", "FOOBAR"));
|
||||
}
|
||||
|
||||
TEST(strings, StartsWith_contains_prefix) {
|
||||
ASSERT_FALSE(android::base::StartsWith("foobar", "oba"));
|
||||
ASSERT_FALSE(android::base::StartsWith("foobar", "bar"));
|
||||
}
|
||||
|
||||
TEST(strings, StartsWithIgnoreCase_contains_prefix) {
|
||||
ASSERT_FALSE(android::base::StartsWithIgnoreCase("foobar", "oba"));
|
||||
ASSERT_FALSE(android::base::StartsWithIgnoreCase("foobar", "OBA"));
|
||||
ASSERT_FALSE(android::base::StartsWithIgnoreCase("foobar", "bar"));
|
||||
ASSERT_FALSE(android::base::StartsWithIgnoreCase("foobar", "BAR"));
|
||||
}
|
||||
|
||||
TEST(strings, StartsWith_char) {
|
||||
ASSERT_FALSE(android::base::StartsWith("", 'f'));
|
||||
ASSERT_TRUE(android::base::StartsWith("foo", 'f'));
|
||||
ASSERT_FALSE(android::base::StartsWith("foo", 'o'));
|
||||
}
|
||||
|
||||
TEST(strings, EndsWith_empty) {
|
||||
ASSERT_FALSE(android::base::EndsWith("", "foo"));
|
||||
ASSERT_TRUE(android::base::EndsWith("", ""));
|
||||
}
|
||||
|
||||
TEST(strings, EndsWithIgnoreCase_empty) {
|
||||
ASSERT_FALSE(android::base::EndsWithIgnoreCase("", "foo"));
|
||||
ASSERT_FALSE(android::base::EndsWithIgnoreCase("", "FOO"));
|
||||
ASSERT_TRUE(android::base::EndsWithIgnoreCase("", ""));
|
||||
}
|
||||
|
||||
TEST(strings, EndsWith_simple) {
|
||||
ASSERT_TRUE(android::base::EndsWith("foo", ""));
|
||||
ASSERT_TRUE(android::base::EndsWith("foo", "o"));
|
||||
ASSERT_TRUE(android::base::EndsWith("foo", "oo"));
|
||||
ASSERT_TRUE(android::base::EndsWith("foo", "foo"));
|
||||
}
|
||||
|
||||
TEST(strings, EndsWithIgnoreCase_simple) {
|
||||
ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", ""));
|
||||
ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "o"));
|
||||
ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "O"));
|
||||
ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "oo"));
|
||||
ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "oO"));
|
||||
ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "Oo"));
|
||||
ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "OO"));
|
||||
ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "foo"));
|
||||
ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "foO"));
|
||||
ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "fOo"));
|
||||
ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "fOO"));
|
||||
ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "Foo"));
|
||||
ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "FoO"));
|
||||
ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "FOo"));
|
||||
ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "FOO"));
|
||||
}
|
||||
|
||||
TEST(strings, EndsWith_prefix_too_long) {
|
||||
ASSERT_FALSE(android::base::EndsWith("foo", "foobar"));
|
||||
}
|
||||
|
||||
TEST(strings, EndsWithIgnoreCase_prefix_too_long) {
|
||||
ASSERT_FALSE(android::base::EndsWithIgnoreCase("foo", "foobar"));
|
||||
ASSERT_FALSE(android::base::EndsWithIgnoreCase("foo", "FOOBAR"));
|
||||
}
|
||||
|
||||
TEST(strings, EndsWith_contains_prefix) {
|
||||
ASSERT_FALSE(android::base::EndsWith("foobar", "oba"));
|
||||
ASSERT_FALSE(android::base::EndsWith("foobar", "foo"));
|
||||
}
|
||||
|
||||
TEST(strings, EndsWithIgnoreCase_contains_prefix) {
|
||||
ASSERT_FALSE(android::base::EndsWithIgnoreCase("foobar", "OBA"));
|
||||
ASSERT_FALSE(android::base::EndsWithIgnoreCase("foobar", "FOO"));
|
||||
}
|
||||
|
||||
TEST(strings, StartsWith_std_string) {
|
||||
ASSERT_TRUE(android::base::StartsWith("hello", std::string{"hell"}));
|
||||
ASSERT_FALSE(android::base::StartsWith("goodbye", std::string{"hell"}));
|
||||
}
|
||||
|
||||
TEST(strings, StartsWithIgnoreCase_std_string) {
|
||||
ASSERT_TRUE(android::base::StartsWithIgnoreCase("HeLlO", std::string{"hell"}));
|
||||
ASSERT_FALSE(android::base::StartsWithIgnoreCase("GoOdByE", std::string{"hell"}));
|
||||
}
|
||||
|
||||
TEST(strings, EndsWith_std_string) {
|
||||
ASSERT_TRUE(android::base::EndsWith("hello", std::string{"lo"}));
|
||||
ASSERT_FALSE(android::base::EndsWith("goodbye", std::string{"lo"}));
|
||||
}
|
||||
|
||||
TEST(strings, EndsWithIgnoreCase_std_string) {
|
||||
ASSERT_TRUE(android::base::EndsWithIgnoreCase("HeLlO", std::string{"lo"}));
|
||||
ASSERT_FALSE(android::base::EndsWithIgnoreCase("GoOdByE", std::string{"lo"}));
|
||||
}
|
||||
|
||||
TEST(strings, EndsWith_char) {
|
||||
ASSERT_FALSE(android::base::EndsWith("", 'o'));
|
||||
ASSERT_TRUE(android::base::EndsWith("foo", 'o'));
|
||||
ASSERT_FALSE(android::base::EndsWith("foo", "f"));
|
||||
}
|
||||
|
||||
TEST(strings, EqualsIgnoreCase) {
|
||||
ASSERT_TRUE(android::base::EqualsIgnoreCase("foo", "FOO"));
|
||||
ASSERT_TRUE(android::base::EqualsIgnoreCase("FOO", "foo"));
|
||||
ASSERT_FALSE(android::base::EqualsIgnoreCase("foo", "bar"));
|
||||
ASSERT_FALSE(android::base::EqualsIgnoreCase("foo", "fool"));
|
||||
}
|
||||
|
||||
TEST(strings, ubsan_28729303) {
|
||||
android::base::Split("/dev/null", ":");
|
||||
}
|
||||
|
||||
TEST(strings, ConsumePrefix) {
|
||||
std::string_view s{"foo.bar"};
|
||||
ASSERT_FALSE(android::base::ConsumePrefix(&s, "bar."));
|
||||
ASSERT_EQ("foo.bar", s);
|
||||
ASSERT_TRUE(android::base::ConsumePrefix(&s, "foo."));
|
||||
ASSERT_EQ("bar", s);
|
||||
}
|
||||
|
||||
TEST(strings, ConsumeSuffix) {
|
||||
std::string_view s{"foo.bar"};
|
||||
ASSERT_FALSE(android::base::ConsumeSuffix(&s, ".foo"));
|
||||
ASSERT_EQ("foo.bar", s);
|
||||
ASSERT_TRUE(android::base::ConsumeSuffix(&s, ".bar"));
|
||||
ASSERT_EQ("foo", s);
|
||||
}
|
||||
|
||||
TEST(strings, StringReplace_false) {
|
||||
// No change.
|
||||
ASSERT_EQ("abcabc", android::base::StringReplace("abcabc", "z", "Z", false));
|
||||
ASSERT_EQ("", android::base::StringReplace("", "z", "Z", false));
|
||||
ASSERT_EQ("abcabc", android::base::StringReplace("abcabc", "", "Z", false));
|
||||
|
||||
// Equal lengths.
|
||||
ASSERT_EQ("Abcabc", android::base::StringReplace("abcabc", "a", "A", false));
|
||||
ASSERT_EQ("aBcabc", android::base::StringReplace("abcabc", "b", "B", false));
|
||||
ASSERT_EQ("abCabc", android::base::StringReplace("abcabc", "c", "C", false));
|
||||
|
||||
// Longer replacement.
|
||||
ASSERT_EQ("foobcabc", android::base::StringReplace("abcabc", "a", "foo", false));
|
||||
ASSERT_EQ("afoocabc", android::base::StringReplace("abcabc", "b", "foo", false));
|
||||
ASSERT_EQ("abfooabc", android::base::StringReplace("abcabc", "c", "foo", false));
|
||||
|
||||
// Shorter replacement.
|
||||
ASSERT_EQ("xxyz", android::base::StringReplace("abcxyz", "abc", "x", false));
|
||||
ASSERT_EQ("axyz", android::base::StringReplace("abcxyz", "bcx", "x", false));
|
||||
ASSERT_EQ("abcx", android::base::StringReplace("abcxyz", "xyz", "x", false));
|
||||
}
|
||||
|
||||
TEST(strings, StringReplace_true) {
|
||||
// No change.
|
||||
ASSERT_EQ("abcabc", android::base::StringReplace("abcabc", "z", "Z", true));
|
||||
ASSERT_EQ("", android::base::StringReplace("", "z", "Z", true));
|
||||
ASSERT_EQ("abcabc", android::base::StringReplace("abcabc", "", "Z", true));
|
||||
|
||||
// Equal lengths.
|
||||
ASSERT_EQ("AbcAbc", android::base::StringReplace("abcabc", "a", "A", true));
|
||||
ASSERT_EQ("aBcaBc", android::base::StringReplace("abcabc", "b", "B", true));
|
||||
ASSERT_EQ("abCabC", android::base::StringReplace("abcabc", "c", "C", true));
|
||||
|
||||
// Longer replacement.
|
||||
ASSERT_EQ("foobcfoobc", android::base::StringReplace("abcabc", "a", "foo", true));
|
||||
ASSERT_EQ("afoocafooc", android::base::StringReplace("abcabc", "b", "foo", true));
|
||||
ASSERT_EQ("abfooabfoo", android::base::StringReplace("abcabc", "c", "foo", true));
|
||||
|
||||
// Shorter replacement.
|
||||
ASSERT_EQ("xxyzx", android::base::StringReplace("abcxyzabc", "abc", "x", true));
|
||||
ASSERT_EQ("<xx>", android::base::StringReplace("<abcabc>", "abc", "x", true));
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "android-base/logging.h"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
android::base::InitLogging(argv, android::base::StderrLogger);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "android-base/test_utils.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
|
||||
CapturedStdFd::CapturedStdFd(int std_fd) : std_fd_(std_fd), old_fd_(-1) {
|
||||
Start();
|
||||
}
|
||||
|
||||
CapturedStdFd::~CapturedStdFd() {
|
||||
if (old_fd_ != -1) {
|
||||
Stop();
|
||||
}
|
||||
}
|
||||
|
||||
int CapturedStdFd::fd() const {
|
||||
return temp_file_.fd;
|
||||
}
|
||||
|
||||
std::string CapturedStdFd::str() {
|
||||
std::string result;
|
||||
CHECK_EQ(0, TEMP_FAILURE_RETRY(lseek(fd(), 0, SEEK_SET)));
|
||||
android::base::ReadFdToString(fd(), &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
void CapturedStdFd::Reset() {
|
||||
// Do not reset while capturing.
|
||||
CHECK_EQ(-1, old_fd_);
|
||||
CHECK_EQ(0, TEMP_FAILURE_RETRY(lseek(fd(), 0, SEEK_SET)));
|
||||
CHECK_EQ(0, ftruncate(fd(), 0));
|
||||
}
|
||||
|
||||
void CapturedStdFd::Start() {
|
||||
#if defined(_WIN32)
|
||||
// On Windows, stderr is often buffered, so make sure it is unbuffered so
|
||||
// that we can immediately read back what was written to stderr.
|
||||
if (std_fd_ == STDERR_FILENO) CHECK_EQ(0, setvbuf(stderr, nullptr, _IONBF, 0));
|
||||
#endif
|
||||
old_fd_ = dup(std_fd_);
|
||||
CHECK_NE(-1, old_fd_);
|
||||
CHECK_NE(-1, dup2(fd(), std_fd_));
|
||||
}
|
||||
|
||||
void CapturedStdFd::Stop() {
|
||||
CHECK_NE(-1, old_fd_);
|
||||
CHECK_NE(-1, dup2(old_fd_, std_fd_));
|
||||
close(old_fd_);
|
||||
old_fd_ = -1;
|
||||
// Note: cannot restore prior setvbuf() setting.
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "android-base/test_utils.h"
|
||||
|
||||
#include <gtest/gtest-spi.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
TEST(TestUtilsTest, AssertMatch) {
|
||||
ASSERT_MATCH("foobar", R"(fo+baz?r)");
|
||||
EXPECT_FATAL_FAILURE(ASSERT_MATCH("foobar", R"(foobaz)"), "regex mismatch");
|
||||
}
|
||||
|
||||
TEST(TestUtilsTest, AssertNotMatch) {
|
||||
ASSERT_NOT_MATCH("foobar", R"(foobaz)");
|
||||
EXPECT_FATAL_FAILURE(ASSERT_NOT_MATCH("foobar", R"(foobar)"), "regex mismatch");
|
||||
}
|
||||
|
||||
TEST(TestUtilsTest, ExpectMatch) {
|
||||
EXPECT_MATCH("foobar", R"(fo+baz?r)");
|
||||
EXPECT_NONFATAL_FAILURE(EXPECT_MATCH("foobar", R"(foobaz)"), "regex mismatch");
|
||||
}
|
||||
|
||||
TEST(TestUtilsTest, ExpectNotMatch) {
|
||||
EXPECT_NOT_MATCH("foobar", R"(foobaz)");
|
||||
EXPECT_NONFATAL_FAILURE(EXPECT_NOT_MATCH("foobar", R"(foobar)"), "regex mismatch");
|
||||
}
|
||||
|
||||
TEST(TestUtilsTest, CaptureStdout_smoke) {
|
||||
CapturedStdout cap;
|
||||
printf("This should be captured.\n");
|
||||
cap.Stop();
|
||||
printf("This will not be captured.\n");
|
||||
ASSERT_EQ("This should be captured.\n", cap.str());
|
||||
|
||||
cap.Start();
|
||||
printf("And this text should be captured too.\n");
|
||||
cap.Stop();
|
||||
ASSERT_EQ("This should be captured.\nAnd this text should be captured too.\n", cap.str());
|
||||
|
||||
printf("Still not going to be captured.\n");
|
||||
cap.Reset();
|
||||
cap.Start();
|
||||
printf("Only this will be captured.\n");
|
||||
ASSERT_EQ("Only this will be captured.\n", cap.str());
|
||||
}
|
||||
|
||||
TEST(TestUtilsTest, CaptureStderr_smoke) {
|
||||
CapturedStderr cap;
|
||||
fprintf(stderr, "This should be captured.\n");
|
||||
cap.Stop();
|
||||
fprintf(stderr, "This will not be captured.\n");
|
||||
ASSERT_EQ("This should be captured.\n", cap.str());
|
||||
|
||||
cap.Start();
|
||||
fprintf(stderr, "And this text should be captured too.\n");
|
||||
cap.Stop();
|
||||
ASSERT_EQ("This should be captured.\nAnd this text should be captured too.\n", cap.str());
|
||||
|
||||
fprintf(stderr, "Still not going to be captured.\n");
|
||||
cap.Reset();
|
||||
cap.Start();
|
||||
fprintf(stderr, "Only this will be captured.\n");
|
||||
ASSERT_EQ("Only this will be captured.\n", cap.str());
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <android-base/threads.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <pthread.h>
|
||||
#elif defined(__linux__) && !defined(__ANDROID__)
|
||||
#include <syscall.h>
|
||||
#elif defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
uint64_t GetThreadId() {
|
||||
#if defined(__BIONIC__)
|
||||
return gettid();
|
||||
#elif defined(__APPLE__)
|
||||
uint64_t tid;
|
||||
pthread_threadid_np(NULL, &tid);
|
||||
return tid;
|
||||
#elif defined(__linux__)
|
||||
return syscall(__NR_gettid);
|
||||
#elif defined(_WIN32)
|
||||
return GetCurrentThreadId();
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
||||
|
||||
#if defined(__GLIBC__)
|
||||
int tgkill(int tgid, int tid, int sig) {
|
||||
return syscall(__NR_tgkill, tgid, tid, sig);
|
||||
}
|
||||
#endif
|
|
@ -1,32 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "android-base/unique_fd.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
extern void consume_unique_fd(android::base::unique_fd fd);
|
||||
|
||||
TEST(unique_fd, bugprone_use_after_move) {
|
||||
// Compile time test for clang-tidy's bugprone-use-after-move check.
|
||||
android::base::unique_fd ufd(open("/dev/null", O_RDONLY | O_CLOEXEC));
|
||||
consume_unique_fd(std::move(ufd));
|
||||
ufd.reset(open("/dev/null", O_RDONLY | O_CLOEXEC));
|
||||
ufd.get();
|
||||
consume_unique_fd(std::move(ufd));
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "android-base/unique_fd.h"
|
||||
|
||||
void consume_unique_fd(android::base::unique_fd) {}
|
235
base/utf8.cpp
235
base/utf8.cpp
|
@ -1,235 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "android-base/utf8.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
#include "android-base/logging.h"
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
// Helper to set errno based on GetLastError() after WideCharToMultiByte()/MultiByteToWideChar().
|
||||
static void SetErrnoFromLastError() {
|
||||
switch (GetLastError()) {
|
||||
case ERROR_NO_UNICODE_TRANSLATION:
|
||||
errno = EILSEQ;
|
||||
break;
|
||||
default:
|
||||
errno = EINVAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool WideToUTF8(const wchar_t* utf16, const size_t size, std::string* utf8) {
|
||||
utf8->clear();
|
||||
|
||||
if (size == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: Consider using std::wstring_convert once libcxx is supported on
|
||||
// Windows.
|
||||
|
||||
// Only Vista or later has this flag that causes WideCharToMultiByte() to
|
||||
// return an error on invalid characters.
|
||||
const DWORD flags =
|
||||
#if (WINVER >= 0x0600)
|
||||
WC_ERR_INVALID_CHARS;
|
||||
#else
|
||||
0;
|
||||
#endif
|
||||
|
||||
const int chars_required = WideCharToMultiByte(CP_UTF8, flags, utf16, size,
|
||||
NULL, 0, NULL, NULL);
|
||||
if (chars_required <= 0) {
|
||||
SetErrnoFromLastError();
|
||||
return false;
|
||||
}
|
||||
|
||||
// This could potentially throw a std::bad_alloc exception.
|
||||
utf8->resize(chars_required);
|
||||
|
||||
const int result = WideCharToMultiByte(CP_UTF8, flags, utf16, size,
|
||||
&(*utf8)[0], chars_required, NULL,
|
||||
NULL);
|
||||
if (result != chars_required) {
|
||||
SetErrnoFromLastError();
|
||||
CHECK_LE(result, chars_required) << "WideCharToMultiByte wrote " << result
|
||||
<< " chars to buffer of " << chars_required << " chars";
|
||||
utf8->clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WideToUTF8(const wchar_t* utf16, std::string* utf8) {
|
||||
// Compute string length of NULL-terminated string with wcslen().
|
||||
return WideToUTF8(utf16, wcslen(utf16), utf8);
|
||||
}
|
||||
|
||||
bool WideToUTF8(const std::wstring& utf16, std::string* utf8) {
|
||||
// Use the stored length of the string which allows embedded NULL characters
|
||||
// to be converted.
|
||||
return WideToUTF8(utf16.c_str(), utf16.length(), utf8);
|
||||
}
|
||||
|
||||
// Internal helper function that takes MultiByteToWideChar() flags.
|
||||
static bool UTF8ToWideWithFlags(const char* utf8, const size_t size, std::wstring* utf16,
|
||||
const DWORD flags) {
|
||||
utf16->clear();
|
||||
|
||||
if (size == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: Consider using std::wstring_convert once libcxx is supported on
|
||||
// Windows.
|
||||
const int chars_required = MultiByteToWideChar(CP_UTF8, flags, utf8, size,
|
||||
NULL, 0);
|
||||
if (chars_required <= 0) {
|
||||
SetErrnoFromLastError();
|
||||
return false;
|
||||
}
|
||||
|
||||
// This could potentially throw a std::bad_alloc exception.
|
||||
utf16->resize(chars_required);
|
||||
|
||||
const int result = MultiByteToWideChar(CP_UTF8, flags, utf8, size,
|
||||
&(*utf16)[0], chars_required);
|
||||
if (result != chars_required) {
|
||||
SetErrnoFromLastError();
|
||||
CHECK_LE(result, chars_required) << "MultiByteToWideChar wrote " << result
|
||||
<< " chars to buffer of " << chars_required << " chars";
|
||||
utf16->clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UTF8ToWide(const char* utf8, const size_t size, std::wstring* utf16) {
|
||||
// If strictly interpreting as UTF-8 succeeds, return success.
|
||||
if (UTF8ToWideWithFlags(utf8, size, utf16, MB_ERR_INVALID_CHARS)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const int saved_errno = errno;
|
||||
|
||||
// Fallback to non-strict interpretation, allowing invalid characters and
|
||||
// converting as best as possible, and return false to signify a problem.
|
||||
(void)UTF8ToWideWithFlags(utf8, size, utf16, 0);
|
||||
errno = saved_errno;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UTF8ToWide(const char* utf8, std::wstring* utf16) {
|
||||
// Compute string length of NULL-terminated string with strlen().
|
||||
return UTF8ToWide(utf8, strlen(utf8), utf16);
|
||||
}
|
||||
|
||||
bool UTF8ToWide(const std::string& utf8, std::wstring* utf16) {
|
||||
// Use the stored length of the string which allows embedded NULL characters
|
||||
// to be converted.
|
||||
return UTF8ToWide(utf8.c_str(), utf8.length(), utf16);
|
||||
}
|
||||
|
||||
static bool isDriveLetter(wchar_t c) {
|
||||
return (c >= L'a' && c <= L'z') || (c >= L'A' && c <= L'Z');
|
||||
}
|
||||
|
||||
bool UTF8PathToWindowsLongPath(const char* utf8, std::wstring* utf16) {
|
||||
if (!UTF8ToWide(utf8, utf16)) {
|
||||
return false;
|
||||
}
|
||||
// Note: Although most Win32 File I/O API are limited to MAX_PATH (260
|
||||
// characters), the CreateDirectory API is limited to 248 characters.
|
||||
if (utf16->length() >= 248) {
|
||||
// If path is of the form "x:\" or "x:/"
|
||||
if (isDriveLetter((*utf16)[0]) && (*utf16)[1] == L':' &&
|
||||
((*utf16)[2] == L'\\' || (*utf16)[2] == L'/')) {
|
||||
// Append long path prefix, and make sure there are no unix-style
|
||||
// separators to ensure a fully compliant Win32 long path string.
|
||||
utf16->insert(0, LR"(\\?\)");
|
||||
std::replace(utf16->begin(), utf16->end(), L'/', L'\\');
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Versions of standard library APIs that support UTF-8 strings.
|
||||
namespace utf8 {
|
||||
|
||||
FILE* fopen(const char* name, const char* mode) {
|
||||
std::wstring name_utf16;
|
||||
if (!UTF8PathToWindowsLongPath(name, &name_utf16)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::wstring mode_utf16;
|
||||
if (!UTF8ToWide(mode, &mode_utf16)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return _wfopen(name_utf16.c_str(), mode_utf16.c_str());
|
||||
}
|
||||
|
||||
int mkdir(const char* name, mode_t) {
|
||||
std::wstring name_utf16;
|
||||
if (!UTF8PathToWindowsLongPath(name, &name_utf16)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return _wmkdir(name_utf16.c_str());
|
||||
}
|
||||
|
||||
int open(const char* name, int flags, ...) {
|
||||
std::wstring name_utf16;
|
||||
if (!UTF8PathToWindowsLongPath(name, &name_utf16)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int mode = 0;
|
||||
if ((flags & O_CREAT) != 0) {
|
||||
va_list args;
|
||||
va_start(args, flags);
|
||||
mode = va_arg(args, int);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
return _wopen(name_utf16.c_str(), flags, mode);
|
||||
}
|
||||
|
||||
int unlink(const char* name) {
|
||||
std::wstring name_utf16;
|
||||
if (!UTF8PathToWindowsLongPath(name, &name_utf16)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return _wunlink(name_utf16.c_str());
|
||||
}
|
||||
|
||||
} // namespace utf8
|
||||
} // namespace base
|
||||
} // namespace android
|
|
@ -1,488 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "android-base/utf8.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "android-base/file.h"
|
||||
#include "android-base/macros.h"
|
||||
#include "android-base/unique_fd.h"
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
TEST(UTFStringConversionsTest, ConvertInvalidUTF8) {
|
||||
std::wstring wide;
|
||||
|
||||
errno = 0;
|
||||
|
||||
// Standalone \xa2 is an invalid UTF-8 sequence, so this should return an
|
||||
// error. Concatenate two C/C++ literal string constants to prevent the
|
||||
// compiler from giving an error about "\xa2af" containing a "hex escape
|
||||
// sequence out of range".
|
||||
EXPECT_FALSE(android::base::UTF8ToWide("before\xa2" "after", &wide));
|
||||
|
||||
EXPECT_EQ(EILSEQ, errno);
|
||||
|
||||
// Even if an invalid character is encountered, UTF8ToWide() should still do
|
||||
// its best to convert the rest of the string. sysdeps_win32.cpp:
|
||||
// _console_write_utf8() depends on this behavior.
|
||||
//
|
||||
// Thus, we verify that the valid characters are converted, but we ignore the
|
||||
// specific replacement character that UTF8ToWide() may replace the invalid
|
||||
// UTF-8 characters with because we want to allow that to change if the
|
||||
// implementation changes.
|
||||
EXPECT_EQ(0U, wide.find(L"before"));
|
||||
const wchar_t after_wide[] = L"after";
|
||||
EXPECT_EQ(wide.length() - (arraysize(after_wide) - 1), wide.find(after_wide));
|
||||
}
|
||||
|
||||
// Below is adapted from https://chromium.googlesource.com/chromium/src/+/master/base/strings/utf_string_conversions_unittest.cc
|
||||
|
||||
// Copyright (c) 2010 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.
|
||||
|
||||
// The tests below from utf_string_conversions_unittest.cc check for this
|
||||
// preprocessor symbol, so define it, as it is appropriate for Windows.
|
||||
#define WCHAR_T_IS_UTF16
|
||||
static_assert(sizeof(wchar_t) == 2, "wchar_t is not 2 bytes");
|
||||
|
||||
// The tests below from utf_string_conversions_unittest.cc call versions of
|
||||
// UTF8ToWide() and WideToUTF8() that don't return success/failure, so these are
|
||||
// stub implementations with that signature. These are just for testing and
|
||||
// should not be moved to base because they assert/expect no errors which is
|
||||
// probably not a good idea (or at least it is something that should be left
|
||||
// up to the caller, not a base library).
|
||||
|
||||
static std::wstring UTF8ToWide(const std::string& utf8) {
|
||||
std::wstring utf16;
|
||||
EXPECT_TRUE(UTF8ToWide(utf8, &utf16));
|
||||
return utf16;
|
||||
}
|
||||
|
||||
static std::string WideToUTF8(const std::wstring& utf16) {
|
||||
std::string utf8;
|
||||
EXPECT_TRUE(WideToUTF8(utf16, &utf8));
|
||||
return utf8;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
const wchar_t* const kConvertRoundtripCases[] = {
|
||||
L"Google Video",
|
||||
// "网页 图片 资讯更多 »"
|
||||
L"\x7f51\x9875\x0020\x56fe\x7247\x0020\x8d44\x8baf\x66f4\x591a\x0020\x00bb",
|
||||
// "Παγκόσμιος Ιστός"
|
||||
L"\x03a0\x03b1\x03b3\x03ba\x03cc\x03c3\x03bc\x03b9"
|
||||
L"\x03bf\x03c2\x0020\x0399\x03c3\x03c4\x03cc\x03c2",
|
||||
// "Поиск страниц на русском"
|
||||
L"\x041f\x043e\x0438\x0441\x043a\x0020\x0441\x0442"
|
||||
L"\x0440\x0430\x043d\x0438\x0446\x0020\x043d\x0430"
|
||||
L"\x0020\x0440\x0443\x0441\x0441\x043a\x043e\x043c",
|
||||
// "전체서비스"
|
||||
L"\xc804\xccb4\xc11c\xbe44\xc2a4",
|
||||
|
||||
// Test characters that take more than 16 bits. This will depend on whether
|
||||
// wchar_t is 16 or 32 bits.
|
||||
#if defined(WCHAR_T_IS_UTF16)
|
||||
L"\xd800\xdf00",
|
||||
// ????? (Mathematical Alphanumeric Symbols (U+011d40 - U+011d44 : A,B,C,D,E)
|
||||
L"\xd807\xdd40\xd807\xdd41\xd807\xdd42\xd807\xdd43\xd807\xdd44",
|
||||
#elif defined(WCHAR_T_IS_UTF32)
|
||||
L"\x10300",
|
||||
// ????? (Mathematical Alphanumeric Symbols (U+011d40 - U+011d44 : A,B,C,D,E)
|
||||
L"\x11d40\x11d41\x11d42\x11d43\x11d44",
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(UTFStringConversionsTest, ConvertUTF8AndWide) {
|
||||
// we round-trip all the wide strings through UTF-8 to make sure everything
|
||||
// agrees on the conversion. This uses the stream operators to test them
|
||||
// simultaneously.
|
||||
for (size_t i = 0; i < arraysize(kConvertRoundtripCases); ++i) {
|
||||
std::ostringstream utf8;
|
||||
utf8 << WideToUTF8(kConvertRoundtripCases[i]);
|
||||
std::wostringstream wide;
|
||||
wide << UTF8ToWide(utf8.str());
|
||||
|
||||
EXPECT_EQ(kConvertRoundtripCases[i], wide.str());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(UTFStringConversionsTest, ConvertUTF8AndWideEmptyString) {
|
||||
// An empty std::wstring should be converted to an empty std::string,
|
||||
// and vice versa.
|
||||
std::wstring wempty;
|
||||
std::string empty;
|
||||
EXPECT_EQ(empty, WideToUTF8(wempty));
|
||||
EXPECT_EQ(wempty, UTF8ToWide(empty));
|
||||
}
|
||||
|
||||
TEST(UTFStringConversionsTest, ConvertUTF8ToWide) {
|
||||
struct UTF8ToWideCase {
|
||||
const char* utf8;
|
||||
const wchar_t* wide;
|
||||
bool success;
|
||||
} convert_cases[] = {
|
||||
// Regular UTF-8 input.
|
||||
{"\xe4\xbd\xa0\xe5\xa5\xbd", L"\x4f60\x597d", true},
|
||||
// Non-character is passed through.
|
||||
{"\xef\xbf\xbfHello", L"\xffffHello", true},
|
||||
// Truncated UTF-8 sequence.
|
||||
{"\xe4\xa0\xe5\xa5\xbd", L"\xfffd\x597d", false},
|
||||
// Truncated off the end.
|
||||
{"\xe5\xa5\xbd\xe4\xa0", L"\x597d\xfffd", false},
|
||||
// Non-shortest-form UTF-8.
|
||||
{"\xf0\x84\xbd\xa0\xe5\xa5\xbd", L"\xfffd\x597d", false},
|
||||
// This UTF-8 character decodes to a UTF-16 surrogate, which is illegal.
|
||||
// Note that for whatever reason, this test fails on Windows XP.
|
||||
{"\xed\xb0\x80", L"\xfffd", false},
|
||||
// Non-BMP characters. The second is a non-character regarded as valid.
|
||||
// The result will either be in UTF-16 or UTF-32.
|
||||
#if defined(WCHAR_T_IS_UTF16)
|
||||
{"A\xF0\x90\x8C\x80z", L"A\xd800\xdf00z", true},
|
||||
{"A\xF4\x8F\xBF\xBEz", L"A\xdbff\xdffez", true},
|
||||
#elif defined(WCHAR_T_IS_UTF32)
|
||||
{"A\xF0\x90\x8C\x80z", L"A\x10300z", true},
|
||||
{"A\xF4\x8F\xBF\xBEz", L"A\x10fffez", true},
|
||||
#endif
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < arraysize(convert_cases); i++) {
|
||||
std::wstring converted;
|
||||
errno = 0;
|
||||
const bool success = UTF8ToWide(convert_cases[i].utf8,
|
||||
strlen(convert_cases[i].utf8),
|
||||
&converted);
|
||||
EXPECT_EQ(convert_cases[i].success, success);
|
||||
// The original test always compared expected and converted, but don't do
|
||||
// that because our implementation of UTF8ToWide() does not guarantee to
|
||||
// produce the same output in error situations.
|
||||
if (success) {
|
||||
std::wstring expected(convert_cases[i].wide);
|
||||
EXPECT_EQ(expected, converted);
|
||||
} else {
|
||||
EXPECT_EQ(EILSEQ, errno);
|
||||
}
|
||||
}
|
||||
|
||||
// Manually test an embedded NULL.
|
||||
std::wstring converted;
|
||||
EXPECT_TRUE(UTF8ToWide("\00Z\t", 3, &converted));
|
||||
ASSERT_EQ(3U, converted.length());
|
||||
EXPECT_EQ(static_cast<wchar_t>(0), converted[0]);
|
||||
EXPECT_EQ('Z', converted[1]);
|
||||
EXPECT_EQ('\t', converted[2]);
|
||||
|
||||
// Make sure that conversion replaces, not appends.
|
||||
EXPECT_TRUE(UTF8ToWide("B", 1, &converted));
|
||||
ASSERT_EQ(1U, converted.length());
|
||||
EXPECT_EQ('B', converted[0]);
|
||||
}
|
||||
|
||||
#if defined(WCHAR_T_IS_UTF16)
|
||||
// This test is only valid when wchar_t == UTF-16.
|
||||
TEST(UTFStringConversionsTest, ConvertUTF16ToUTF8) {
|
||||
struct WideToUTF8Case {
|
||||
const wchar_t* utf16;
|
||||
const char* utf8;
|
||||
bool success;
|
||||
} convert_cases[] = {
|
||||
// Regular UTF-16 input.
|
||||
{L"\x4f60\x597d", "\xe4\xbd\xa0\xe5\xa5\xbd", true},
|
||||
// Test a non-BMP character.
|
||||
{L"\xd800\xdf00", "\xF0\x90\x8C\x80", true},
|
||||
// Non-characters are passed through.
|
||||
{L"\xffffHello", "\xEF\xBF\xBFHello", true},
|
||||
{L"\xdbff\xdffeHello", "\xF4\x8F\xBF\xBEHello", true},
|
||||
// The first character is a truncated UTF-16 character.
|
||||
// Note that for whatever reason, this test fails on Windows XP.
|
||||
{L"\xd800\x597d", "\xef\xbf\xbd\xe5\xa5\xbd",
|
||||
#if (WINVER >= 0x0600)
|
||||
// Only Vista and later has a new API/flag that correctly returns false.
|
||||
false
|
||||
#else
|
||||
true
|
||||
#endif
|
||||
},
|
||||
// Truncated at the end.
|
||||
// Note that for whatever reason, this test fails on Windows XP.
|
||||
{L"\x597d\xd800", "\xe5\xa5\xbd\xef\xbf\xbd",
|
||||
#if (WINVER >= 0x0600)
|
||||
// Only Vista and later has a new API/flag that correctly returns false.
|
||||
false
|
||||
#else
|
||||
true
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < arraysize(convert_cases); i++) {
|
||||
std::string converted;
|
||||
errno = 0;
|
||||
const bool success = WideToUTF8(convert_cases[i].utf16,
|
||||
wcslen(convert_cases[i].utf16),
|
||||
&converted);
|
||||
EXPECT_EQ(convert_cases[i].success, success);
|
||||
// The original test always compared expected and converted, but don't do
|
||||
// that because our implementation of WideToUTF8() does not guarantee to
|
||||
// produce the same output in error situations.
|
||||
if (success) {
|
||||
std::string expected(convert_cases[i].utf8);
|
||||
EXPECT_EQ(expected, converted);
|
||||
} else {
|
||||
EXPECT_EQ(EILSEQ, errno);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(WCHAR_T_IS_UTF32)
|
||||
// This test is only valid when wchar_t == UTF-32.
|
||||
TEST(UTFStringConversionsTest, ConvertUTF32ToUTF8) {
|
||||
struct WideToUTF8Case {
|
||||
const wchar_t* utf32;
|
||||
const char* utf8;
|
||||
bool success;
|
||||
} convert_cases[] = {
|
||||
// Regular 16-bit input.
|
||||
{L"\x4f60\x597d", "\xe4\xbd\xa0\xe5\xa5\xbd", true},
|
||||
// Test a non-BMP character.
|
||||
{L"A\x10300z", "A\xF0\x90\x8C\x80z", true},
|
||||
// Non-characters are passed through.
|
||||
{L"\xffffHello", "\xEF\xBF\xBFHello", true},
|
||||
{L"\x10fffeHello", "\xF4\x8F\xBF\xBEHello", true},
|
||||
// Invalid Unicode code points.
|
||||
{L"\xfffffffHello", "\xEF\xBF\xBDHello", false},
|
||||
// The first character is a truncated UTF-16 character.
|
||||
{L"\xd800\x597d", "\xef\xbf\xbd\xe5\xa5\xbd", false},
|
||||
{L"\xdc01Hello", "\xef\xbf\xbdHello", false},
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < arraysize(convert_cases); i++) {
|
||||
std::string converted;
|
||||
EXPECT_EQ(convert_cases[i].success,
|
||||
WideToUTF8(convert_cases[i].utf32,
|
||||
wcslen(convert_cases[i].utf32),
|
||||
&converted));
|
||||
std::string expected(convert_cases[i].utf8);
|
||||
EXPECT_EQ(expected, converted);
|
||||
}
|
||||
}
|
||||
#endif // defined(WCHAR_T_IS_UTF32)
|
||||
|
||||
// The test below uses these types and functions, so just do enough to get the
|
||||
// test running.
|
||||
typedef wchar_t char16;
|
||||
typedef std::wstring string16;
|
||||
|
||||
template<typename T>
|
||||
static void* WriteInto(T* t, size_t size) {
|
||||
// std::(w)string::resize() already includes space for a NULL terminator.
|
||||
t->resize(size - 1);
|
||||
return &(*t)[0];
|
||||
}
|
||||
|
||||
// A stub implementation that calls a helper from above, just to get the test
|
||||
// below working. This is just for testing and should not be moved to base
|
||||
// because this ignores errors which is probably not a good idea, plus it takes
|
||||
// a string16 type which we don't really have.
|
||||
static std::string UTF16ToUTF8(const string16& utf16) {
|
||||
return WideToUTF8(utf16);
|
||||
}
|
||||
|
||||
TEST(UTFStringConversionsTest, ConvertMultiString) {
|
||||
static char16 multi16[] = {
|
||||
'f', 'o', 'o', '\0',
|
||||
'b', 'a', 'r', '\0',
|
||||
'b', 'a', 'z', '\0',
|
||||
'\0'
|
||||
};
|
||||
static char multi[] = {
|
||||
'f', 'o', 'o', '\0',
|
||||
'b', 'a', 'r', '\0',
|
||||
'b', 'a', 'z', '\0',
|
||||
'\0'
|
||||
};
|
||||
string16 multistring16;
|
||||
memcpy(WriteInto(&multistring16, arraysize(multi16)), multi16,
|
||||
sizeof(multi16));
|
||||
EXPECT_EQ(arraysize(multi16) - 1, multistring16.length());
|
||||
std::string expected;
|
||||
memcpy(WriteInto(&expected, arraysize(multi)), multi, sizeof(multi));
|
||||
EXPECT_EQ(arraysize(multi) - 1, expected.length());
|
||||
const std::string& converted = UTF16ToUTF8(multistring16);
|
||||
EXPECT_EQ(arraysize(multi) - 1, converted.length());
|
||||
EXPECT_EQ(expected, converted);
|
||||
}
|
||||
|
||||
// The tests below from sys_string_conversions_unittest.cc call SysWideToUTF8()
|
||||
// and SysUTF8ToWide(), so these are stub implementations that call the helpers
|
||||
// above. These are just for testing and should not be moved to base because
|
||||
// they ignore errors which is probably not a good idea.
|
||||
|
||||
static std::string SysWideToUTF8(const std::wstring& utf16) {
|
||||
return WideToUTF8(utf16);
|
||||
}
|
||||
|
||||
static std::wstring SysUTF8ToWide(const std::string& utf8) {
|
||||
return UTF8ToWide(utf8);
|
||||
}
|
||||
|
||||
// Below is adapted from https://chromium.googlesource.com/chromium/src/+/master/base/strings/sys_string_conversions_unittest.cc
|
||||
|
||||
// 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.
|
||||
|
||||
#ifdef WCHAR_T_IS_UTF32
|
||||
static const std::wstring kSysWideOldItalicLetterA = L"\x10300";
|
||||
#else
|
||||
static const std::wstring kSysWideOldItalicLetterA = L"\xd800\xdf00";
|
||||
#endif
|
||||
|
||||
TEST(SysStrings, SysWideToUTF8) {
|
||||
EXPECT_EQ("Hello, world", SysWideToUTF8(L"Hello, world"));
|
||||
EXPECT_EQ("\xe4\xbd\xa0\xe5\xa5\xbd", SysWideToUTF8(L"\x4f60\x597d"));
|
||||
|
||||
// >16 bits
|
||||
EXPECT_EQ("\xF0\x90\x8C\x80", SysWideToUTF8(kSysWideOldItalicLetterA));
|
||||
|
||||
// Error case. When Windows finds a UTF-16 character going off the end of
|
||||
// a string, it just converts that literal value to UTF-8, even though this
|
||||
// is invalid.
|
||||
//
|
||||
// This is what XP does, but Vista has different behavior, so we don't bother
|
||||
// verifying it:
|
||||
// EXPECT_EQ("\xE4\xBD\xA0\xED\xA0\x80zyxw",
|
||||
// SysWideToUTF8(L"\x4f60\xd800zyxw"));
|
||||
|
||||
// Test embedded NULLs.
|
||||
std::wstring wide_null(L"a");
|
||||
wide_null.push_back(0);
|
||||
wide_null.push_back('b');
|
||||
|
||||
std::string expected_null("a");
|
||||
expected_null.push_back(0);
|
||||
expected_null.push_back('b');
|
||||
|
||||
EXPECT_EQ(expected_null, SysWideToUTF8(wide_null));
|
||||
}
|
||||
|
||||
TEST(SysStrings, SysUTF8ToWide) {
|
||||
EXPECT_EQ(L"Hello, world", SysUTF8ToWide("Hello, world"));
|
||||
EXPECT_EQ(L"\x4f60\x597d", SysUTF8ToWide("\xe4\xbd\xa0\xe5\xa5\xbd"));
|
||||
// >16 bits
|
||||
EXPECT_EQ(kSysWideOldItalicLetterA, SysUTF8ToWide("\xF0\x90\x8C\x80"));
|
||||
|
||||
// Error case. When Windows finds an invalid UTF-8 character, it just skips
|
||||
// it. This seems weird because it's inconsistent with the reverse conversion.
|
||||
//
|
||||
// This is what XP does, but Vista has different behavior, so we don't bother
|
||||
// verifying it:
|
||||
// EXPECT_EQ(L"\x4f60zyxw", SysUTF8ToWide("\xe4\xbd\xa0\xe5\xa5zyxw"));
|
||||
|
||||
// Test embedded NULLs.
|
||||
std::string utf8_null("a");
|
||||
utf8_null.push_back(0);
|
||||
utf8_null.push_back('b');
|
||||
|
||||
std::wstring expected_null(L"a");
|
||||
expected_null.push_back(0);
|
||||
expected_null.push_back('b');
|
||||
|
||||
EXPECT_EQ(expected_null, SysUTF8ToWide(utf8_null));
|
||||
}
|
||||
|
||||
TEST(UTF8PathToWindowsLongPathTest, DontAddPrefixIfShorterThanMaxPath) {
|
||||
std::string utf8 = "c:\\mypath\\myfile.txt";
|
||||
|
||||
std::wstring wide;
|
||||
EXPECT_TRUE(UTF8PathToWindowsLongPath(utf8.c_str(), &wide));
|
||||
|
||||
EXPECT_EQ(std::string::npos, wide.find(LR"(\\?\)"));
|
||||
}
|
||||
|
||||
TEST(UTF8PathToWindowsLongPathTest, AddPrefixIfLongerThanMaxPath) {
|
||||
std::string utf8 = "c:\\mypath";
|
||||
while (utf8.length() < 300 /* MAX_PATH is 260 */) {
|
||||
utf8 += "\\mypathsegment";
|
||||
}
|
||||
|
||||
std::wstring wide;
|
||||
EXPECT_TRUE(UTF8PathToWindowsLongPath(utf8.c_str(), &wide));
|
||||
|
||||
EXPECT_EQ(0U, wide.find(LR"(\\?\)"));
|
||||
EXPECT_EQ(std::string::npos, wide.find(L"/"));
|
||||
}
|
||||
|
||||
TEST(UTF8PathToWindowsLongPathTest, AddPrefixAndFixSeparatorsIfLongerThanMaxPath) {
|
||||
std::string utf8 = "c:/mypath";
|
||||
while (utf8.length() < 300 /* MAX_PATH is 260 */) {
|
||||
utf8 += "/mypathsegment";
|
||||
}
|
||||
|
||||
std::wstring wide;
|
||||
EXPECT_TRUE(UTF8PathToWindowsLongPath(utf8.c_str(), &wide));
|
||||
|
||||
EXPECT_EQ(0U, wide.find(LR"(\\?\)"));
|
||||
EXPECT_EQ(std::string::npos, wide.find(L"/"));
|
||||
}
|
||||
|
||||
namespace utf8 {
|
||||
|
||||
TEST(Utf8FilesTest, CanCreateOpenAndDeleteFileWithLongPath) {
|
||||
TemporaryDir td;
|
||||
|
||||
// Create long directory path
|
||||
std::string utf8 = td.path;
|
||||
while (utf8.length() < 300 /* MAX_PATH is 260 */) {
|
||||
utf8 += "\\mypathsegment";
|
||||
EXPECT_EQ(0, mkdir(utf8.c_str(), 0));
|
||||
}
|
||||
|
||||
// Create file
|
||||
utf8 += "\\test-file.bin";
|
||||
int flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
|
||||
int mode = 0666;
|
||||
android::base::unique_fd fd(open(utf8.c_str(), flags, mode));
|
||||
EXPECT_NE(-1, fd.get());
|
||||
|
||||
// Close file
|
||||
fd.reset();
|
||||
EXPECT_EQ(-1, fd.get());
|
||||
|
||||
// Open file with fopen
|
||||
FILE* file = fopen(utf8.c_str(), "rb");
|
||||
EXPECT_NE(nullptr, file);
|
||||
|
||||
if (file) {
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
// Delete file
|
||||
EXPECT_EQ(0, unlink(utf8.c_str()));
|
||||
}
|
||||
|
||||
} // namespace utf8
|
||||
} // namespace base
|
||||
} // namespace android
|
Loading…
Reference in New Issue