Merge remote-tracking branch 'origin/upstream/master' into 'main'
Change-Id: Ife29631ee9cc2631f528383b85bbca1811f77200
diff --git a/Android.bp b/Android.bp
index ef56692..d2cdadd 100644
--- a/Android.bp
+++ b/Android.bp
@@ -283,7 +283,11 @@
name: "libbase_benchmark",
defaults: ["libbase_cflags_defaults"],
- srcs: ["format_benchmark.cpp"],
+ srcs: [
+ "file_benchmark.cpp",
+ "format_benchmark.cpp",
+ "function_ref_benchmark.cpp",
+ ],
shared_libs: ["libbase"],
compile_multilib: "both",
diff --git a/abi_compatibility.cpp b/abi_compatibility.cpp
index 06a7801..b5e4741 100644
--- a/abi_compatibility.cpp
+++ b/abi_compatibility.cpp
@@ -46,7 +46,11 @@
}
bool WriteStringToFd(const std::string& content, int fd) {
- return WriteStringToFd(content, borrowed_fd(fd));
+ return WriteStringToFd(std::string_view(content), borrowed_fd(fd));
+}
+
+bool WriteStringToFd(const std::string& content, borrowed_fd fd) {
+ return WriteStringToFd(std::string_view(content), fd);
}
bool ReadFully(int fd, void* data, size_t byte_count) {
@@ -61,6 +65,14 @@
return WriteFully(borrowed_fd(fd), data, byte_count);
}
+std::string Basename(const std::string& path) {
+ return Basename(std::string_view(path));
+}
+
+std::string Dirname(const std::string& path) {
+ return Dirname(std::string_view(path));
+}
+
#if defined(__LP64__)
#define MAPPEDFILE_FROMFD _ZN10MappedFile6FromFdEilmi
#else
diff --git a/file.cpp b/file.cpp
index 0a06712..d06336a 100644
--- a/file.cpp
+++ b/file.cpp
@@ -226,7 +226,7 @@
content->reserve(sb.st_size);
}
- char buf[BUFSIZ] __attribute__((__uninitialized__));
+ char buf[4096] __attribute__((__uninitialized__));
ssize_t n;
while ((n = TEMP_FAILURE_RETRY(read(fd.get(), &buf[0], sizeof(buf)))) > 0) {
content->append(buf, n);
@@ -245,7 +245,7 @@
return ReadFdToString(fd, content);
}
-bool WriteStringToFd(const std::string& content, borrowed_fd fd) {
+bool WriteStringToFd(std::string_view content, borrowed_fd fd) {
const char* p = content.data();
size_t left = content.size();
while (left > 0) {
@@ -337,6 +337,21 @@
}
return static_cast<ssize_t>(bytes_read);
}
+
+static ssize_t pwrite(borrowed_fd fd, const void* data, size_t byte_count, off64_t offset) {
+ DWORD bytes_written;
+ OVERLAPPED overlapped;
+ memset(&overlapped, 0, sizeof(OVERLAPPED));
+ overlapped.Offset = static_cast<DWORD>(offset);
+ overlapped.OffsetHigh = static_cast<DWORD>(offset >> 32);
+ if (!WriteFile(reinterpret_cast<HANDLE>(_get_osfhandle(fd.get())), data,
+ static_cast<DWORD>(byte_count), &bytes_written, &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_written);
+}
#endif
bool ReadFullyAtOffset(borrowed_fd fd, void* data, size_t byte_count, off64_t offset) {
@@ -351,6 +366,19 @@
return true;
}
+bool WriteFullyAtOffset(borrowed_fd fd, const void* data, size_t byte_count, off64_t offset) {
+ const uint8_t* p = reinterpret_cast<const uint8_t*>(data);
+ size_t remaining = byte_count;
+ while (remaining > 0) {
+ ssize_t n = TEMP_FAILURE_RETRY(pwrite(fd.get(), p, remaining, offset));
+ if (n == -1) return false;
+ p += n;
+ remaining -= 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;
@@ -472,7 +500,7 @@
}
#if defined(_WIN32)
-std::string Basename(const std::string& path) {
+std::string Basename(std::string_view path) {
// TODO: how much of this is actually necessary for mingw?
// Copy path because basename may modify the string passed in.
@@ -498,21 +526,21 @@
}
#else
// Copied from bionic so that Basename() below can be portable and thread-safe.
-static int __basename_r(const char* path, char* buffer, size_t buffer_size) {
+static int _basename_r(const char* path, size_t path_size, char* buffer, size_t buffer_size) {
const char* startp = nullptr;
const char* endp = nullptr;
int len;
int result;
// Empty or NULL string gets treated as ".".
- if (path == nullptr || *path == '\0') {
+ if (path == nullptr || path_size == 0) {
startp = ".";
len = 1;
goto Exit;
}
// Strip trailing slashes.
- endp = path + strlen(path) - 1;
+ endp = path + path_size - 1;
while (endp > path && *endp == '/') {
endp--;
}
@@ -549,15 +577,15 @@
}
return result;
}
-std::string Basename(const std::string& path) {
- char buf[PATH_MAX];
- __basename_r(path.c_str(), buf, sizeof(buf));
- return buf;
+std::string Basename(std::string_view path) {
+ char buf[PATH_MAX] __attribute__((__uninitialized__));
+ const auto size = _basename_r(path.data(), path.size(), buf, sizeof(buf));
+ return size > 0 ? std::string(buf, size) : std::string();
}
#endif
#if defined(_WIN32)
-std::string Dirname(const std::string& path) {
+std::string Dirname(std::string_view path) {
// TODO: how much of this is actually necessary for mingw?
// Copy path because dirname may modify the string passed in.
@@ -583,20 +611,20 @@
}
#else
// Copied from bionic so that Dirname() below can be portable and thread-safe.
-static int __dirname_r(const char* path, char* buffer, size_t buffer_size) {
+static int _dirname_r(const char* path, size_t path_size, char* buffer, size_t buffer_size) {
const char* endp = nullptr;
int len;
int result;
// Empty or NULL string gets treated as ".".
- if (path == nullptr || *path == '\0') {
+ if (path == nullptr || path_size == 0) {
path = ".";
len = 1;
goto Exit;
}
// Strip trailing slashes.
- endp = path + strlen(path) - 1;
+ endp = path + path_size - 1;
while (endp > path && *endp == '/') {
endp--;
}
@@ -641,10 +669,10 @@
}
return result;
}
-std::string Dirname(const std::string& path) {
- char buf[PATH_MAX];
- __dirname_r(path.c_str(), buf, sizeof(buf));
- return buf;
+std::string Dirname(std::string_view path) {
+ char buf[PATH_MAX] __attribute__((__uninitialized__));
+ const auto size = _dirname_r(path.data(), path.size(), buf, sizeof(buf));
+ return size > 0 ? std::string(buf, size) : std::string();
}
#endif
diff --git a/file_benchmark.cpp b/file_benchmark.cpp
new file mode 100644
index 0000000..86252ce
--- /dev/null
+++ b/file_benchmark.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2023 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 <fcntl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include <benchmark/benchmark.h>
+
+#include "android-base/file.h"
+#include "android-base/logging.h"
+
+static void BenchmarkReadFdToString(benchmark::State& state) {
+ android::base::unique_fd fd(memfd_create("memfile", 0));
+ CHECK(fd.get() > 0);
+ CHECK_EQ(ftruncate(fd, state.range(0)), 0);
+ for (auto _ : state) {
+ CHECK_EQ(lseek(fd, 0, SEEK_SET), 0);
+ std::string str;
+ benchmark::DoNotOptimize(android::base::ReadFdToString(fd, &str));
+ }
+ state.SetBytesProcessed(state.iterations() * state.range(0));
+}
+
+BENCHMARK_RANGE(BenchmarkReadFdToString, 0, 1024 * 1024);
diff --git a/file_test.cpp b/file_test.cpp
index f178945..2474f7b 100644
--- a/file_test.cpp
+++ b/file_test.cpp
@@ -176,7 +176,7 @@
}
#endif
-TEST(file, WriteStringToFd) {
+TEST(file, WriteStringToFd_StringLiteral) {
TemporaryFile tf;
ASSERT_NE(tf.fd, -1) << tf.path;
ASSERT_TRUE(android::base::WriteStringToFd("abc", tf.fd));
@@ -188,6 +188,32 @@
EXPECT_EQ("abc", s);
}
+TEST(file, WriteStringToFd_String) {
+ std::string testStr = "def";
+ TemporaryFile tf;
+ ASSERT_NE(tf.fd, -1) << tf.path;
+ ASSERT_TRUE(android::base::WriteStringToFd(testStr, 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(testStr, s);
+}
+
+TEST(file, WriteStringToFd_StringView) {
+ std::string_view testStrView = "ghi";
+ TemporaryFile tf;
+ ASSERT_NE(tf.fd, -1) << tf.path;
+ ASSERT_TRUE(android::base::WriteStringToFd(testStrView, 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(testStrView, s);
+}
+
TEST(file, WriteFully) {
TemporaryFile tf;
ASSERT_NE(tf.fd, -1) << tf.path;
diff --git a/function_ref_benchmark.cpp b/function_ref_benchmark.cpp
new file mode 100644
index 0000000..404043e
--- /dev/null
+++ b/function_ref_benchmark.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2022 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/function_ref.h"
+
+#include <benchmark/benchmark.h>
+
+#include <functional>
+#include <utility>
+
+#include <time.h>
+
+using android::base::function_ref;
+
+template <class Callable, class... Args>
+[[clang::noinline]] auto call(Callable&& c, Args&&... args) {
+ return c(std::forward<Args>(args)...);
+}
+
+[[clang::noinline]] static int testFunc(int, const char*, char) {
+ return time(nullptr);
+}
+
+using Func = decltype(testFunc);
+
+static void BenchmarkFuncRaw(benchmark::State& state) {
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(call(testFunc, 1, "1", '1'));
+ }
+}
+BENCHMARK(BenchmarkFuncRaw);
+
+static void BenchmarkFuncPtr(benchmark::State& state) {
+ auto ptr = &testFunc;
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(call(ptr, 1, "1", '1'));
+ }
+}
+BENCHMARK(BenchmarkFuncPtr);
+
+static void BenchmarkStdFunction(benchmark::State& state) {
+ std::function<Func> f(testFunc);
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(call(f, 1, "1", '1'));
+ }
+}
+BENCHMARK(BenchmarkStdFunction);
+
+static void BenchmarkFunctionRef(benchmark::State& state) {
+ function_ref<Func> f(testFunc);
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(call(f, 1, "1", '1'));
+ }
+}
+BENCHMARK(BenchmarkFunctionRef);
+
+namespace {
+struct BigFunc {
+ char big[128];
+ [[clang::noinline]] int operator()(int, const char*, char) const { return time(nullptr); }
+};
+
+static BigFunc bigFunc;
+} // namespace
+
+static void BenchmarkBigRaw(benchmark::State& state) {
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(call(bigFunc, 1, "1", '1'));
+ }
+}
+BENCHMARK(BenchmarkBigRaw);
+
+static void BenchmarkBigStdFunction(benchmark::State& state) {
+ std::function<Func> f(bigFunc);
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(call(f, 1, "1", '1'));
+ }
+}
+BENCHMARK(BenchmarkBigStdFunction);
+
+static void BenchmarkBigFunctionRef(benchmark::State& state) {
+ function_ref<Func> f(bigFunc);
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(call(f, 1, "1", '1'));
+ }
+}
+BENCHMARK(BenchmarkBigFunctionRef);
+
+static void BenchmarkMakeFunctionRef(benchmark::State& state) {
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(call<function_ref<Func>>(bigFunc, 1, "1", '1'));
+ }
+}
+BENCHMARK(BenchmarkMakeFunctionRef);
+
+static void BenchmarkMakeStdFunction(benchmark::State& state) {
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(call<std::function<Func>>(bigFunc, 1, "1", '1'));
+ }
+}
+BENCHMARK(BenchmarkMakeStdFunction);
diff --git a/include/android-base/file.h b/include/android-base/file.h
index c622562..b11b305 100644
--- a/include/android-base/file.h
+++ b/include/android-base/file.h
@@ -84,7 +84,7 @@
bool WriteStringToFile(const std::string& content, const std::string& path,
bool follow_symlinks = false);
-bool WriteStringToFd(const std::string& content, borrowed_fd fd);
+bool WriteStringToFd(std::string_view content, borrowed_fd fd);
#if !defined(_WIN32)
bool WriteStringToFile(const std::string& content, const std::string& path,
@@ -105,6 +105,7 @@
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 WriteFullyAtOffset(borrowed_fd fd, const void* data, size_t byte_count, off64_t offset);
bool RemoveFileIfExists(const std::string& path, std::string* err = nullptr);
@@ -118,8 +119,8 @@
// 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);
+std::string Basename(std::string_view path);
+std::string Dirname(std::string_view path);
} // namespace base
} // namespace android
diff --git a/include/android-base/function_ref.h b/include/android-base/function_ref.h
index 2de14e3..42594cc 100644
--- a/include/android-base/function_ref.h
+++ b/include/android-base/function_ref.h
@@ -87,40 +87,47 @@
constexpr function_ref(const function_ref& other) noexcept = default;
constexpr function_ref& operator=(const function_ref&) noexcept = default;
- template <class Callable, class = std::enable_if_t<
- std::is_invocable_r<Ret, Callable, Args...>::value &&
- !std::is_same_v<function_ref, std::remove_reference_t<Callable>>>>
- function_ref(Callable&& c) noexcept
- : mTypeErasedFunction([](const function_ref* self, Args... args) -> Ret {
- // Generate a lambda that remembers the type of the passed
- // |Callable|.
- return (*reinterpret_cast<std::remove_reference_t<Callable>*>(self->mCallable))(
- std::forward<Args>(args)...);
- }),
- mCallable(reinterpret_cast<intptr_t>(&c)) {}
+ using RawFunc = Ret(Args...);
+
+ function_ref(RawFunc* funcptr) noexcept { *this = funcptr; }
template <class Callable, class = std::enable_if_t<
- std::is_invocable_r<Ret, Callable, Args...>::value &&
+ std::is_invocable_r_v<Ret, Callable, Args...> &&
+ !std::is_same_v<function_ref, std::remove_reference_t<Callable>>>>
+ function_ref(Callable&& c) noexcept {
+ *this = std::forward<Callable>(c);
+ }
+
+ function_ref& operator=(RawFunc* funcptr) noexcept {
+ mTypeErasedFunction = [](uintptr_t funcptr, Args... args) -> Ret {
+ return (reinterpret_cast<RawFunc*>(funcptr))(std::forward<Args>(args)...);
+ };
+ mCallable = reinterpret_cast<uintptr_t>(funcptr);
+ return *this;
+ }
+
+ template <class Callable, class = std::enable_if_t<
+ std::is_invocable_r_v<Ret, Callable, Args...> &&
!std::is_same_v<function_ref, std::remove_reference_t<Callable>>>>
function_ref& operator=(Callable&& c) noexcept {
- mTypeErasedFunction = [](const function_ref* self, Args... args) -> Ret {
+ mTypeErasedFunction = [](uintptr_t callable, Args... args) -> Ret {
// Generate a lambda that remembers the type of the passed
// |Callable|.
- return (*reinterpret_cast<std::remove_reference_t<Callable>*>(self->mCallable))(
+ return (*reinterpret_cast<std::remove_reference_t<Callable>*>(callable))(
std::forward<Args>(args)...);
};
- mCallable = reinterpret_cast<intptr_t>(&c);
+ mCallable = reinterpret_cast<uintptr_t>(&c);
return *this;
}
Ret operator()(Args... args) const {
- return mTypeErasedFunction(this, std::forward<Args>(args)...);
+ return mTypeErasedFunction(mCallable, std::forward<Args>(args)...);
}
private:
- using TypeErasedFunc = Ret(const function_ref*, Args...);
+ using TypeErasedFunc = Ret(uintptr_t, Args...);
TypeErasedFunc* mTypeErasedFunction;
- intptr_t mCallable;
+ uintptr_t mCallable;
};
} // namespace android::base
diff --git a/include/android-base/macros.h b/include/android-base/macros.h
index 546b2ec..f141f34 100644
--- a/include/android-base/macros.h
+++ b/include/android-base/macros.h
@@ -141,6 +141,8 @@
#define ABI_STRING "arm64"
#elif defined(__i386__)
#define ABI_STRING "x86"
+#elif defined(__riscv)
+#define ABI_STRING "riscv64"
#elif defined(__x86_64__)
#define ABI_STRING "x86_64"
#endif
diff --git a/properties.cpp b/properties.cpp
index 8190987..d925b10 100644
--- a/properties.cpp
+++ b/properties.cpp
@@ -232,7 +232,7 @@
}
const char* CachedProperty::Get(bool* changed) {
- std::optional<uint32_t> initial_property_serial_ = cached_property_serial_;
+ std::optional<uint32_t> initial_property_serial = cached_property_serial_;
// Do we have a `struct prop_info` yet?
if (prop_info_ == nullptr) {
@@ -267,7 +267,7 @@
}
if (changed) {
- *changed = cached_property_serial_ != initial_property_serial_;
+ *changed = cached_property_serial_ != initial_property_serial;
}
if (is_read_only_) {