Merge "libbase: add ConsumePrefix/ConsumeSuffix." am: 7046bcb461
am: 9f6f62a485

Change-Id: Ie328e1a6da12ace87ebb2da65cbc649a1c972039
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 2dd22b3..24d4292 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -1048,9 +1048,9 @@
         // New transport selection protocol:
         // This is essentially identical to the previous version, except it returns the selected
         // transport id to the caller as well.
-        if (ConsumePrefix(&service, "tport:")) {
+        if (android::base::ConsumePrefix(&service, "tport:")) {
             legacy = false;
-            if (ConsumePrefix(&service, "serial:")) {
+            if (android::base::ConsumePrefix(&service, "serial:")) {
                 serial_storage = service;
                 serial = serial_storage.c_str();
             } else if (service == "usb") {
@@ -1064,7 +1064,7 @@
             // Selection by id is unimplemented, since you obviously already know the transport id
             // you're connecting to.
         } else {
-            if (ConsumePrefix(&service, "transport-id:")) {
+            if (android::base::ConsumePrefix(&service, "transport-id:")) {
                 if (!ParseUint(&transport_id, service)) {
                     SendFail(reply_fd, "invalid transport id");
                     return HostRequestResult::Handled;
@@ -1075,7 +1075,7 @@
                 type = kTransportLocal;
             } else if (service == "transport-any") {
                 type = kTransportAny;
-            } else if (ConsumePrefix(&service, "transport:")) {
+            } else if (android::base::ConsumePrefix(&service, "transport:")) {
                 serial_storage = service;
                 serial = serial_storage.c_str();
             }
@@ -1216,7 +1216,7 @@
     }
 
     // Indicates a new emulator instance has started.
-    if (ConsumePrefix(&service, "emulator:")) {
+    if (android::base::ConsumePrefix(&service, "emulator:")) {
         unsigned int port;
         if (!ParseUint(&port, service)) {
           LOG(ERROR) << "received invalid port for emulator: " << service;
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index 5800a62..8b0dcee 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -141,11 +141,3 @@
 
     return true;
 }
-
-inline bool ConsumePrefix(std::string_view* str, std::string_view prefix) {
-  if (str->starts_with(prefix)) {
-    str->remove_prefix(prefix.size());
-    return true;
-  }
-  return false;
-}
diff --git a/adb/daemon/abb.cpp b/adb/daemon/abb.cpp
index eeac41a..aa75bb1 100644
--- a/adb/daemon/abb.cpp
+++ b/adb/daemon/abb.cpp
@@ -17,6 +17,7 @@
 #include <sys/wait.h>
 
 #include <android-base/cmsg.h>
+#include <android-base/strings.h>
 #include <cmd.h>
 
 #include "adb.h"
@@ -87,9 +88,9 @@
 
         std::string_view name = data;
         auto protocol = SubprocessProtocol::kShell;
-        if (ConsumePrefix(&name, "abb:")) {
+        if (android::base::ConsumePrefix(&name, "abb:")) {
             protocol = SubprocessProtocol::kShell;
-        } else if (ConsumePrefix(&name, "abb_exec:")) {
+        } else if (android::base::ConsumePrefix(&name, "abb_exec:")) {
             protocol = SubprocessProtocol::kNone;
         } else {
             LOG(FATAL) << "Unknown command prefix for abb: " << data;
diff --git a/adb/daemon/services.cpp b/adb/daemon/services.cpp
index b0cc450..e6f4499 100644
--- a/adb/daemon/services.cpp
+++ b/adb/daemon/services.cpp
@@ -223,13 +223,13 @@
         return create_jdwp_service_socket();
     } else if (name == "track-jdwp") {
         return create_jdwp_tracker_service_socket();
-    } else if (ConsumePrefix(&name, "sink:")) {
+    } else if (android::base::ConsumePrefix(&name, "sink:")) {
         uint64_t byte_count = 0;
         if (!ParseUint(&byte_count, name)) {
             return nullptr;
         }
         return new SinkSocket(byte_count);
-    } else if (ConsumePrefix(&name, "source:")) {
+    } else if (android::base::ConsumePrefix(&name, "source:")) {
         uint64_t byte_count = 0;
         if (!ParseUint(&byte_count, name)) {
             return nullptr;
@@ -250,11 +250,11 @@
 #if defined(__ANDROID__)
     if (name.starts_with("framebuffer:")) {
         return create_service_thread("fb", framebuffer_service);
-    } else if (ConsumePrefix(&name, "remount:")) {
+    } else if (android::base::ConsumePrefix(&name, "remount:")) {
         std::string arg(name);
         return create_service_thread("remount",
                                      std::bind(remount_service, std::placeholders::_1, arg));
-    } else if (ConsumePrefix(&name, "reboot:")) {
+    } else if (android::base::ConsumePrefix(&name, "reboot:")) {
         std::string arg(name);
         return create_service_thread("reboot",
                                      std::bind(reboot_service, std::placeholders::_1, arg));
@@ -262,7 +262,7 @@
         return create_service_thread("root", restart_root_service);
     } else if (name.starts_with("unroot:")) {
         return create_service_thread("unroot", restart_unroot_service);
-    } else if (ConsumePrefix(&name, "backup:")) {
+    } else if (android::base::ConsumePrefix(&name, "backup:")) {
         std::string cmd = "/system/bin/bu backup ";
         cmd += name;
         return StartSubprocess(cmd, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
@@ -275,7 +275,7 @@
     } else if (name.starts_with("enable-verity:")) {
         return create_service_thread("verity-off", std::bind(set_verity_enabled_state_service,
                                                              std::placeholders::_1, true));
-    } else if (ConsumePrefix(&name, "tcpip:")) {
+    } else if (android::base::ConsumePrefix(&name, "tcpip:")) {
         std::string str(name);
 
         int port;
@@ -289,22 +289,22 @@
     }
 #endif
 
-    if (ConsumePrefix(&name, "dev:")) {
+    if (android::base::ConsumePrefix(&name, "dev:")) {
         return unique_fd{unix_open(name, O_RDWR | O_CLOEXEC)};
-    } else if (ConsumePrefix(&name, "jdwp:")) {
+    } else if (android::base::ConsumePrefix(&name, "jdwp:")) {
         pid_t pid;
         if (!ParseUint(&pid, name)) {
             return unique_fd{};
         }
         return create_jdwp_connection_fd(pid);
-    } else if (ConsumePrefix(&name, "shell")) {
+    } else if (android::base::ConsumePrefix(&name, "shell")) {
         return ShellService(name, transport);
-    } else if (ConsumePrefix(&name, "exec:")) {
+    } else if (android::base::ConsumePrefix(&name, "exec:")) {
         return StartSubprocess(std::string(name), nullptr, SubprocessType::kRaw,
                                SubprocessProtocol::kNone);
     } else if (name.starts_with("sync:")) {
         return create_service_thread("sync", file_sync_service);
-    } else if (ConsumePrefix(&name, "reverse:")) {
+    } else if (android::base::ConsumePrefix(&name, "reverse:")) {
         return reverse_service(name, transport);
     } else if (name == "reconnect") {
         return create_service_thread(
diff --git a/adb/services.cpp b/adb/services.cpp
index 46cab6e..6185aa6 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -202,7 +202,7 @@
         return create_device_tracker(false);
     } else if (name == "track-devices-l") {
         return create_device_tracker(true);
-    } else if (ConsumePrefix(&name, "wait-for-")) {
+    } else if (android::base::ConsumePrefix(&name, "wait-for-")) {
         std::shared_ptr<state_info> sinfo = std::make_shared<state_info>();
         if (sinfo == nullptr) {
             fprintf(stderr, "couldn't allocate state_info: %s", strerror(errno));
@@ -212,11 +212,11 @@
         sinfo->serial = serial;
         sinfo->transport_id = transport_id;
 
-        if (ConsumePrefix(&name, "local")) {
+        if (android::base::ConsumePrefix(&name, "local")) {
             sinfo->transport_type = kTransportLocal;
-        } else if (ConsumePrefix(&name, "usb")) {
+        } else if (android::base::ConsumePrefix(&name, "usb")) {
             sinfo->transport_type = kTransportUsb;
-        } else if (ConsumePrefix(&name, "any")) {
+        } else if (android::base::ConsumePrefix(&name, "any")) {
             sinfo->transport_type = kTransportAny;
         } else {
             return nullptr;
@@ -243,7 +243,7 @@
         unique_fd fd = create_service_thread(
                 "wait", [sinfo](unique_fd fd) { wait_for_state(std::move(fd), sinfo.get()); });
         return create_local_socket(std::move(fd));
-    } else if (ConsumePrefix(&name, "connect:")) {
+    } else if (android::base::ConsumePrefix(&name, "connect:")) {
         std::string host(name);
         unique_fd fd = create_service_thread(
                 "connect", std::bind(connect_service, std::placeholders::_1, host));
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 8a2bf9a..75993b3 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -31,6 +31,8 @@
 #include <string>
 #include <vector>
 
+#include <android-base/strings.h>
+
 #if !ADB_HOST
 #include <android-base/properties.h>
 #include <log/log_properties.h>
@@ -755,26 +757,26 @@
 
 #if ADB_HOST
     service = std::string_view(s->smart_socket_data).substr(4);
-    if (ConsumePrefix(&service, "host-serial:")) {
+    if (android::base::ConsumePrefix(&service, "host-serial:")) {
         // serial number should follow "host:" and could be a host:port string.
         if (!internal::parse_host_service(&serial, &service, service)) {
             LOG(ERROR) << "SS(" << s->id << "): failed to parse host service: " << service;
             goto fail;
         }
-    } else if (ConsumePrefix(&service, "host-transport-id:")) {
+    } else if (android::base::ConsumePrefix(&service, "host-transport-id:")) {
         if (!ParseUint(&transport_id, service, &service)) {
             LOG(ERROR) << "SS(" << s->id << "): failed to parse host transport id: " << service;
             return -1;
         }
-        if (!ConsumePrefix(&service, ":")) {
+        if (!android::base::ConsumePrefix(&service, ":")) {
             LOG(ERROR) << "SS(" << s->id << "): host-transport-id without command";
             return -1;
         }
-    } else if (ConsumePrefix(&service, "host-usb:")) {
+    } else if (android::base::ConsumePrefix(&service, "host-usb:")) {
         type = kTransportUsb;
-    } else if (ConsumePrefix(&service, "host-local:")) {
+    } else if (android::base::ConsumePrefix(&service, "host-local:")) {
         type = kTransportLocal;
-    } else if (ConsumePrefix(&service, "host:")) {
+    } else if (android::base::ConsumePrefix(&service, "host:")) {
         type = kTransportAny;
     } else {
         service = std::string_view{};
diff --git a/base/include/android-base/strings.h b/base/include/android-base/strings.h
index 8e9716f..b1c22c9 100644
--- a/base/include/android-base/strings.h
+++ b/base/include/android-base/strings.h
@@ -18,6 +18,7 @@
 
 #include <sstream>
 #include <string>
+#include <string_view>
 #include <vector>
 
 namespace android {
@@ -68,5 +69,21 @@
 // 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;
+}
+
 }  // namespace base
 }  // namespace android
diff --git a/base/strings_test.cpp b/base/strings_test.cpp
index 9d74094..ca3c0b8 100644
--- a/base/strings_test.cpp
+++ b/base/strings_test.cpp
@@ -295,3 +295,19 @@
 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);
+}