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_) {