first commit

This commit is contained in:
William Dillon 2025-11-30 17:56:18 -05:00
commit f95c5aeddb
4 changed files with 615 additions and 0 deletions

7
Makefile Normal file
View File

@ -0,0 +1,7 @@
CXXFLAGS:=-std=c++23 -Wall -Wextra -pthread -g
gostrings_test: gostrings.hpp
$(CXX) $(CXXFLAGS) -o gostrings_test gostrings_test.cpp -lpthread -lgtest
clean:
rm -f gostrings_test

15
README.md Normal file
View File

@ -0,0 +1,15 @@
# unimplemented functions:
* func Clone(s string) string
* func EqualFold(s, t string) bool
* func FieldsFuncSeq(s string, f func(rune) bool) iter.Seq[string]
* func FieldsSeq(s string) iter.Seq[string]
* func Lines(s string) iter.Seq[string]
* func Map(mapping func(rune) rune, s string) string
* func SplitAfterSeq(s, sep string) iter.Seq[string]
* func SplitSeq(s, sep string) iter.Seq[string]
* func Title(s string) stringdeprecated
* func ToLowerSpecial(c unicode.SpecialCase, s string) string
* func ToTitle(s string) string
* func ToTitleSpecial(c unicode.SpecialCase, s string) string
* func ToUpperSpecial(c unicode.SpecialCase, s string) string
* func ToValidUTF8(s, replacement string) string

347
gostrings.hpp Normal file
View File

