mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-20 00:51:42 +00:00
[wpiutil] Rewrite StringExtras for std::string_view (#3394)
Remove unused functions and add StringRef-like convenience functions. Minimize header dependencies.
This commit is contained in:
252
wpiutil/src/main/native/cpp/StringExtras.cpp
Normal file
252
wpiutil/src/main/native/cpp/StringExtras.cpp
Normal file
@@ -0,0 +1,252 @@
|
||||
// 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.
|
||||
|
||||
//===-- StringExtras.cpp - Implement the StringExtras header --------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements the StringExtras.h header
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "wpi/StringExtras.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <string_view>
|
||||
|
||||
#include "wpi/SmallString.h"
|
||||
#include "wpi/SmallVector.h"
|
||||
#include "wpi/StringRef.h"
|
||||
|
||||
// strncasecmp() is not available on non-POSIX systems, so define an
|
||||
// alternative function here.
|
||||
static int ascii_strncasecmp(const char* lhs, const char* rhs,
|
||||
size_t length) noexcept {
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
unsigned char lhc = wpi::toLower(lhs[i]);
|
||||
unsigned char rhc = wpi::toLower(rhs[i]);
|
||||
if (lhc != rhc) {
|
||||
return lhc < rhc ? -1 : 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wpi::compare_lower(std::string_view lhs, std::string_view rhs) noexcept {
|
||||
if (int Res = ascii_strncasecmp(lhs.data(), rhs.data(),
|
||||
(std::min)(lhs.size(), rhs.size()))) {
|
||||
return Res;
|
||||
}
|
||||
if (lhs.size() == rhs.size()) {
|
||||
return 0;
|
||||
}
|
||||
return lhs.size() < rhs.size() ? -1 : 1;
|
||||
}
|
||||
|
||||
std::string_view::size_type wpi::find_lower(
|
||||
std::string_view str, char ch, std::string_view::size_type from) noexcept {
|
||||
char lch = toLower(ch);
|
||||
auto s = drop_front(str, from);
|
||||
while (!s.empty()) {
|
||||
if (toLower(s.front()) == lch) {
|
||||
return str.size() - s.size();
|
||||
}
|
||||
s.remove_prefix(1);
|
||||
}
|
||||
return std::string_view::npos;
|
||||
}
|
||||
|
||||
std::string_view::size_type wpi::find_lower(
|
||||
std::string_view str, std::string_view other,
|
||||
std::string_view::size_type from) noexcept {
|
||||
auto s = str.substr(from);
|
||||
while (s.size() >= other.size()) {
|
||||
if (starts_with_lower(s, other)) {
|
||||
return from;
|
||||
}
|
||||
s.remove_prefix(1);
|
||||
++from;
|
||||
}
|
||||
return std::string_view::npos;
|
||||
}
|
||||
|
||||
std::string_view::size_type wpi::rfind_lower(
|
||||
std::string_view str, char ch, std::string_view::size_type from) noexcept {
|
||||
from = (std::min)(from, str.size());
|
||||
auto data = str.data();
|
||||
std::string_view::size_type i = from;
|
||||
while (i != 0) {
|
||||
--i;
|
||||
if (toLower(data[i]) == toLower(ch)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return std::string_view::npos;
|
||||
}
|
||||
|
||||
std::string_view::size_type wpi::rfind_lower(std::string_view str,
|
||||
std::string_view other) noexcept {
|
||||
std::string_view::size_type n = other.size();
|
||||
if (n > str.size()) {
|
||||
return std::string_view::npos;
|
||||
}
|
||||
for (size_t i = str.size() - n + 1, e = 0; i != e;) {
|
||||
--i;
|
||||
if (equals_lower(str.substr(i, n), other)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return std::string_view::npos;
|
||||
}
|
||||
|
||||
bool wpi::starts_with_lower(std::string_view str,
|
||||
std::string_view prefix) noexcept {
|
||||
return str.size() >= prefix.size() &&
|
||||
ascii_strncasecmp(str.data(), prefix.data(), prefix.size()) == 0;
|
||||
}
|
||||
|
||||
bool wpi::ends_with_lower(std::string_view str,
|
||||
std::string_view suffix) noexcept {
|
||||
return str.size() >= suffix.size() &&
|
||||
ascii_strncasecmp(str.data() + str.size() - suffix.size(),
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
bool wpi::detail::GetAsUnsignedInteger(
|
||||
std::string_view str, unsigned radix,
|
||||
unsigned long long& result) noexcept { // NOLINT(runtime/int)
|
||||
return wpi::getAsUnsignedInteger(str, radix, result);
|
||||
}
|
||||
|
||||
bool wpi::detail::GetAsSignedInteger(
|
||||
std::string_view str, unsigned radix,
|
||||
long long& result) noexcept { // NOLINT(runtime/int)
|
||||
return wpi::getAsSignedInteger(str, radix, result);
|
||||
}
|
||||
|
||||
bool wpi::detail::ConsumeUnsignedInteger(
|
||||
std::string_view& str, unsigned radix,
|
||||
unsigned long long& result) noexcept { // NOLINT(runtime/int)
|
||||
wpi::StringRef sref = str;
|
||||
bool rv = wpi::consumeUnsignedInteger(sref, radix, result);
|
||||
str = sref;
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool wpi::detail::ConsumeSignedInteger(
|
||||
std::string_view& str, unsigned radix,
|
||||
long long& result) noexcept { // NOLINT(runtime/int)
|
||||
wpi::StringRef sref = str;
|
||||
bool rv = wpi::consumeSignedInteger(sref, radix, result);
|
||||
str = sref;
|
||||
return rv;
|
||||
}
|
||||
|
||||
template <>
|
||||
std::optional<float> wpi::parse_float<float>(std::string_view str) noexcept {
|
||||
if (str.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
wpi::SmallString<32> storage{str};
|
||||
char* end;
|
||||
float val = std::strtof(storage.c_str(), &end);
|
||||
if (*end != '\0') {
|
||||
return std::nullopt;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
template <>
|
||||
std::optional<double> wpi::parse_float<double>(std::string_view str) noexcept {
|
||||
if (str.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
wpi::SmallString<32> storage{str};
|
||||
char* end;
|
||||
double val = std::strtod(storage.c_str(), &end);
|
||||
if (*end != '\0') {
|
||||
return std::nullopt;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
template <>
|
||||
std::optional<long double> wpi::parse_float<long double>(
|
||||
std::string_view str) noexcept {
|
||||
if (str.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
wpi::SmallString<32> storage{str};
|
||||
char* end;
|
||||
long double val = std::strtold(storage.c_str(), &end);
|
||||
if (*end != '\0') {
|
||||
return std::nullopt;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
//===-- StringExtras.cpp - Implement the StringExtras header --------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements the StringExtras.h header
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "wpi/StringExtras.h"
|
||||
#include "wpi/SmallVector.h"
|
||||
#include "wpi/raw_ostream.h"
|
||||
using namespace wpi;
|
||||
|
||||
/// StrInStrNoCase - Portable version of strcasestr. Locates the first
|
||||
/// occurrence of string 's1' in string 's2', ignoring case. Returns
|
||||
/// the offset of s2 in s1 or npos if s2 cannot be found.
|
||||
StringRef::size_type wpi::StrInStrNoCase(StringRef s1, StringRef s2) {
|
||||
size_t N = s2.size(), M = s1.size();
|
||||
if (N > M)
|
||||
return StringRef::npos;
|
||||
for (size_t i = 0, e = M - N + 1; i != e; ++i)
|
||||
if (s1.substr(i, N).equals_lower(s2))
|
||||
return i;
|
||||
return StringRef::npos;
|
||||
}
|
||||
|
||||
/// getToken - This function extracts one token from source, ignoring any
|
||||
/// leading characters that appear in the Delimiters string, and ending the
|
||||
/// token at any of the characters that appear in the Delimiters string. If
|
||||
/// there are no tokens in the source string, an empty string is returned.
|
||||
/// The function returns a pair containing the extracted token and the
|
||||
/// remaining tail string.
|
||||
std::pair<StringRef, StringRef> wpi::getToken(StringRef Source,
|
||||
StringRef Delimiters) {
|
||||
// Figure out where the token starts.
|
||||
StringRef::size_type Start = Source.find_first_not_of(Delimiters);
|
||||
|
||||
// Find the next occurrence of the delimiter.
|
||||
StringRef::size_type End = Source.find_first_of(Delimiters, Start);
|
||||
|
||||
return std::make_pair(Source.slice(Start, End), Source.substr(End));
|
||||
}
|
||||
|
||||
/// SplitString - Split up the specified string according to the specified
|
||||
/// delimiters, appending the result fragments to the output list.
|
||||
void wpi::SplitString(StringRef Source,
|
||||
SmallVectorImpl<StringRef> &OutFragments,
|
||||
StringRef Delimiters) {
|
||||
std::pair<StringRef, StringRef> S = getToken(Source, Delimiters);
|
||||
while (!S.first.empty()) {
|
||||
OutFragments.push_back(S.first);
|
||||
S = getToken(S.second, Delimiters);
|
||||
}
|
||||
}
|
||||
|
||||
void wpi::printEscapedString(StringRef Name, raw_ostream &Out) {
|
||||
for (unsigned i = 0, e = Name.size(); i != e; ++i) {
|
||||
unsigned char C = Name[i];
|
||||
if (isPrint(C) && C != '\\' && C != '"')
|
||||
Out << C;
|
||||
else
|
||||
Out << '\\' << hexdigit(C >> 4) << hexdigit(C & 0x0F);
|
||||
}
|
||||
}
|
||||
|
||||
void wpi::printHTMLEscaped(StringRef String, raw_ostream &Out) {
|
||||
for (char C : String) {
|
||||
if (C == '&')
|
||||
Out << "&";
|
||||
else if (C == '<')
|
||||
Out << "<";
|
||||
else if (C == '>')
|
||||
Out << ">";
|
||||
else if (C == '\"')
|
||||
Out << """;
|
||||
else if (C == '\'')
|
||||
Out << "'";
|
||||
else
|
||||
Out << C;
|
||||
}
|
||||
}
|
||||
|
||||
void wpi::printLowerCase(StringRef String, raw_ostream &Out) {
|
||||
for (const char C : String)
|
||||
Out << toLower(C);
|
||||
}
|
||||
@@ -19,6 +19,21 @@
|
||||
|
||||
using namespace wpi;
|
||||
|
||||
/// HashString - Hash function for strings.
|
||||
///
|
||||
/// This is the Bernstein hash function.
|
||||
//
|
||||
// FIXME: Investigate whether a modified bernstein hash function performs
|
||||
// better: http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx
|
||||
// X*33+c -> X*33^c
|
||||
static inline unsigned HashString(std::string_view str,
|
||||
unsigned result = 0) noexcept {
|
||||
for (std::string_view::size_type i = 0, e = str.size(); i != e; ++i) {
|
||||
result = result * 33 + static_cast<unsigned char>(str[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Returns the number of buckets to allocate to ensure that the DenseMap can
|
||||
/// accommodate \p NumEntries without need to grow().
|
||||
static unsigned getMinBucketToReserveForEntries(unsigned NumEntries) {
|
||||
|
||||
Reference in New Issue
Block a user