libbase: add ConsumePrefix/ConsumeSuffix.
adb was already using ConsumePrefix, and now we have another would-be
user in cutils. (There appears to be one place in adb that should use
ConsumeSuffix, so I'm assuming we'll want that sooner or later.)
I've kept these inline because adb and google3's versions both were, and
I'm easily led.
Test: treehugger
Change-Id: I29d99032f6f6ccbfaefece59725db8afb02a4c87
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);
+}