@ -0,0 +1,347 @@
#ifndef __GOSTRINGS_HPP__
#define __GOSTRINGS_HPP__
#include <string>
#include <vector>
namespace gostrings {
inline int Compare(const std::string &a, const std::string &b) {
if (a == b) return 0;
return (a < b) ? -1 : 1;
}
inline bool Contains(const std::string &s, const std::string &substr) {
return s.find(substr) != std::string::npos;
}
inline bool ContainsAny(const std::string &s, const std::string &chars) {
return s.find_first_of(chars) != std::string::npos;
}
inline bool ContainsFunc(const std::string &s, bool (*f)(char)) {
for (char c : s) {
if (f(c)) {
return true;
}
}
return false;
}
inline int Count(const std::string &s, const std::string &substr) {
if (substr.empty()) {
return static_cast<int>(s.size()) + 1;
}
int count = 0;
size_t pos = 0;
while ((pos = s.find(substr, pos)) != std::string::npos) {
++count;
pos += substr.length();
}
return count;
}
struct CutPrefixResult {
std::string after;
bool found;
};
struct CutSuffixResult {
std::string before;
bool found;
};
struct CutResult {
CutPrefixResult prefix;
CutSuffixResult suffix;
inline bool found() const {
return prefix.found && suffix.found;
}
};
inline CutResult Cut(const std::string &s, const std::string &sep) {
size_t pos = s.find(sep);
if (pos == std::string::npos) {
return {{"", false}, {"", false}};
}
return { {s.substr(pos + sep.length()), true}, {s.substr(0, pos), true} };
}
inline CutPrefixResult CutPrefix(const std::string &s, const std::string &prefix) {
if (s.substr(0, prefix.length()) == prefix) {
return {s.substr(prefix.length()), true};
}
return {"", false};
}
inline CutSuffixResult CutSuffix(const std::string &s, const std::string &suffix) {
if (s.length() >= suffix.length() &&
s.substr(s.length() - suffix.length()) == suffix) {
return {s.substr(0, s.length() - suffix.length()), true};
}
return {"", false};
}
inline std::vector<std::string> Fields(const std::string &s) {
std::vector<std::string> result;
size_t start = s.find_first_not_of(" \t\n\r\f\v");
while (start != std::string::npos) {
size_t end = s.find_first_of(" \t\n\r\f\v", start);
result.push_back(s.substr(start, end - start));
start = s.find_first_not_of(" \t\n\r\f\v", end);
}
return result;
}
inline std::vector<std::string> FieldsFunc(const std::string &s, bool (*f)(char)) {
std::vector<std::string> result;
size_t start = 0;
while (start < s.length()) {
while (start < s.length() && f(s[start])) {
++start;
}
if (start >= s.length()) break;
size_t end = start;
while (end < s.length() && !f(s[end])) {
++end;
}
result.push_back(s.substr(start, end - start));
start = end;
}
return result;
}
inline bool HasPrefix(const std::string &s, const std::string &prefix) {
return s.substr(0, prefix.length()) == prefix;
}
inline bool HasSuffix(const std::string &s, const std::string &suffix) {
return s.length() >= suffix.length() &&
s.substr(s.length() - suffix.length()) == suffix;
}
inline size_t Index(const std::string &s, const std::string &substr) {
size_t pos = s.find(substr);
return (pos == std::string::npos) ? -1 : pos;
}
inline size_t IndexAny(const std::string &s, const std::string &chars) {
size_t pos = s.find_first_of(chars);
return (pos == std::string::npos) ? -1 : pos;
}
inline size_t IndexByte(const std::string &s, char c) {
size_t pos = s.find(c);
return (pos == std::string::npos) ? -1 : pos;
}
inline size_t IndexFunc(const std::string &s, bool (*f)(char)) {
for (size_t i = 0; i < s.length(); ++i) {
if (f(s[i])) {
return i;
}
}
return -1;
}
inline std::string Join(const std::vector<std::string> &elems, const std::string &sep) {
std::string result;
for (size_t i = 0; i < elems.size(); ++i) {
result += elems[i];
if (i < elems.size() - 1) {
result += sep;
}
}
return result;
}
inline size_t LastIndex(const std::string &s, const std::string &substr) {
size_t pos = s.rfind(substr);
return (pos == std::string::npos) ? -1 : pos;
}
inline size_t LastIndexAny(const std::string &s, const std::string &chars) {
size_t pos = s.find_last_of(chars);
return (pos == std::string::npos) ? -1 : pos;
}
inline size_t LastIndexByte(const std::string &s, char c) {
size_t pos = s.rfind(c);
return (pos == std::string::npos) ? -1 : pos;
}
inline size_t LastIndexFunc(const std::string &s, bool (*f)(char)) {
for (size_t i = s.length(); i-- > 0;) {
if (f(s[i])) {
return i;
}
}
return -1;
}
inline std::string Repeat(const std::string &s, int count) {
if (count <= 0) return "";
std::string result;
for (int i = 0; i < count; ++i) {
result += s;
}
return result;
}
inline std::string Replace(const std::string &s, const std::string &old, const std::string &newstr, int n) {
std::string result = s;
size_t pos = 0;
int count = 0;
while ((pos = result.find(old, pos)) != std::string::npos) {
if (n != -1 && count >= n) break;
result.replace(pos, old.length(), newstr);
pos += newstr.length();
++count;
}
return result;
}
inline std::string ReplaceAll(const std::string &s, const std::string &old, const std::string &newstr) {
return Replace(s, old, newstr, -1);
}
inline std::vector<std::string> Split(const std::string &s, const std::string &sep) {
std::vector<std::string> result;
size_t start = 0;
size_t end;
while ((end = s.find(sep, start)) != std::string::npos) {
result.push_back(s.substr(start, end - start));
start = end + sep.length();
}
result.push_back(s.substr(start));
return result;
}
inline std::vector<std::string> SplitAfter(const std::string &s, const std::string &sep) {
std::vector<std::string> result;
size_t start = 0;
size_t end;
while ((end = s.find(sep, start)) != std::string::npos) {
result.push_back(s.substr(start, end + sep.length() - start));
start = end + sep.length();
}
result.push_back(s.substr(start));
return result;
}
inline std::vector<std::string> SplitAfterN(const std::string &s, const std::string &sep, int n) {
std::vector<std::string> result;
size_t start = 0;
size_t end;
int count = 0;
while (count < n - 1 && (end = s.find(sep, start)) != std::string::npos) {
result.push_back(s.substr(start, end + sep.length() - start));
start = end + sep.length();
++count;
}
result.push_back(s.substr(start));
return result;
}
inline std::vector<std::string> SplitN(const std::string &s, const std::string &sep, int n) {
std::vector<std::string> result;
size_t start = 0;
size_t end;
int count = 0;
while (count < n - 1 && (end = s.find(sep, start)) != std::string::npos) {
result.push_back(s.substr(start, end - start));
start = end + sep.length();
++count;
}
result.push_back(s.substr(start));
return result;
}
inline std::string ToLower(const std::string &s) {
std::string result = s;
for (char &c : result) {
c = static_cast<char>(tolower(static_cast<unsigned char>(c)));
}
return result;
}
inline std::string ToUpper(const std::string &s) {
std::string result = s;
for (char &c : result) {
c = static_cast<char>(toupper(static_cast<unsigned char>(c)));
}
return result;
}
inline std::string Trim(const std::string &s, const std::string &cutset) {
size_t start = s.find_first_not_of(cutset);
if (start == std::string::npos) return "";
size_t end = s.find_last_not_of(cutset);
return s.substr(start, end - start + 1);
}
inline std::string TrimFunc(const std::string &s, bool (*f)(char)) {
size_t start = 0;
while (start < s.length() && f(s[start])) {
++start;
}
if (start == s.length()) return "";
size_t end = s.length() - 1;
while (end > start && f(s[end])) {
--end;
}
return s.substr(start, end - start + 1);
}
inline std::string TrimLeft(const std::string &s, const std::string &cutset) {
size_t start = s.find_first_not_of(cutset);
if (start == std::string::npos) return "";
return s.substr(start);
}
inline std::string TrimLeftFunc(const std::string &s, bool (*f)(char)) {
size_t start = 0;
while (start < s.length() && f(s[start])) {
++start;
}
return s.substr(start);
}
inline std::string TrimPrefix(const std::string &s, const std::string &prefix) {
if (s.substr(0, prefix.length()) == prefix) {
return s.substr(prefix.length());
}
return s;
}
inline std::string TrimRight(const std::string &s, const std::string &cutset) {
size_t end = s.find_last_not_of(cutset);
if (end == std::string::npos) return "";
return s.substr(0, end + 1);
}
inline std::string TrimRightFunc(const std::string &s, bool (*f)(char)) {
size_t end = s.length();
while (end > 0 && f(s[end - 1])) {
--end;
}
return s.substr(0, end);
}
inline std::string TrimSpace(const std::string &s) {
return Trim(s, " \t\n\r\f\v");
}
inline std::string TrimSuffix(const std::string &s, const std::string &suffix) {
if (s.length() >= suffix.length() &&
s.substr(s.length() - suffix.length()) == suffix) {
return s.substr(0, s.length() - suffix.length());
}
return s;
}
} // namespace gostrings
#endif // __GOSTRINGS_HPP__

