Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[wpiutil C++] Add remove_prefix() and remove_suffix() #6118

Merged
merged 15 commits into from
Jun 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 7 additions & 9 deletions glass/src/lib/native/cpp/support/DataLogReaderThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,25 +96,23 @@ void DataLogReaderThread::ReadMain() {
if (data.empty()) {
continue;
}
if (wpi::starts_with(name, "NT:")) {
name = wpi::drop_front(name, 3);
if (auto strippedName = wpi::remove_prefix(name, "NT:")) {
name = *strippedName;
}
if (wpi::starts_with(name, "/.schema/struct:")) {
auto typeStr = wpi::drop_front(name, 16);
if (auto typeStr = wpi::remove_prefix(name, "/.schema/struct:")) {
std::string_view schema{reinterpret_cast<const char*>(data.data()),
data.size()};
std::string err;
auto desc = m_structDb.Add(typeStr, schema, &err);
auto desc = m_structDb.Add(*typeStr, schema, &err);
if (!desc) {
wpi::print("could not decode struct '{}' schema '{}': {}\n", name,
schema, err);
}
} else if (wpi::starts_with(name, "/.schema/proto:")) {
} else if (auto filename = wpi::remove_prefix(name, "/.schema/proto:")) {
// protobuf descriptor handling
auto filename = wpi::drop_front(name, 15);
if (!m_protoDb.Add(filename, data)) {
if (!m_protoDb.Add(*filename, data)) {
wpi::print("could not decode protobuf '{}' filename '{}'\n", name,
filename);
*filename);
}
}
}
Expand Down
6 changes: 4 additions & 2 deletions glass/src/libnt/native/cpp/NTField2D.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ void NTField2DModel::Update() {
for (auto&& event : m_poller.ReadQueue()) {
if (auto info = event.GetTopicInfo()) {
// handle publish/unpublish
auto name = wpi::drop_front(info->name, m_path.size());
auto name = wpi::remove_prefix(info->name, m_path).value_or("");
if (name.empty() || name[0] == '.') {
continue;
}
Expand Down Expand Up @@ -198,7 +198,9 @@ void NTField2DModel::ForEachFieldObject(
func) {
for (auto&& obj : m_objects) {
if (obj->Exists()) {
func(*obj, wpi::drop_front(obj->GetName(), m_path.size()));
if (auto name = wpi::remove_prefix(obj->GetName(), m_path)) {
func(*obj, *name);
}
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions glass/src/libnt/native/cpp/NTMechanism2D.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ NTMechanism2DModel::~NTMechanism2DModel() = default;
void NTMechanism2DModel::Update() {
for (auto&& event : m_poller.ReadQueue()) {
if (auto info = event.GetTopicInfo()) {
auto name = wpi::drop_front(info->name, m_path.size());
auto name = wpi::remove_prefix(info->name, m_path).value_or("");
if (name.empty() || name[0] == '.') {
continue;
}
Expand Down Expand Up @@ -307,7 +307,7 @@ void NTMechanism2DModel::Update() {
}
} else {
auto fullName = nt::Topic{valueData->topic}.GetName();
auto name = wpi::drop_front(fullName, m_path.size());
auto name = wpi::remove_prefix(fullName, m_path).value_or("");
if (name.empty() || name[0] == '.') {
continue;
}
Expand Down
77 changes: 40 additions & 37 deletions glass/src/libnt/native/cpp/NetworkTables.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -727,11 +727,12 @@ void NetworkTablesModel::ValueSource::UpdateFromValue(
mpack_reader_init_data(&r, value.GetRaw());
UpdateMsgpackValueSource(model, this, r, name, value.last_change());
mpack_reader_destroy(&r);
} else if (wpi::starts_with(typeStr, "struct:")) {
auto structName = wpi::drop_front(typeStr, 7);
bool isArray = structName.ends_with("[]");
} else if (auto structNameOpt = wpi::remove_prefix(typeStr, "struct:")) {
auto structName = *structNameOpt;
auto withoutArray = wpi::remove_suffix(structName, "[]");
bool isArray = withoutArray.has_value();
if (isArray) {
structName = wpi::drop_back(structName, 2);
structName = *withoutArray;
}
auto desc = model.m_structDb.Find(structName);
if (desc && desc->IsValid()) {
Expand Down Expand Up @@ -762,8 +763,8 @@ void NetworkTablesModel::ValueSource::UpdateFromValue(
} else {
valueChildren.clear();
}
} else if (wpi::starts_with(typeStr, "proto:")) {
auto msg = model.m_protoDb.Find(wpi::drop_front(typeStr, 6));
} else if (auto filename = wpi::remove_prefix(typeStr, "proto:")) {
auto msg = model.m_protoDb.Find(*filename);
if (msg) {
msg->Clear();
auto raw = value.GetRaw();
Expand Down Expand Up @@ -808,13 +809,15 @@ void NetworkTablesModel::Update() {
m_server.publishers.clear();
} else if (info->name == "$serversub") {
m_server.subscribers.clear();
} else if (wpi::starts_with(info->name, "$clientpub$")) {
auto it = m_clients.find(wpi::drop_front(info->name, 11));
} else if (auto client =
wpi::remove_prefix(info->name, "$clientpub$")) {
auto it = m_clients.find(*client);
if (it != m_clients.end()) {
it->second.publishers.clear();
}
} else if (wpi::starts_with(info->name, "$clientsub$")) {
auto it = m_clients.find(wpi::drop_front(info->name, 11));
} else if (auto client =
wpi::remove_prefix(info->name, "$clientsub$")) {
auto it = m_clients.find(*client);
if (it != m_clients.end()) {
it->second.subscribers.clear();
}
Expand Down Expand Up @@ -854,27 +857,29 @@ void NetworkTablesModel::Update() {
m_server.UpdatePublishers(entry->value.GetRaw());
} else if (entry->info.name == "$serversub") {
m_server.UpdateSubscribers(entry->value.GetRaw());
} else if (wpi::starts_with(entry->info.name, "$clientpub$")) {
auto it = m_clients.find(wpi::drop_front(entry->info.name, 11));
} else if (auto client =
wpi::remove_prefix(entry->info.name, "$clientpub$")) {
auto it = m_clients.find(*client);
if (it != m_clients.end()) {
it->second.UpdatePublishers(entry->value.GetRaw());
}
} else if (wpi::starts_with(entry->info.name, "$clientsub$")) {
auto it = m_clients.find(wpi::drop_front(entry->info.name, 11));
} else if (auto client =
wpi::remove_prefix(entry->info.name, "$clientsub$")) {
auto it = m_clients.find(*client);
if (it != m_clients.end()) {
it->second.UpdateSubscribers(entry->value.GetRaw());
}
}
} else if (entry->value.IsRaw() &&
wpi::starts_with(entry->info.name, "/.schema/struct:") &&
} else if (auto typeStr =
wpi::remove_prefix(entry->info.name, "/.schema/struct:");
entry->value.IsRaw() && typeStr &&
entry->info.type_str == "structschema") {
// struct schema handling
auto typeStr = wpi::drop_front(entry->info.name, 16);
std::string_view schema{
reinterpret_cast<const char*>(entry->value.GetRaw().data()),
entry->value.GetRaw().size()};
std::string err;
auto desc = m_structDb.Add(typeStr, schema, &err);
auto desc = m_structDb.Add(*typeStr, schema, &err);
if (!desc) {
wpi::print("could not decode struct '{}' schema '{}': {}\n",
entry->info.name, schema, err);
Expand All @@ -884,25 +889,23 @@ void NetworkTablesModel::Update() {
if (!entryPair.second) {
continue;
}
std::string_view ts = entryPair.second->info.type_str;
if (!wpi::starts_with(ts, "struct:")) {
continue;
}
ts = wpi::drop_front(ts, 7);
if (ts == typeStr || (wpi::ends_with(ts, "[]") &&
wpi::drop_back(ts, 2) == typeStr)) {
entryPair.second->UpdateFromValue(*this);
if (auto ts = wpi::remove_prefix(entryPair.second->info.type_str,
"struct:")) {
if (*ts == *typeStr ||
wpi::remove_suffix(*ts, "[]").value_or(*ts) == *typeStr) {
entryPair.second->UpdateFromValue(*this);
}
PeterJohnson marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
} else if (entry->value.IsRaw() &&
wpi::starts_with(entry->info.name, "/.schema/proto:") &&
} else if (auto filename =
wpi::remove_prefix(entry->info.name, "/.schema/proto:");
entry->value.IsRaw() && filename &&
entry->info.type_str == "proto:FileDescriptorProto") {
// protobuf descriptor handling
auto filename = wpi::drop_front(entry->info.name, 15);
if (!m_protoDb.Add(filename, entry->value.GetRaw())) {
if (!m_protoDb.Add(*filename, entry->value.GetRaw())) {
wpi::print("could not decode protobuf '{}' filename '{}'\n",
entry->info.name, filename);
entry->info.name, *filename);
} else {
// loop over all protobuf entries and update (conservatively)
for (auto&& entryPair : m_entries) {
Expand Down Expand Up @@ -1080,18 +1083,18 @@ void NetworkTablesModel::UpdateClients(std::span<const uint8_t> data) {
}

static bool GetHeadingTypeString(std::string_view* ts) {
if (wpi::starts_with(*ts, "proto:")) {
*ts = wpi::drop_front(*ts, 6);
if (auto withoutProto = wpi::remove_prefix(*ts, "proto:")) {
*ts = *withoutProto;
auto lastdot = ts->rfind('.');
if (lastdot != std::string_view::npos) {
*ts = wpi::substr(*ts, lastdot + 1);
}
if (wpi::starts_with(*ts, "Protobuf")) {
*ts = wpi::drop_front(*ts, 8);
if (auto withoutProtobuf = wpi::remove_prefix(*ts, "Protobuf")) {
*ts = *withoutProtobuf;
}
return true;
} else if (wpi::starts_with(*ts, "struct:")) {
*ts = wpi::drop_front(*ts, 7);
} else if (auto withoutStruct = wpi::remove_prefix(*ts, "struct:")) {
*ts = *withoutStruct;
return true;
}
return false;
Expand Down
8 changes: 4 additions & 4 deletions glass/src/libnt/native/cpp/NetworkTablesProvider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,8 @@ void NetworkTablesProvider::Update() {
}

auto topicName = nt::GetTopicName(valueData->topic);
auto tableName = wpi::drop_back(topicName, 6);
auto tableName =
wpi::remove_suffix(topicName, "/.type").value_or(topicName);

GetOrCreateView(builderIt->second, nt::Topic{valueData->topic},
tableName);
Expand Down Expand Up @@ -196,9 +197,8 @@ void NetworkTablesProvider::Show(ViewEntry* entry, Window* window) {
if (!window) {
return;
}
if (wpi::starts_with(entry->name, "/SmartDashboard/")) {
window->SetDefaultName(
fmt::format("{} (SmartDashboard)", wpi::drop_front(entry->name, 16)));
if (auto name = wpi::remove_prefix(entry->name, "/SmartDashboard/")) {
window->SetDefaultName(fmt::format("{} (SmartDashboard)", *name));
}
entry->window = window;

Expand Down
9 changes: 5 additions & 4 deletions ntcore/src/main/native/cpp/LocalStorage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,11 @@ int LocalStorage::DataLoggerData::Start(TopicData* topic, int64_t time) {
} else if (typeStr == "int[]") {
typeStr = "int64[]";
}
return log.Start(fmt::format("{}{}", logPrefix,
wpi::drop_front(topic->name, prefix.size())),
typeStr, DataLoggerEntry::MakeMetadata(topic->propertiesStr),
time);
return log.Start(
fmt::format(
"{}{}", logPrefix,
wpi::remove_prefix(topic->name, prefix).value_or(topic->name)),
typeStr, DataLoggerEntry::MakeMetadata(topic->propertiesStr), time);
}

void LocalStorage::DataLoggerEntry::Append(const Value& v) {
Expand Down
4 changes: 2 additions & 2 deletions ntcore/src/main/native/cpp/NetworkServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -224,8 +224,8 @@ void NetworkServer::ServerConnection4::ProcessWsUpgrade() {
wpi::SmallString<128> nameBuf;
std::string_view name;
bool err = false;
if (wpi::starts_with(path, "/nt/")) {
name = wpi::UnescapeURI(wpi::drop_front(path, 4), nameBuf, &err);
if (auto uri = wpi::remove_prefix(path, "/nt/")) {
name = wpi::UnescapeURI(*uri, nameBuf, &err);
}
if (err || name.empty()) {
INFO("invalid path '{}' (from {}), must match /nt/[clientId], closing",
Expand Down
3 changes: 1 addition & 2 deletions ntcore/src/main/native/cpp/net/WireDecoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,6 @@ bool nt::net::WireDecodeBinary(std::span<const uint8_t>* in, int64_t* outId,
outValue->SetServerTime(time);
outValue->SetTime(time == 0 ? 0 : time + localTimeOffset);
// update input range
*in = wpi::drop_front(*in,
in->size() - mpack_reader_remaining(&reader, nullptr));
*in = wpi::take_back(*in, mpack_reader_remaining(&reader, nullptr));
return true;
}
Original file line number Diff line number Diff line change
Expand Up @@ -169,14 +169,13 @@ void HALSimHttpConnection::ProcessRequest() {
!wpi::contains(path, "..") && !wpi::contains(path, "//")) {
// convert to fs native representation
fs::path nativePath;
if (wpi::starts_with(path, "/user/")) {
nativePath =
fs::path{m_server->GetWebrootSys()} /
fs::path{wpi::drop_front(path, 6), fs::path::format::generic_format};
if (auto userPath = wpi::remove_prefix(path, "/user/")) {
nativePath = fs::path{m_server->GetWebrootSys()} /
fs::path{*userPath, fs::path::format::generic_format};
} else {
nativePath =
fs::path{m_server->GetWebrootSys()} /
fs::path{wpi::drop_front(path, 1), fs::path::format::generic_format};
fs::path{wpi::drop_front(path), fs::path::format::generic_format};
}

if (fs::is_directory(nativePath)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ bool wpi::detail::ConsumeSignedInteger(
}

// Get the positive part of the value.
std::string_view str2 = wpi::drop_front(str, 1);
std::string_view str2 = wpi::drop_front(str);
if (wpi::detail::ConsumeUnsignedInteger(str2, radix, ullVal) ||
// Reject values so large they'd overflow as negative signed, but allow
// "-0". This negates the unsigned so that the negative isn't undefined
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,32 @@ inline bool contains_lower(std::string_view str, const char* other) noexcept {
return find_lower(str, other) != std::string_view::npos;
}

/**
* Return an optional containing @p str but with @p prefix removed if the string
* starts with the prefix. If the string does not start with the prefix, return
* an empty optional.
*/
constexpr std::optional<std::string_view> remove_prefix(std::string_view str, std::string_view prefix) noexcept {
if (str.starts_with(prefix)) {
str.remove_prefix(prefix.size());
return str;
}
return std::nullopt;
}

/**
* Return an optional containing @p str but with @p suffix removed if the
* string ends with the suffix. If the string does not end with the suffix,
* return an empty optional.
*/
constexpr std::optional<std::string_view> remove_suffix(std::string_view str, std::string_view suffix) noexcept {
if (str.ends_with(suffix)) {
str.remove_suffix(suffix.size());
return str;
}
return std::nullopt;
}

/**
* Return a string_view equal to @p str but with the first @p n elements
* dropped.
Expand Down
35 changes: 35 additions & 0 deletions wpiutil/src/test/native/cpp/StringExtrasTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.

#include <gtest/gtest.h>

#include "wpi/StringExtras.h"

TEST(StringExtrasTest, RemovePrefix) {
std::string_view original = "wpilib";
auto modified = wpi::remove_prefix(original, "wpi");
EXPECT_EQ(original, "wpilib");
EXPECT_EQ(modified, std::optional{"lib"});
}

TEST(StringExtrasTest, RemoveSuffix) {
std::string_view original = "wpilib";
auto modified = wpi::remove_suffix(original, "lib");
EXPECT_EQ(original, "wpilib");
EXPECT_EQ(modified, std::optional{"wpi"});
}

TEST(StringExtrasTest, RemovePrefixNoMatch) {
std::string_view original = "wpilib";
auto modified = wpi::remove_prefix(original, "foo");
EXPECT_EQ(original, "wpilib");
EXPECT_EQ(modified, std::nullopt);
}

TEST(StringExtrasTest, RemoveSuffixNoMatch) {
std::string_view original = "wpilib";
auto modified = wpi::remove_suffix(original, "foo");
EXPECT_EQ(original, "wpilib");
EXPECT_EQ(modified, std::nullopt);
}
Loading