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
This commit is contained in:
Elliott Hughes 2019-05-03 09:02:45 -07:00
parent 56311071fe
commit b4dc7be6c5
8 changed files with 65 additions and 37 deletions

View file

@ -1048,9 +1048,9 @@ HostRequestResult handle_host_request(std::string_view service, TransportType ty
// 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 @@ HostRequestResult handle_host_request(std::string_view service, TransportType ty
// 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 @@ HostRequestResult handle_host_request(std::string_view service, TransportType ty
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 @@ HostRequestResult handle_host_request(std::string_view service, TransportType ty
}
// 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;

View file

@ -141,11 +141,3 @@ inline bool ParseUint(T* result, std::string_view str, std::string_view* remaini
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;
}

View file

@ -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 @@ int main(int argc, char* const argv[]) {
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;

View file

@ -223,13 +223,13 @@ asocket* daemon_service_to_socket(std::string_view name) {
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 @@ unique_fd daemon_service_to_fd(std::string_view name, atransport* transport) {
#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 @@ unique_fd daemon_service_to_fd(std::string_view name, atransport* transport) {
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 @@ unique_fd daemon_service_to_fd(std::string_view name, atransport* transport) {
} 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 @@ unique_fd daemon_service_to_fd(std::string_view name, atransport* transport) {
}
#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(

View file

@ -202,7 +202,7 @@ asocket* host_service_to_socket(std::string_view name, std::string_view serial,
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 @@ asocket* host_service_to_socket(std::string_view name, std::string_view serial,
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 @@ asocket* host_service_to_socket(std::string_view name, std::string_view serial,
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));

View file

@ -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 @@ static int smart_socket_enqueue(asocket* s, apacket::payload_type data) {
#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{};

View file

@ -18,6 +18,7 @@
#include <sstream>
#include <string>
#include <string_view>
#include <vector>
namespace android {
@ -68,5 +69,21 @@ 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;
}
} // namespace base
} // namespace android

View file

@ -295,3 +295,19 @@ TEST(strings, EqualsIgnoreCase) {
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);
}