mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-19 00:41:43 +00:00
[wpiutil] Change StringExtras split() to template (#7636)
It now calls back a function for each part rather than creating a SmallVector.
This commit is contained in:
@@ -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()) {
|
||||
|
||||
@@ -1015,7 +1015,8 @@ void NetworkTablesModel::RebuildTreeImpl(std::vector<TreeNode>* 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()) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include <imgui.h>
|
||||
#include <imgui_stdlib.h>
|
||||
#include <ntcore_cpp.h>
|
||||
#include <wpi/SmallVector.h>
|
||||
#include <wpi/StringExtras.h>
|
||||
|
||||
#include "glass/Context.h"
|
||||
@@ -69,12 +68,10 @@ void NetworkTablesSettings::Thread::Main() {
|
||||
(team = wpi::parse_integer<unsigned int>(serverTeam, 10))) {
|
||||
nt::SetServerTeam(m_inst, team.value(), m_port);
|
||||
} else {
|
||||
wpi::SmallVector<std::string_view, 4> serverNames;
|
||||
std::vector<std::pair<std::string_view, unsigned int>> 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -352,16 +352,15 @@ void InitializeTeamNumber(void) {
|
||||
std::string_view hostname{hostnameBuf, sizeof(hostnameBuf)};
|
||||
|
||||
// hostname is frc-{TEAM}-roborio
|
||||
// Split string around '-' (max of 2 splits), take the second element of the
|
||||
// resulting array.
|
||||
wpi::SmallVector<std::string_view> elements;
|
||||
wpi::split(hostname, elements, "-", 2);
|
||||
if (elements.size() < 3) {
|
||||
teamNumber = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
teamNumber = wpi::parse_integer<int32_t>(elements[1], 10).value_or(0);
|
||||
// Split string around '-' (max of 2 splits), take the second element
|
||||
teamNumber = 0;
|
||||
int i = 0;
|
||||
wpi::split(hostname, '-', 2, false, [&](auto part) {
|
||||
if (i == 1) {
|
||||
teamNumber = wpi::parse_integer<int32_t>(part, 10).value_or(0);
|
||||
}
|
||||
++i;
|
||||
});
|
||||
}
|
||||
|
||||
int32_t HAL_GetTeamNumber(void) {
|
||||
|
||||
@@ -245,7 +245,8 @@ void SerialHelper::QueryHubPaths(int32_t* status) {
|
||||
|
||||
wpi::SmallVector<std::string_view, 16> pathSplitVec;
|
||||
// Split path into individual directories
|
||||
wpi::split(path, pathSplitVec, '/', -1, false);
|
||||
wpi::split(path, '/', -1, false,
|
||||
[&](auto part) { pathSplitVec.emplace_back(part); });
|
||||
|
||||
// Find each individual item index
|
||||
int findusb = -1;
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <wpi/SmallVector.h>
|
||||
#include <wpi/StringExtras.h>
|
||||
#include <wpi/fs.h>
|
||||
#include <wpi/print.h>
|
||||
@@ -100,7 +99,6 @@ int HAL_LoadOneExtension(const char* library) {
|
||||
|
||||
int HAL_LoadExtensions(void) {
|
||||
int rc = 1;
|
||||
wpi::SmallVector<std::string_view, 2> 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#include <utility>
|
||||
|
||||
#include <wpi/MemoryBuffer.h>
|
||||
#include <wpi/SmallString.h>
|
||||
#include <wpi/StringExtras.h>
|
||||
#include <wpi/fs.h>
|
||||
#include <wpi/mutex.h>
|
||||
@@ -233,16 +232,15 @@ void InitializeTeamNumber(void) {
|
||||
std::string_view hostname{hostnameBuf, sizeof(hostnameBuf)};
|
||||
|
||||
// hostname is frc-{TEAM}-roborio
|
||||
// Split string around '-' (max of 2 splits), take the second element of the
|
||||
// resulting array.
|
||||
wpi::SmallVector<std::string_view> elements;
|
||||
wpi::split(hostname, elements, "-", 2);
|
||||
if (elements.size() < 3) {
|
||||
teamNumber = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
teamNumber = wpi::parse_integer<int32_t>(elements[1], 10).value_or(0);
|
||||
// Split string around '-' (max of 2 splits), take the second element
|
||||
teamNumber = 0;
|
||||
int i = 0;
|
||||
wpi::split(hostname, '-', 2, false, [&](auto part) {
|
||||
if (i == 1) {
|
||||
teamNumber = wpi::parse_integer<int32_t>(part, 10).value_or(0);
|
||||
}
|
||||
++i;
|
||||
});
|
||||
}
|
||||
|
||||
int32_t HAL_GetTeamNumber(void) {
|
||||
|
||||
@@ -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<std::string_view, 16> 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<std::string> 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<std::string_view, 16> 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;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <string>
|
||||
|
||||
#include <wpi/SmallString.h>
|
||||
#include <wpi/SmallVector.h>
|
||||
#include <wpi/StringExtras.h>
|
||||
#include <wpi/print.h>
|
||||
#include <wpinet/uv/util.h>
|
||||
@@ -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<std::string_view, 16> 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;
|
||||
}
|
||||
|
||||
@@ -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<std::string_view, 16> 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;
|
||||
}
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -98,9 +98,7 @@ std::string_view EscapeHTML(std::string_view str, SmallVectorImpl<char>& buf) {
|
||||
}
|
||||
|
||||
HttpQueryMap::HttpQueryMap(std::string_view query) {
|
||||
SmallVector<std::string_view, 16> 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<std::string_view> HttpQueryMap::Get(
|
||||
@@ -132,9 +130,7 @@ HttpPath::HttpPath(std::string_view path) {
|
||||
m_pathEnds.emplace_back(0);
|
||||
return;
|
||||
}
|
||||
wpi::SmallVector<std::string_view, 16> 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,
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "wpinet/WebSocketServer.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <wpi/StringExtras.h>
|
||||
@@ -31,14 +32,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<std::string_view, 2> 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) {
|
||||
@@ -53,7 +52,22 @@ std::pair<bool, std::string_view> WebSocketServerHelper::MatchProtocol(
|
||||
if (protocols.empty() && m_protocols.empty()) {
|
||||
return {true, {}};
|
||||
}
|
||||
for (auto protocol : protocols) {
|
||||
for (auto&& protocol : protocols) {
|
||||
for (auto&& clientProto : m_protocols) {
|
||||
if (protocol == clientProto) {
|
||||
return {true, protocol};
|
||||
}
|
||||
}
|
||||
}
|
||||
return {false, {}};
|
||||
}
|
||||
|
||||
std::pair<bool, std::string_view> WebSocketServerHelper::MatchProtocol(
|
||||
std::span<const std::string> protocols) {
|
||||
if (protocols.empty() && m_protocols.empty()) {
|
||||
return {true, {}};
|
||||
}
|
||||
for (auto&& protocol : protocols) {
|
||||
for (auto&& clientProto : m_protocols) {
|
||||
if (protocol == clientProto) {
|
||||
return {true, protocol};
|
||||
@@ -101,9 +115,7 @@ WebSocketServer::WebSocketServer(uv::Stream& stream,
|
||||
}
|
||||
|
||||
// Negotiate sub-protocol
|
||||
SmallVector<std::string_view, 2> 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();
|
||||
|
||||
@@ -56,6 +56,18 @@ class WebSocketServerHelper {
|
||||
std::pair<bool, std::string_view> MatchProtocol(
|
||||
std::span<const std::string_view> 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<bool, std::string_view> MatchProtocol(
|
||||
std::span<const std::string> 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.
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include <string_view>
|
||||
|
||||
#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<std::string_view>& 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<std::string_view>& 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;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <concepts>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
@@ -541,42 +542,95 @@ constexpr std::pair<std::string_view, std::string_view> 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.
|
||||
* @param func Function to call for each substring.
|
||||
*/
|
||||
void split(std::string_view str, SmallVectorImpl<std::string_view>& arr,
|
||||
std::string_view separator, int maxSplit = -1,
|
||||
bool keepEmpty = true) noexcept;
|
||||
template <std::invocable<std::string_view> 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.
|
||||
* @param func Function to call for each substring.
|
||||
*/
|
||||
void split(std::string_view str, SmallVectorImpl<std::string_view>& arr,
|
||||
char separator, int maxSplit = -1, bool keepEmpty = true) noexcept;
|
||||
template <std::invocable<std::string_view> 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
|
||||
|
||||
Reference in New Issue
Block a user