Merge branch 'develop' for release v3.42.1
This commit is contained in:
@@ -365,7 +365,7 @@ namespace API::Subsonic
|
|||||||
void Response::JsonSerializer::serializeEscapedString(std::ostream& os, std::string_view str)
|
void Response::JsonSerializer::serializeEscapedString(std::ostream& os, std::string_view str)
|
||||||
{
|
{
|
||||||
os << '\"';
|
os << '\"';
|
||||||
StringUtils::writeJSEscapedString(os, str);
|
StringUtils::writeJsonEscapedString(os, str);
|
||||||
os << '\"';
|
os << '\"';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <unordered_map>
|
#include <utility>
|
||||||
|
|
||||||
#include <boost/algorithm/string/split.hpp>
|
#include <boost/algorithm/string/split.hpp>
|
||||||
#include <boost/algorithm/string/join.hpp>
|
#include <boost/algorithm/string/join.hpp>
|
||||||
@@ -33,6 +33,62 @@
|
|||||||
namespace StringUtils
|
namespace StringUtils
|
||||||
{
|
{
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
constexpr std::pair<char, std::string_view> jsEscapeChars[]
|
||||||
|
{
|
||||||
|
{ '\\', "\\\\" },
|
||||||
|
{ '\n', "\\n" },
|
||||||
|
{ '\r', "\\r" },
|
||||||
|
{ '\t', "\\t" },
|
||||||
|
{ '"', "\\\"" },
|
||||||
|
{ '\'', "\\\'" },
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr std::pair<char, std::string_view> jsonEscapeChars[]
|
||||||
|
{
|
||||||
|
{ '\\', "\\\\" },
|
||||||
|
{ '\n', "\\n" },
|
||||||
|
{ '\r', "\\r" },
|
||||||
|
{ '\t', "\\t" },
|
||||||
|
{ '"', "\\\"" },
|
||||||
|
};
|
||||||
|
|
||||||
|
template <std::size_t N>
|
||||||
|
std::string escape(std::string_view str, const std::pair<char, std::string_view>(&charsToEscape)[N])
|
||||||
|
{
|
||||||
|
std::string escaped;
|
||||||
|
escaped.reserve(str.length());
|
||||||
|
|
||||||
|
for (const char c : str)
|
||||||
|
{
|
||||||
|
auto it{ std::find_if(std::cbegin(charsToEscape), std::cend(charsToEscape), [c](const auto& entry) { return entry.first == c; }) };
|
||||||
|
if (it == std::cend(charsToEscape))
|
||||||
|
{
|
||||||
|
escaped += c;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
escaped += it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
return escaped;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <std::size_t N>
|
||||||
|
void writeEscapedString(std::ostream& os, std::string_view str, const std::pair<char, std::string_view>(&charsToEscape)[N])
|
||||||
|
{
|
||||||
|
for (const char c : str)
|
||||||
|
{
|
||||||
|
auto itEntry{ std::find_if(std::cbegin(charsToEscape), std::cend(charsToEscape), [=](const auto& entry) { return entry.first == c;}) };
|
||||||
|
if (itEntry != std::cend(charsToEscape))
|
||||||
|
os << itEntry->second;
|
||||||
|
else
|
||||||
|
os << c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool readList(const std::string& str, const std::string& separators, std::list<std::string>& results)
|
bool readList(const std::string& str, const std::string& separators, std::list<std::string>& results)
|
||||||
{
|
{
|
||||||
std::string curStr;
|
std::string curStr;
|
||||||
@@ -221,54 +277,22 @@ namespace StringUtils
|
|||||||
|
|
||||||
std::string jsEscape(std::string_view str)
|
std::string jsEscape(std::string_view str)
|
||||||
{
|
{
|
||||||
static const std::unordered_map<char, std::string_view> escapeMap
|
return escape(str, jsEscapeChars);
|
||||||
{
|
|
||||||
{ '\\', "\\\\" },
|
|
||||||
{ '\n', "\\n" },
|
|
||||||
{ '\r', "\\r" },
|
|
||||||
{ '\t', "\\t" },
|
|
||||||
{ '"', "\\\"" },
|
|
||||||
{ '\'', "\\\'" },
|
|
||||||
};
|
|
||||||
|
|
||||||
std::string escaped;
|
|
||||||
escaped.reserve(str.length());
|
|
||||||
|
|
||||||
for (const char c : str)
|
|
||||||
{
|
|
||||||
auto it{ escapeMap.find(c) };
|
|
||||||
if (it == std::cend(escapeMap))
|
|
||||||
{
|
|
||||||
escaped += c;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
escaped += it->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
return escaped;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeJSEscapedString(std::ostream& os, std::string_view str)
|
void writeJSEscapedString(std::ostream& os, std::string_view str)
|
||||||
{
|
{
|
||||||
static constexpr std::pair<char, std::string_view> charsToEscape[]
|
writeEscapedString(os, str, jsEscapeChars);
|
||||||
{
|
|
||||||
{'\\', "\\\\" },
|
|
||||||
{ '\n', "\\n" },
|
|
||||||
{ '\r', "\\r" },
|
|
||||||
{ '\t', "\\t" },
|
|
||||||
{ '"', "\\\"" },
|
|
||||||
{ '\'', "\\\'" },
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const char c : str)
|
|
||||||
{
|
|
||||||
auto itEntry{ std::find_if(std::cbegin(charsToEscape), std::cend(charsToEscape), [=](const auto& entry) { return entry.first == c;}) };
|
|
||||||
if (itEntry != std::cend(charsToEscape))
|
|
||||||
os << itEntry->second;
|
|
||||||
else
|
|
||||||
os << c;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string jsonEscape(std::string_view str)
|
||||||
|
{
|
||||||
|
return escape(str, jsonEscapeChars);
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeJsonEscapedString(std::ostream& os, std::string_view str)
|
||||||
|
{
|
||||||
|
writeEscapedString(os, str, jsonEscapeChars);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string escapeString(std::string_view str, std::string_view charsToEscape, char escapeChar)
|
std::string escapeString(std::string_view str, std::string_view charsToEscape, char escapeChar)
|
||||||
|
|||||||
@@ -84,7 +84,9 @@ namespace StringUtils {
|
|||||||
[[nodiscard]] std::string replaceInString(std::string_view str, const std::string& from, const std::string& to);
|
[[nodiscard]] std::string replaceInString(std::string_view str, const std::string& from, const std::string& to);
|
||||||
|
|
||||||
[[nodiscard]] std::string jsEscape(std::string_view str);
|
[[nodiscard]] std::string jsEscape(std::string_view str);
|
||||||
|
[[nodiscard]] std::string jsonEscape(std::string_view str);
|
||||||
void writeJSEscapedString(std::ostream& os, std::string_view str);
|
void writeJSEscapedString(std::ostream& os, std::string_view str);
|
||||||
|
void writeJsonEscapedString(std::ostream& os, std::string_view str);
|
||||||
|
|
||||||
[[nodiscard]] std::string escapeString(std::string_view str, std::string_view charsToEscape, char escapeChar);
|
[[nodiscard]] std::string escapeString(std::string_view str, std::string_view charsToEscape, char escapeChar);
|
||||||
|
|
||||||
|
|||||||
@@ -27,50 +27,50 @@
|
|||||||
TEST(StringUtils, splitString)
|
TEST(StringUtils, splitString)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
const std::string test {"a"};
|
const std::string test{ "a" };
|
||||||
|
|
||||||
const std::vector<std::string_view> strings {StringUtils::splitString(test, "")};
|
const std::vector<std::string_view> strings{ StringUtils::splitString(test, "") };
|
||||||
ASSERT_EQ(strings.size(), 1);
|
ASSERT_EQ(strings.size(), 1);
|
||||||
EXPECT_EQ(strings.front() , "a");
|
EXPECT_EQ(strings.front(), "a");
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const std::string test {"a b"};
|
const std::string test{ "a b" };
|
||||||
|
|
||||||
const std::vector<std::string_view> strings {StringUtils::splitString(test, "|")};
|
const std::vector<std::string_view> strings{ StringUtils::splitString(test, "|") };
|
||||||
ASSERT_EQ(strings.size(), 1);
|
ASSERT_EQ(strings.size(), 1);
|
||||||
EXPECT_EQ(strings.front(), "a b");
|
EXPECT_EQ(strings.front(), "a b");
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const std::string test {" a"};
|
const std::string test{ " a" };
|
||||||
|
|
||||||
const std::vector<std::string_view> strings {StringUtils::splitString(test, " ")};
|
const std::vector<std::string_view> strings{ StringUtils::splitString(test, " ") };
|
||||||
ASSERT_EQ(strings.size(), 1);
|
ASSERT_EQ(strings.size(), 1);
|
||||||
EXPECT_EQ(strings.front(), "a");
|
EXPECT_EQ(strings.front(), "a");
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const std::string test {"a "};
|
const std::string test{ "a " };
|
||||||
|
|
||||||
const std::vector<std::string_view> strings {StringUtils::splitString(test, " ")};
|
const std::vector<std::string_view> strings{ StringUtils::splitString(test, " ") };
|
||||||
ASSERT_EQ(strings.size(), 1);
|
ASSERT_EQ(strings.size(), 1);
|
||||||
EXPECT_EQ(strings.front(), "a");
|
EXPECT_EQ(strings.front(), "a");
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const std::string test {"a b"};
|
const std::string test{ "a b" };
|
||||||
|
|
||||||
const std::vector<std::string_view> strings {StringUtils::splitString(test, " ")};
|
const std::vector<std::string_view> strings{ StringUtils::splitString(test, " ") };
|
||||||
ASSERT_EQ(strings.size(), 2);
|
ASSERT_EQ(strings.size(), 2);
|
||||||
EXPECT_EQ(strings.front(), "a");
|
EXPECT_EQ(strings.front(), "a");
|
||||||
EXPECT_EQ(strings.back(), "b");
|
EXPECT_EQ(strings.back(), "b");
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const std::string test {"a b,c|defgh "};
|
const std::string test{ "a b,c|defgh " };
|
||||||
|
|
||||||
const std::vector<std::string_view> strings {StringUtils::splitString(test, " ,|")};
|
const std::vector<std::string_view> strings{ StringUtils::splitString(test, " ,|") };
|
||||||
ASSERT_EQ(strings.size(), 4);
|
ASSERT_EQ(strings.size(), 4);
|
||||||
EXPECT_EQ(strings[0], "a");
|
EXPECT_EQ(strings[0], "a");
|
||||||
EXPECT_EQ(strings[1], "b");
|
EXPECT_EQ(strings[1], "b");
|
||||||
@@ -83,24 +83,40 @@ TEST(StringUtils, splitString)
|
|||||||
TEST(StringUtils, splitStringCopy)
|
TEST(StringUtils, splitStringCopy)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
const std::string test {"test=foo"};
|
const std::string test{ "test=foo" };
|
||||||
|
|
||||||
const std::vector<std::string> strings {StringUtils::splitStringCopy(test, "=")};
|
const std::vector<std::string> strings{ StringUtils::splitStringCopy(test, "=") };
|
||||||
ASSERT_EQ(strings.size(), 2);
|
ASSERT_EQ(strings.size(), 2);
|
||||||
EXPECT_EQ(strings[0], "test");
|
EXPECT_EQ(strings[0], "test");
|
||||||
EXPECT_EQ(strings[1], "foo");
|
EXPECT_EQ(strings[1], "foo");
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const std::string test {"test=foo bar"};
|
const std::string test{ "test=foo bar" };
|
||||||
|
|
||||||
const std::vector<std::string> strings {StringUtils::splitStringCopy(test, "=")};
|
const std::vector<std::string> strings{ StringUtils::splitStringCopy(test, "=") };
|
||||||
ASSERT_EQ(strings.size(), 2);
|
ASSERT_EQ(strings.size(), 2);
|
||||||
EXPECT_EQ(strings[0], "test");
|
EXPECT_EQ(strings[0], "test");
|
||||||
EXPECT_EQ(strings[1], "foo bar");
|
EXPECT_EQ(strings[1], "foo bar");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(StringUtils, escapeJSString)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(StringUtils::jsEscape(""), "");
|
||||||
|
EXPECT_EQ(StringUtils::jsEscape(R"(Test'.mp3)"), R"(Test\'.mp3)");
|
||||||
|
EXPECT_EQ(StringUtils::jsEscape(R"(Test"".mp3)"), R"(Test\"\".mp3)");
|
||||||
|
EXPECT_EQ(StringUtils::jsEscape(R"(\Test\.mp3)"), R"(\\Test\\.mp3)");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(StringUtils, escapeJsonString)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(StringUtils::jsonEscape(""), "");
|
||||||
|
EXPECT_EQ(StringUtils::jsonEscape(R"(Test'.mp3)"), R"(Test'.mp3)");
|
||||||
|
EXPECT_EQ(StringUtils::jsonEscape(R"(Test"".mp3)"), R"(Test\"\".mp3)");
|
||||||
|
EXPECT_EQ(StringUtils::jsonEscape(R"(\Test\.mp3)"), R"(\\Test\\.mp3)");
|
||||||
|
}
|
||||||
|
|
||||||
TEST(StringUtils, escapeString)
|
TEST(StringUtils, escapeString)
|
||||||
{
|
{
|
||||||
EXPECT_EQ(StringUtils::escapeString("", "*", ' '), "");
|
EXPECT_EQ(StringUtils::escapeString("", "*", ' '), "");
|
||||||
@@ -145,7 +161,7 @@ TEST(StringUtils, capitalize)
|
|||||||
|
|
||||||
for (const TestCase& test : tests)
|
for (const TestCase& test : tests)
|
||||||
{
|
{
|
||||||
std::string str {test.input};
|
std::string str{ test.input };
|
||||||
StringUtils::capitalize(str);
|
StringUtils::capitalize(str);
|
||||||
EXPECT_EQ(str, test.expectedOutput) << " str was '" << test.input << "'";
|
EXPECT_EQ(str, test.expectedOutput) << " str was '" << test.input << "'";
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user