diff --git a/datalogtool/src/main/native/cpp/Exporter.cpp b/datalogtool/src/main/native/cpp/Exporter.cpp index a1213f40b62..149b6718309 100644 --- a/datalogtool/src/main/native/cpp/Exporter.cpp +++ b/datalogtool/src/main/native/cpp/Exporter.cpp @@ -102,7 +102,8 @@ static void RebuildEntryTree() { } else { parts.emplace_back(prefix); } - wpi::split(mainpart, parts, '/', -1, false); + wpi::split(mainpart, '/', -1, false, + [&](auto part) { parts.emplace_back(part); }); // ignore a raw "/" key if (parts.empty()) { diff --git a/glass/src/libnt/native/cpp/NetworkTables.cpp b/glass/src/libnt/native/cpp/NetworkTables.cpp index 3103f6b7764..e10ff93c09a 100644 --- a/glass/src/libnt/native/cpp/NetworkTables.cpp +++ b/glass/src/libnt/native/cpp/NetworkTables.cpp @@ -1015,7 +1015,8 @@ void NetworkTablesModel::RebuildTreeImpl(std::vector* tree, continue; } parts.clear(); - wpi::split(entry->info.name, parts, '/', -1, false); + wpi::split(entry->info.name, '/', -1, false, + [&](auto part) { parts.emplace_back(part); }); // ignore a raw "/" key if (parts.empty()) { diff --git a/glass/src/libnt/native/cpp/NetworkTablesProvider.cpp b/glass/src/libnt/native/cpp/NetworkTablesProvider.cpp index 36e892ecaa0..2386ee0e533 100644 --- a/glass/src/libnt/native/cpp/NetworkTablesProvider.cpp +++ b/glass/src/libnt/native/cpp/NetworkTablesProvider.cpp @@ -68,7 +68,8 @@ void NetworkTablesProvider::DisplayMenu() { wpi::SmallString<64> name; for (auto&& entry : m_viewEntries) { path.clear(); - wpi::split(entry->name, path, '/', -1, false); + wpi::split(entry->name, '/', -1, false, + [&](auto name) { path.emplace_back(name); }); bool fullDepth = true; int depth = 0; diff --git a/glass/src/libnt/native/cpp/NetworkTablesSettings.cpp b/glass/src/libnt/native/cpp/NetworkTablesSettings.cpp index aac53b50ac1..78f4fc8cd77 100644 --- a/glass/src/libnt/native/cpp/NetworkTablesSettings.cpp +++ b/glass/src/libnt/native/cpp/NetworkTablesSettings.cpp @@ -12,7 +12,6 @@ #include #include #include -#include #include #include "glass/Context.h" @@ -69,12 +68,10 @@ void NetworkTablesSettings::Thread::Main() { (team = wpi::parse_integer(serverTeam, 10))) { nt::SetServerTeam(m_inst, team.value(), m_port); } else { - wpi::SmallVector serverNames; std::vector> servers; - wpi::split(serverTeam, serverNames, ',', -1, false); - for (auto&& serverName : serverNames) { + wpi::split(serverTeam, ',', -1, false, [&](auto serverName) { servers.emplace_back(serverName, m_port); - } + }); nt::SetServer(m_inst, servers); } diff --git a/hal/src/main/native/sim/Extensions.cpp b/hal/src/main/native/sim/Extensions.cpp index 7d22f41fe55..a1b2d7bf262 100644 --- a/hal/src/main/native/sim/Extensions.cpp +++ b/hal/src/main/native/sim/Extensions.cpp @@ -10,7 +10,6 @@ #include #include -#include #include #include #include @@ -100,7 +99,6 @@ int HAL_LoadOneExtension(const char* library) { int HAL_LoadExtensions(void) { int rc = 1; - wpi::SmallVector libraries; const char* e = std::getenv("HALSIM_EXTENSIONS"); if (!e) { if (GetShowNotFoundMessage()) { @@ -109,13 +107,11 @@ int HAL_LoadExtensions(void) { } return rc; } - wpi::split(e, libraries, DELIM, -1, false); - for (auto& library : libraries) { - rc = HAL_LoadOneExtension(std::string(library).c_str()); - if (rc < 0) { - break; + wpi::split(e, DELIM, -1, false, [&](auto library) { + if (rc >= 0) { + rc = HAL_LoadOneExtension(std::string(library).c_str()); } - } + }); return rc; } diff --git a/ntcore/src/main/native/cpp/networktables/NetworkTable.cpp b/ntcore/src/main/native/cpp/networktables/NetworkTable.cpp index 750e5520bc7..1defd6200d0 100644 --- a/ntcore/src/main/native/cpp/networktables/NetworkTable.cpp +++ b/ntcore/src/main/native/cpp/networktables/NetworkTable.cpp @@ -54,12 +54,10 @@ std::string_view NetworkTable::NormalizeKey(std::string_view key, buf.push_back(PATH_SEPARATOR_CHAR); } // for each path element, add it with a slash following - wpi::SmallVector parts; - wpi::split(key, parts, PATH_SEPARATOR_CHAR, -1, false); - for (auto i = parts.begin(); i != parts.end(); ++i) { - buf.append(i->begin(), i->end()); + wpi::split(key, PATH_SEPARATOR_CHAR, -1, false, [&](auto part) { + buf.append(part.begin(), part.end()); buf.push_back(PATH_SEPARATOR_CHAR); - } + }); // remove trailing slash if the input key didn't have one if (!key.empty() && key.back() != PATH_SEPARATOR_CHAR) { buf.pop_back(); @@ -72,19 +70,17 @@ std::vector NetworkTable::GetHierarchy(std::string_view key) { hierarchy.emplace_back(1, PATH_SEPARATOR_CHAR); // for each path element, add it to the end of what we built previously wpi::SmallString<128> path; - wpi::SmallVector parts; - wpi::split(key, parts, PATH_SEPARATOR_CHAR, -1, false); - if (!parts.empty()) { - for (auto i = parts.begin(); i != parts.end(); ++i) { - path += PATH_SEPARATOR_CHAR; - path += *i; - hierarchy.emplace_back(path.str()); - } - // handle trailing slash - if (key.back() == PATH_SEPARATOR_CHAR) { - path += PATH_SEPARATOR_CHAR; - hierarchy.emplace_back(path.str()); - } + bool any = false; + wpi::split(key, PATH_SEPARATOR_CHAR, -1, false, [&](auto part) { + any = true; + path += PATH_SEPARATOR_CHAR; + path += part; + hierarchy.emplace_back(path.str()); + }); + // handle trailing slash + if (any && key.back() == PATH_SEPARATOR_CHAR) { + path += PATH_SEPARATOR_CHAR; + hierarchy.emplace_back(path.str()); } return hierarchy; } diff --git a/simulation/halsim_ws_client/src/main/native/cpp/HALSimWS.cpp b/simulation/halsim_ws_client/src/main/native/cpp/HALSimWS.cpp index fa1aab2e978..da72cbc7981 100644 --- a/simulation/halsim_ws_client/src/main/native/cpp/HALSimWS.cpp +++ b/simulation/halsim_ws_client/src/main/native/cpp/HALSimWS.cpp @@ -9,7 +9,6 @@ #include #include -#include #include #include #include @@ -73,15 +72,8 @@ bool HALSimWS::Initialize() { const char* msgFilters = std::getenv("HALSIMWS_FILTERS"); if (msgFilters != nullptr) { m_useMsgFiltering = true; - - std::string_view filters(msgFilters); - filters = wpi::trim(filters); - wpi::SmallVector filtersSplit; - - wpi::split(filters, filtersSplit, ',', -1, false); - for (auto val : filtersSplit) { - m_msgFilters[wpi::trim(val)] = true; - } + wpi::split(wpi::trim(msgFilters), ',', -1, false, + [&](auto val) { m_msgFilters[wpi::trim(val)] = true; }); } else { m_useMsgFiltering = false; } diff --git a/simulation/halsim_ws_server/src/main/native/cpp/HALSimWeb.cpp b/simulation/halsim_ws_server/src/main/native/cpp/HALSimWeb.cpp index c482cf2ee94..fa16ceff7b2 100644 --- a/simulation/halsim_ws_server/src/main/native/cpp/HALSimWeb.cpp +++ b/simulation/halsim_ws_server/src/main/native/cpp/HALSimWeb.cpp @@ -83,15 +83,8 @@ bool HALSimWeb::Initialize() { const char* msgFilters = std::getenv("HALSIMWS_FILTERS"); if (msgFilters != nullptr) { m_useMsgFiltering = true; - - std::string_view filters(msgFilters); - filters = wpi::trim(filters); - wpi::SmallVector filtersSplit; - - wpi::split(filters, filtersSplit, ',', -1, false); - for (auto val : filtersSplit) { - m_msgFilters[wpi::trim(val)] = true; - } + wpi::split(wpi::trim(msgFilters), ',', -1, false, + [&](auto val) { m_msgFilters[wpi::trim(val)] = true; }); } else { m_useMsgFiltering = false; } diff --git a/sysid/src/main/native/cpp/view/LogLoader.cpp b/sysid/src/main/native/cpp/view/LogLoader.cpp index d43f6ec4da1..8e0fe429b2e 100644 --- a/sysid/src/main/native/cpp/view/LogLoader.cpp +++ b/sysid/src/main/native/cpp/view/LogLoader.cpp @@ -128,7 +128,8 @@ void LogLoader::RebuildEntryTree() { } else { parts.emplace_back(prefix); } - wpi::split(mainpart, parts, '/', -1, false); + wpi::split(mainpart, '/', -1, false, + [&](auto part) { parts.emplace_back(part); }); // ignore a raw "/" key if (parts.empty()) { diff --git a/wpinet/src/main/native/cpp/HttpUtil.cpp b/wpinet/src/main/native/cpp/HttpUtil.cpp index 83d9ded426b..5a63039fdf4 100644 --- a/wpinet/src/main/native/cpp/HttpUtil.cpp +++ b/wpinet/src/main/native/cpp/HttpUtil.cpp @@ -98,9 +98,7 @@ std::string_view EscapeHTML(std::string_view str, SmallVectorImpl& buf) { } HttpQueryMap::HttpQueryMap(std::string_view query) { - SmallVector queryElems; - split(query, queryElems, '&', 100, false); - for (auto elem : queryElems) { + split(query, '&', 100, false, [&](auto elem) { auto [nameEsc, valueEsc] = split(elem, '='); SmallString<64> nameBuf; bool err = false; @@ -109,7 +107,7 @@ HttpQueryMap::HttpQueryMap(std::string_view query) { if (!err) { m_elems.try_emplace(name, valueEsc); } - } + }); } std::optional HttpQueryMap::Get( @@ -132,9 +130,7 @@ HttpPath::HttpPath(std::string_view path) { m_pathEnds.emplace_back(0); return; } - wpi::SmallVector pathElems; - split(path, pathElems, '/', 100, false); - for (auto elem : pathElems) { + split(path, '/', 100, false, [&](auto elem) { SmallString<64> buf; bool err = false; auto val = wpi::UnescapeURI(elem, buf, &err); @@ -144,7 +140,7 @@ HttpPath::HttpPath(std::string_view path) { } m_pathBuf += val; m_pathEnds.emplace_back(m_pathBuf.size()); - } + }); } bool HttpPath::startswith(size_t start, diff --git a/wpinet/src/main/native/cpp/WebSocketServer.cpp b/wpinet/src/main/native/cpp/WebSocketServer.cpp index 9b75aabaeda..c412c8a0bdf 100644 --- a/wpinet/src/main/native/cpp/WebSocketServer.cpp +++ b/wpinet/src/main/native/cpp/WebSocketServer.cpp @@ -31,14 +31,12 @@ WebSocketServerHelper::WebSocketServerHelper(HttpParser& req) { m_version = value; } else if (equals_lower(name, "sec-websocket-protocol")) { // Protocols are comma delimited, repeated headers add to list - SmallVector protocols; - split(value, protocols, ",", -1, false); - for (auto protocol : protocols) { + split(value, ',', -1, false, [&](auto protocol) { protocol = trim(protocol); if (!protocol.empty()) { m_protocols.emplace_back(protocol); } - } + }); } }); req.headersComplete.connect([&req, this](bool) { @@ -63,6 +61,21 @@ std::pair WebSocketServerHelper::MatchProtocol( return {false, {}}; } +std::pair WebSocketServerHelper::MatchProtocol( + std::span protocols) { + if (protocols.empty() && m_protocols.empty()) { + return {true, {}}; + } + for (auto protocol : protocols) { + for (auto&& clientProto : m_protocols) { + if (protocol == clientProto) { + return {true, protocol}; + } + } + } + return {false, {}}; +} + WebSocketServer::WebSocketServer(uv::Stream& stream, std::span protocols, ServerOptions options, const private_init&) @@ -101,9 +114,7 @@ WebSocketServer::WebSocketServer(uv::Stream& stream, } // Negotiate sub-protocol - SmallVector protocols{m_protocols.begin(), - m_protocols.end()}; - std::string_view protocol = m_helper.MatchProtocol(protocols).second; + std::string_view protocol = m_helper.MatchProtocol(m_protocols).second; // Disconnect our header reader m_dataConn.disconnect(); diff --git a/wpinet/src/main/native/include/wpinet/WebSocketServer.h b/wpinet/src/main/native/include/wpinet/WebSocketServer.h index 9d655d37dc0..469a3ca3d04 100644 --- a/wpinet/src/main/native/include/wpinet/WebSocketServer.h +++ b/wpinet/src/main/native/include/wpinet/WebSocketServer.h @@ -56,6 +56,18 @@ class WebSocketServerHelper { std::pair MatchProtocol( std::span protocols); + /** + * Try to find a match to the list of sub-protocols provided by the client. + * The list is priority ordered, so the first match wins. + * Only valid during and after the upgrade event. + * @param protocols Acceptable protocols + * @return Pair; first item is true if a match was made, false if not. + * Second item is the matched protocol if a match was made, otherwise + * is empty. + */ + std::pair MatchProtocol( + std::span protocols); + /** * Try to find a match to the list of sub-protocols provided by the client. * The list is priority ordered, so the first match wins. diff --git a/wpiutil/src/main/native/thirdparty/llvm/cpp/llvm/StringExtras.cpp b/wpiutil/src/main/native/thirdparty/llvm/cpp/llvm/StringExtras.cpp index f9b42944740..1503584754e 100644 --- a/wpiutil/src/main/native/thirdparty/llvm/cpp/llvm/StringExtras.cpp +++ b/wpiutil/src/main/native/thirdparty/llvm/cpp/llvm/StringExtras.cpp @@ -22,7 +22,6 @@ #include #include "wpi/SmallString.h" -#include "wpi/SmallVector.h" // strncasecmp() is not available on non-POSIX systems, so define an // alternative function here. @@ -118,65 +117,6 @@ bool wpi::ends_with_lower(std::string_view str, suffix.data(), suffix.size()) == 0; } -void wpi::split(std::string_view str, SmallVectorImpl& arr, - std::string_view separator, int maxSplit, - bool keepEmpty) noexcept { - std::string_view s = str; - - // Count down from maxSplit. When maxSplit is -1, this will just split - // "forever". This doesn't support splitting more than 2^31 times - // intentionally; if we ever want that we can make maxSplit a 64-bit integer - // but that seems unlikely to be useful. - while (maxSplit-- != 0) { - auto idx = s.find(separator); - if (idx == std::string_view::npos) { - break; - } - - // Push this split. - if (keepEmpty || idx > 0) { - arr.push_back(slice(s, 0, idx)); - } - - // Jump forward. - s = slice(s, idx + separator.size(), std::string_view::npos); - } - - // Push the tail. - if (keepEmpty || !s.empty()) { - arr.push_back(s); - } -} - -void wpi::split(std::string_view str, SmallVectorImpl& arr, - char separator, int maxSplit, bool keepEmpty) noexcept { - std::string_view s = str; - - // Count down from maxSplit. When maxSplit is -1, this will just split - // "forever". This doesn't support splitting more than 2^31 times - // intentionally; if we ever want that we can make maxSplit a 64-bit integer - // but that seems unlikely to be useful. - while (maxSplit-- != 0) { - size_t idx = s.find(separator); - if (idx == std::string_view::npos) { - break; - } - - // Push this split. - if (keepEmpty || idx > 0) { - arr.push_back(slice(s, 0, idx)); - } - - // Jump forward. - s = slice(s, idx + 1, std::string_view::npos); - } - - // Push the tail. - if (keepEmpty || !s.empty()) { - arr.push_back(s); - } -} - static unsigned GetAutoSenseRadix(std::string_view& str) noexcept { if (str.empty()) { return 10; diff --git a/wpiutil/src/main/native/thirdparty/llvm/include/wpi/StringExtras.h b/wpiutil/src/main/native/thirdparty/llvm/include/wpi/StringExtras.h index 23df3271387..9214f2b741a 100644 --- a/wpiutil/src/main/native/thirdparty/llvm/include/wpi/StringExtras.h +++ b/wpiutil/src/main/native/thirdparty/llvm/include/wpi/StringExtras.h @@ -17,6 +17,7 @@ #pragma once +#include #include #include #include @@ -541,42 +542,95 @@ constexpr std::pair rsplit( /** * Splits @p str into substrings around the occurrences of a separator string. * - * Each substring is stored in @p arr. If @p maxSplit is >= 0, at most - * @p maxSplit splits are done and consequently <= @p maxSplit + 1 - * elements are added to arr. - * If @p keepEmpty is false, empty strings are not added to @p arr. They - * still count when considering @p maxSplit + * Each substring is passed to the callback @p func. If @p maxSplit is >= 0, at + * most @p maxSplit splits are done and consequently <= @p maxSplit + 1 + * elements are provided to the callback. + * If @p keepEmpty is false, empty strings are not included. They + * still count when considering @p maxSplit. * An useful invariant is that - * separator.join(arr) == str if maxSplit == -1 and keepEmpty == true + * separator.join(all callbacks) == str if keepEmpty == true. * - * @param arr Where to put the substrings. * @param separator The string to split on. * @param maxSplit The maximum number of times the string is split. * @param keepEmpty True if empty substring should be added. - */ -void split(std::string_view str, SmallVectorImpl& arr, - std::string_view separator, int maxSplit = -1, - bool keepEmpty = true) noexcept; + * @param func Function to call for each substring. + */ +template F> +void split(std::string_view str, std::string_view separator, int maxSplit, + bool keepEmpty, F&& func) { + std::string_view s = str; + + // Count down from maxSplit. When maxSplit is -1, this will just split + // "forever". This doesn't support splitting more than 2^31 times + // intentionally; if we ever want that we can make maxSplit a 64-bit integer + // but that seems unlikely to be useful. + while (maxSplit-- != 0) { + auto idx = s.find(separator); + if (idx == std::string_view::npos) { + break; + } + + // Provide this split. + if (keepEmpty || idx > 0) { + func(slice(s, 0, idx)); + } + + // Jump forward. + s = slice(s, idx + separator.size(), std::string_view::npos); + } + + // Provide the tail. + if (keepEmpty || !s.empty()) { + func(s); + } +} /** * Splits @p str into substrings around the occurrences of a separator * character. * - * Each substring is stored in @p arr. If @p maxSplit is >= 0, at most - * @p maxSplit splits are done and consequently <= @p maxSplit + 1 - * elements are added to arr. - * If @p keepEmpty is false, empty strings are not added to @p arr. They - * still count when considering @p maxSplit + * Each substring is passed to the callback @p func. If @p maxSplit is >= 0, at + * most @p maxSplit splits are done and consequently <= @p maxSplit + 1 + * elements are provided to the callback. + * If @p keepEmpty is false, empty strings are not included. They + * still count when considering @p maxSplit. * An useful invariant is that - * separator.join(arr) == str if maxSplit == -1 and keepEmpty == true + * separator.join(all callbacks) == str if keepEmpty == true. * - * @param arr Where to put the substrings. * @param separator The character to split on. * @param maxSplit The maximum number of times the string is split. * @param keepEmpty True if empty substring should be added. - */ -void split(std::string_view str, SmallVectorImpl& arr, - char separator, int maxSplit = -1, bool keepEmpty = true) noexcept; + * @param func Function to call for each substring. + */ +template F> +void split(std::string_view str, char separator, int maxSplit, bool keepEmpty, + F&& func) { + std::string_view s = str; + + // Count down from maxSplit. When maxSplit is -1, this will just split + // "forever". This doesn't support splitting more than 2^31 times + // intentionally; if we ever want that we can make maxSplit a 64-bit integer + // but that seems unlikely to be useful. + while (maxSplit-- != 0) { + size_t idx = s.find(separator); + if (idx == std::string_view::npos) { + break; + } + + // Provide this split. + if (keepEmpty || idx > 0) { + func(slice(s, 0, idx)); + } + + // Jump forward. + s = slice(s, idx + 1, std::string_view::npos); + } + + // Provide the tail. + if (keepEmpty || !s.empty()) { + func(s); + } +} /** * Returns @p str with consecutive @p ch characters starting from the