246
gostrings_test.cpp Normal file
View File

@ -0,0 +1,246 @@
#include "gostrings.hpp"
#include <gtest/gtest.h>
TEST(GoStringsTest, Compare) {
EXPECT_EQ(gostrings::Compare("apple", "apple"), 0);
EXPECT_EQ(gostrings::Compare("apple", "banana"), -1);
EXPECT_EQ(gostrings::Compare("banana", "apple"), 1);
}
TEST(GoStringsTest, Contains) {
EXPECT_TRUE(gostrings::Contains("hello world", "world"));
EXPECT_FALSE(gostrings::Contains("hello world", "mars"));
}
TEST(GoStringsTest, ContainsFunc) {
auto isVowel = [](char c) {
c = tolower(c);
return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u';
};
EXPECT_TRUE(gostrings::ContainsFunc("hello", isVowel));
EXPECT_FALSE(gostrings::ContainsFunc("rhythm", isVowel));
}
TEST(GoStringsTest, Count) {
EXPECT_EQ(gostrings::Count("ababab", "ab"), 3);
EXPECT_EQ(gostrings::Count("aaaa", "aa"), 2);
EXPECT_EQ(gostrings::Count("hello", "world"), 0);
EXPECT_EQ(gostrings::Count("hello", ""), 6);
}
TEST(GoStringsTest, Cut) {
auto result = gostrings::Cut("hello world", " ");
EXPECT_TRUE(result.found());
EXPECT_EQ(result.prefix.after, "world");
EXPECT_EQ(result.suffix.before, "hello");
result = gostrings::Cut("helloworld", " ");
EXPECT_FALSE(result.found());
}
TEST(GoStringsTest, CutPrefix) {
auto result = gostrings::CutPrefix("hello world", "hello ");
EXPECT_TRUE(result.found);
EXPECT_EQ(result.after, "world");
result = gostrings::CutPrefix("hello world", "world ");
EXPECT_FALSE(result.found);
}
TEST(GoStringsTest, CutSuffix) {
auto result = gostrings::CutSuffix("hello world", " world");
EXPECT_TRUE(result.found);
EXPECT_EQ(result.before, "hello");
result = gostrings::CutSuffix("hello world", " hello");
EXPECT_FALSE(result.found);
}
TEST(GoStringsTest, Fields) {
auto result = gostrings::Fields(" hello world ");
ASSERT_EQ(result.size(), 2);
EXPECT_EQ(result[0], "hello");
EXPECT_EQ(result[1], "world");
}
TEST(GoStringsTest, FieldsFunc) {
auto isSpace = [](char c) {
return c == ' ' || c == '\t' || c == '\n';
};
auto result = gostrings::FieldsFunc(" hello\tworld\n", isSpace);
ASSERT_EQ(result.size(), 2);
EXPECT_EQ(result[0], "hello");
EXPECT_EQ(result[1], "world");
}
TEST(GoStringsTest, HasPrefix) {
EXPECT_TRUE(gostrings::HasPrefix("hello world", "hello"));
EXPECT_FALSE(gostrings::HasPrefix("hello world", "world"));
}
TEST(GoStringsTest, Index) {
EXPECT_EQ(gostrings::Index("hello world", "world"), 6);
EXPECT_EQ(gostrings::Index("hello world", "mars"), -1);
}
TEST(GoStringsTest, IndexAny) {
EXPECT_EQ(gostrings::IndexAny("hello world", "aeiou"), 1);
EXPECT_EQ(gostrings::IndexAny("rhythm", "aeiou"), -1);
}
TEST(GoStringsTest, IndexByte) {
EXPECT_EQ(gostrings::IndexByte("hello world", 'o'), 4);
EXPECT_EQ(gostrings::IndexByte("hello world", 'z'), -1);
}
TEST(GoStringsTest, IndexFunc) {
auto isVowel = [](char c) {
c = tolower(c);
return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u';
};
EXPECT_EQ(gostrings::IndexFunc("hello", isVowel), 1);
EXPECT_EQ(gostrings::IndexFunc("rhythm", isVowel), -1);
}
TEST(GoStringsTest, Join) {
std::vector<std::string> elems = {"hello", "world"};
EXPECT_EQ(gostrings::Join(elems, " "), "hello world");
}
TEST(GoStringsTest, LastIndex) {
EXPECT_EQ(gostrings::LastIndex("ababcabc", "abc"), 5);
EXPECT_EQ(gostrings::LastIndex("hello world", "mars"), -1);
}
TEST(GoStringsTest, LastIndexAny) {
EXPECT_EQ(gostrings::LastIndexAny("hello world", "aeiou"), 7);
EXPECT_EQ(gostrings::LastIndexAny("rhythm", "aeiou"), -1);
}
TEST(GoStringsTest, LastIndexByte) {
EXPECT_EQ(gostrings::LastIndexByte("hello world", 'o'), 7);
EXPECT_EQ(gostrings::LastIndexByte("hello world", 'z'), -1);
}
TEST(GoStringsTest, LastIndexFunc) {
auto isVowel = [](char c) {
c = tolower(c);
return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u';
};
EXPECT_EQ(gostrings::LastIndexFunc("hello", isVowel), 4);
EXPECT_EQ(gostrings::LastIndexFunc("rhythm", isVowel), -1);
}
TEST(GoStringsTest, Repeat) {
EXPECT_EQ(gostrings::Repeat("ab", 3), "ababab");
EXPECT_EQ(gostrings::Repeat("xyz", 0), "");
}
TEST(GoStringsTest, Replace) {
EXPECT_EQ(gostrings::Replace("ababab", "ab", "xy", 2), "xyxyab");
EXPECT_EQ(gostrings::Replace("hello world", "world", "mars", -1), "hello mars");
}
TEST(GoStringsTest, ReplaceAll) {
EXPECT_EQ(gostrings::ReplaceAll("ababab", "ab", "xy"), "xyxyxy");
EXPECT_EQ(gostrings::ReplaceAll("hello world", "world", "mars"), "hello mars");
}
TEST(GoStringsTest, Split) {
auto result = gostrings::Split("a,b,c", ",");
ASSERT_EQ(result.size(), 3);
EXPECT_EQ(result[0], "a");
EXPECT_EQ(result[1], "b");
EXPECT_EQ(result[2], "c");
}
TEST(GoStringsTest, SplitAfter) {
auto result = gostrings::SplitAfter("a,b,c", ",");
ASSERT_EQ(result.size(), 3);
EXPECT_EQ(result[0], "a,");
EXPECT_EQ(result[1], "b,");
EXPECT_EQ(result[2], "c");
}
TEST(GoStringsTest, SplitAfterN) {
auto result = gostrings::SplitAfterN("a,b,c,d", ",", 3);
ASSERT_EQ(result.size(), 3);
EXPECT_EQ(result[0], "a,");
EXPECT_EQ(result[1], "b,");
EXPECT_EQ(result[2], "c,d");
}
TEST(GoStringsTest, SplitN) {
auto result = gostrings::SplitN("a,b,c,d", ",", 3);
ASSERT_EQ(result.size(), 3);
EXPECT_EQ(result[0], "a");
EXPECT_EQ(result[1], "b");
EXPECT_EQ(result[2], "c,d");
}
TEST(GoStringsTest, ToLower) {
EXPECT_EQ(gostrings::ToLower("HeLLo WoRLD"), "hello world");
}
TEST(GoStringsTest, ToUpper) {
EXPECT_EQ(gostrings::ToUpper("HeLLo WoRLD"), "HELLO WORLD");
}
TEST(GoStringsTest, Trim) {
EXPECT_EQ(gostrings::Trim(" hello world ", " "), "hello world");
}
TEST(GoStringsTest, TrimFunc) {
auto isSpace = [](char c) {
return c == ' ' || c == '\t' || c == '\n';
};
EXPECT_EQ(gostrings::TrimFunc(" \thello world\n ", isSpace), "hello world");
}
TEST(GoStringsTest, TrimLeft) {
EXPECT_EQ(gostrings::TrimLeft(" hello world ", " "), "hello world ");
}
TEST(GoStringsTest, TrimLeftFunc) {
auto isSpace = [](char c) {
return c == ' ' || c == '\t' || c == '\n';
};
EXPECT_EQ(gostrings::TrimLeftFunc(" \thello world\n ", isSpace), "hello world\n ");
}
TEST(GoStringsTest, TrimPrefix) {
auto result = gostrings::TrimPrefix("hello world", "hello ");
EXPECT_EQ(result, "world");
result = gostrings::TrimPrefix("hello world", "world ");
EXPECT_EQ(result, "hello world");
}
TEST(GoStringsTest, TrimRight) {
EXPECT_EQ(gostrings::TrimRight(" hello world ", " "), " hello world");
}
TEST(GoStringsTest, TrimRightFunc) {
auto isSpace = [](char c) {
return c == ' ' || c == '\t' || c == '\n';
};
EXPECT_EQ(gostrings::TrimRightFunc(" \thello world\n ", isSpace), " \thello world");
}
TEST(GoStringsTest, TrimSpace) {
EXPECT_EQ(gostrings::TrimSpace(" \thello world\n "), "hello world");
}
TEST(GoStringsTest, TrimSuffix) {
auto result = gostrings::TrimSuffix("hello world", " world");
EXPECT_EQ(result, "hello");
result = gostrings::TrimSuffix("hello world", " hello");
EXPECT_EQ(result, "hello world");
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}