Add string view variant of Star::String and use it

it's 1:30 AM AGAIN !! !!!!!
This might have broken the inventory icons of custom hats a little, need to look into that
This commit is contained in:
Kae 2023-06-26 01:42:18 +10:00
parent 13a74602bd
commit 09d26d43b5
19 changed files with 745 additions and 156 deletions

View File

@ -844,7 +844,7 @@ shared_ptr<Assets::AssetData> Assets::loadImage(AssetPath const& path) const {
return {};
StringMap<ImageConstPtr> references;
StringList referencePaths;
path.directives.forEach([&](auto const& entry) {
path.directives.forEach([&](auto const& entry, Directives const& directives) {
addImageOperationReferences(entry.operation, referencePaths);
}); // TODO: This can definitely be better, was changed quickly to support the new Directives.
@ -861,7 +861,7 @@ shared_ptr<Assets::AssetData> Assets::loadImage(AssetPath const& path) const {
return unlockDuring([&]() {
auto newData = make_shared<ImageData>();
Image newImage = *source->image;
path.directives.forEach([&](auto const& entry) {
path.directives.forEach([&](auto const& entry, Directives const& directives) {
if (auto error = entry.operation.ptr<ErrorImageOperation>())
std::rethrow_exception(error->exception);
else

View File

@ -106,6 +106,7 @@ SET (star_core_HEADERS
StarStaticRandom.hpp
StarStaticVector.hpp
StarString.hpp
StarStringView.hpp
StarStrongTypedef.hpp
StarTcp.hpp
StarThread.hpp
@ -163,6 +164,7 @@ SET (star_core_SOURCES
StarShellParser.cpp
StarSocket.cpp
StarString.cpp
StarStringView.cpp
StarTcp.cpp
StarThread.cpp
StarTime.cpp

View File

@ -175,9 +175,9 @@ std::ostream& operator<<(std::ostream& os, AssetPath const& rhs) {
os << *rhs.subPath;
}
rhs.directives.forEach([&](auto const& entry) {
rhs.directives.forEach([&](auto const& entry, Directives const& directives) {
os << "?";
os << entry.string;
os << entry.string(*directives.shared);
});
return os;

View File

@ -302,7 +302,7 @@ uint32_t Color::toUint32() const {
return val;
}
Color Color::fromHex(String const& s) {
Color Color::fromHex(StringView s) {
return Color::rgba(hexToVec4B(s));
}
@ -601,7 +601,7 @@ Vec4B Color::hueShiftVec4B(Vec4B color, float hue) {
}
}
Vec4B Color::hexToVec4B(String const& s) {
Vec4B Color::hexToVec4B(StringView s) {
Array<uint8_t, 4> cbytes;
if (s.utf8Size() == 3) {

View File

@ -1,7 +1,7 @@
#ifndef STAR_COLOR_HPP
#define STAR_COLOR_HPP
#include "StarString.hpp"
#include "StarStringView.hpp"
#include "StarVector.hpp"
namespace Star {
@ -58,7 +58,7 @@ public:
static Color gray(uint8_t g);
// Only supports 8 bit color
static Color fromHex(String const& s);
static Color fromHex(StringView s);
// #AARRGGBB
static Color fromUint32(uint32_t v);
@ -67,7 +67,7 @@ public:
static Color temperature(float temp);
static Vec4B hueShiftVec4B(Vec4B color, float hue);
static Vec4B Color::hexToVec4B(String const& s);
static Vec4B Color::hexToVec4B(StringView s);
// Black
Color();

View File

@ -1,8 +1,8 @@
#include "StarPch.hpp"
#ifndef STAR_CONFIG_HPP
#define STAR_CONFIG_HPP
#include "StarPch.hpp"
namespace Star {
// Some really common std namespace includes

View File

@ -1,92 +1,125 @@
#include "StarDirectives.hpp"
#include "StarImage.hpp"
#include "StarImageProcessing.hpp"
#include "StarDirectives.hpp"
#include "StarXXHash.hpp"
#include "StarLogging.hpp"
namespace Star {
Directives::Entry::Entry(ImageOperation&& newOperation, String&& newString) {
Directives::Entry::Entry(ImageOperation&& newOperation, size_t strBegin, size_t strLength) {
operation = move(newOperation);
string = move(newString);
begin = strBegin;
length = strLength;
}
Directives::Entry::Entry(ImageOperation const& newOperation, String const& newString) {
Directives::Entry::Entry(ImageOperation const& newOperation, size_t strBegin, size_t strLength) {
operation = newOperation;
string = newString;
begin = strBegin;
length = strLength;
}
Directives::Entry::Entry(Entry const& other) {
operation = other.operation;
string = other.string;
begin = other.begin;
length = other.length;
}
Directives::Directives() : hash(0) {}
Directives::Directives(String const& directives) : hash(0) {
string = directives;
parse(string);
StringView Directives::Entry::string(Shared const& parent) const {
StringView result(parent.string);
result = result.substr(begin, length);
return result;
}
Directives::Directives(String&& directives) : hash(0) {
string = move(directives);
parse(string);
bool Directives::Shared::empty() const {
return entries.empty();
}
Directives::Directives(const char* directives) : hash(0), string(directives) {
parse(string);
}
Directives::Directives(List<Entry>&& newEntries) {
entries = std::make_shared<List<Entry> const>(move(newEntries));
String newString;
string = move(buildString(newString));
Directives::Shared::Shared(List<Entry>&& givenEntries, String&& givenString) {
entries = move(givenEntries);
string = move(givenString);
hash = XXH3_64bits(string.utf8Ptr(), string.utf8Size());
}
void Directives::parse(String const& directives) {
Directives::Directives() {}
Directives::Directives(String const& directives) {
parse(String(directives));
}
Directives::Directives(String&& directives) {
parse(move(directives));
}
Directives::Directives(const char* directives) {
parse(directives);
}
void Directives::parse(String&& directives) {
if (directives.empty())
return;
List<Entry> newList;
StringList split = directives.split('?');
newList.reserve(split.size());
for (String& str : split) {
if (!str.empty()) {
StringView(directives).forEachSplitView("?", [&](StringView split, size_t beg, size_t end) {
if (!split.empty()) {
try {
ImageOperation operation = imageOperationFromString(str);
newList.emplace_back(move(operation), move(str));
} catch (StarException const& e) {
newList.emplace_back(ErrorImageOperation{ std::current_exception() }, move(str));
ImageOperation operation = imageOperationFromString(split);
newList.emplace_back(move(operation), beg, end);
}
catch (StarException const& e) {
newList.emplace_back(ErrorImageOperation{ std::current_exception() }, beg, end);
}
}
}
});
if (newList.empty())
return;
entries = std::make_shared<List<Entry> const>(move(newList));
hash = XXH3_64bits(directives.utf8Ptr(), directives.utf8Size());
//if (directives.utf8Size() > 1000)
// Logger::logf(LogLevel::Debug, "Directives: Parsed %u character long string", directives.utf8Size());
shared = std::make_shared<Shared const>(move(newList), move(directives));
}
String& Directives::buildString(String& out) const {
if (entries) {
for (auto& entry : *entries) {
out += "?";
out += entry.string;
String Directives::string() const {
if (!shared)
return "";
else
return shared->string;
}
String const* Directives::stringPtr() const {
if (!shared)
return nullptr;
else
return &shared->string;
}
String Directives::buildString() const {
String built;
if (shared) {
for (auto& entry : shared->entries) {
built += "?";
built += entry.string(*shared);
}
}
return built;
}
String& Directives::addToString(String& out) const {
if (!empty())
out += shared->string;
return out;
}
String Directives::toString() const {
return string;
size_t Directives::hash() const {
return shared ? shared->hash : 0;
}
size_t Directives::size() const {
return shared ? shared->entries.size() : 0;
}
inline bool Directives::empty() const {
return !entries || entries->empty();
return !shared || shared->empty();
}
inline Directives::operator bool() const {
@ -98,13 +131,16 @@ DataStream& operator>>(DataStream& ds, Directives& directives) {
String string;
ds.read(string);
directives.parse(string);
directives.parse(move(string));
return ds;
}
DataStream& operator<<(DataStream& ds, Directives const& directives) {
ds.write(directives.toString());
if (directives)
ds.write(directives.shared->string);
else
ds.write(String());
return ds;
}
@ -117,7 +153,7 @@ DirectivesGroup::DirectivesGroup(String const& directives) : m_count(0) {
Directives parsed(directives);
if (parsed) {
m_directives.emplace_back(move(parsed));
m_count = m_directives.back().entries->size();
m_count = m_directives.back().size();
}
}
DirectivesGroup::DirectivesGroup(String&& directives) : m_count(0) {
@ -129,7 +165,7 @@ DirectivesGroup::DirectivesGroup(String&& directives) : m_count(0) {
Directives parsed(move(directives));
if (parsed) {
m_directives.emplace_back(move(parsed));
m_count = m_directives.back().entries->size();
m_count = m_directives.back().size();
}
}
@ -153,14 +189,7 @@ bool DirectivesGroup::compare(DirectivesGroup const& other) const {
void DirectivesGroup::append(Directives const& directives) {
m_directives.emplace_back(directives);
if (directives.entries)
m_count += m_directives.back().entries->size();
}
void DirectivesGroup::append(List<Directives::Entry>&& entries) {
size_t size = entries.size();
m_directives.emplace_back(move(entries));
m_count += size;
m_count += m_directives.back().size();
}
void DirectivesGroup::clear() {
@ -181,23 +210,24 @@ inline String DirectivesGroup::toString() const {
void DirectivesGroup::addToString(String& string) const {
for (auto& directives : m_directives)
string += directives.string;
if (directives.shared)
string += directives.shared->string;
}
void DirectivesGroup::forEach(DirectivesCallback callback) const {
for (auto& directives : m_directives) {
if (directives.entries) {
for (auto& entry : *directives.entries)
callback(entry);
if (directives.shared) {
for (auto& entry : directives.shared->entries)
callback(entry, directives);
}
}
}
bool DirectivesGroup::forEachAbortable(AbortableDirectivesCallback callback) const {
for (auto& directives : m_directives) {
if (directives.entries) {
for (auto& entry : *directives.entries) {
if (!callback(entry))
if (directives.shared) {
for (auto& entry : directives.shared->entries) {
if (!callback(entry, directives))
return false;
}
}
@ -213,7 +243,7 @@ inline Image DirectivesGroup::applyNewImage(Image const& image) const {
}
void DirectivesGroup::applyExistingImage(Image& image) const {
forEach([&](auto const& entry) {
forEach([&](auto const& entry, Directives const& directives) {
if (auto error = entry.operation.ptr<ErrorImageOperation>())
std::rethrow_exception(error->exception);
else
@ -223,8 +253,10 @@ void DirectivesGroup::applyExistingImage(Image& image) const {
inline size_t DirectivesGroup::hash() const {
XXHash3 hasher;
for (auto& directives : m_directives)
hasher.push((const char*)&directives.hash, sizeof(directives.hash));
for (auto& directives : m_directives) {
size_t hash = directives.hash();
hasher.push((const char*)&hash, sizeof(hash));
}
return hasher.digest();
}
@ -245,7 +277,7 @@ DataStream& operator>>(DataStream& ds, DirectivesGroup& directivesGroup) {
String string;
ds.read(string);
directivesGroup = move(DirectivesGroup(move(string)));
directivesGroup = DirectivesGroup(move(string));
return ds;
}

View File

@ -4,6 +4,7 @@
#include "StarImageProcessing.hpp"
#include "StarHash.hpp"
#include "StarDataStream.hpp"
#include "StarStringView.hpp"
namespace Star {
@ -14,33 +15,46 @@ STAR_EXCEPTION(DirectivesException, StarException);
// Kae: My attempt at reducing memory allocation and per-frame string parsing for extremely long directives
class Directives {
public:
struct Shared;
struct Entry {
ImageOperation operation;
String string; // One day, we can make this a string_view pointing to Entry::string.
size_t begin;
size_t length;
Entry(ImageOperation&& newOperation, String&& newString);
Entry(ImageOperation const& newOperation, String const& newString);
inline StringView string(Shared const& parent) const;
Entry(ImageOperation&& newOperation, size_t begin, size_t end);
Entry(ImageOperation const& newOperation, size_t begin, size_t end);
Entry(Entry const& other);
};
struct Shared {
List<Entry> entries;
String string;
size_t hash = 0;
bool empty() const;
Shared(List<Entry>&& givenEntries, String&& givenString);
};
Directives();
Directives(String const& directives);
Directives(String&& directives);
Directives(const char* directives);
Directives(List<Entry>&& entries);
void parse(String const& directives);
String& buildString(String& out) const;
String toString() const;
void parse(String&& directives);
String string() const;
String const* stringPtr() const;
String buildString() const;
String& addToString(String& out) const;
size_t hash() const;
size_t size() const;
bool empty() const;
operator bool() const;
friend DataStream& operator>>(DataStream& ds, Directives& directives);
friend DataStream& operator<<(DataStream& ds, Directives const& directives);
std::shared_ptr<List<Entry> const> entries;
size_t hash = 0;
String string;
std::shared_ptr<Shared const> shared;
};
class DirectivesGroup {
@ -49,13 +63,10 @@ public:
DirectivesGroup(String const& directives);
DirectivesGroup(String&& directives);
void parseDirectivesIntoLeaf(String const& directives);
bool empty() const;
operator bool() const;
bool compare(DirectivesGroup const& other) const;
void append(Directives const& other);
void append(List<Directives::Entry>&& entries);
void clear();
DirectivesGroup& operator+=(Directives const& other);
@ -63,8 +74,8 @@ public:
String toString() const;
void addToString(String& string) const;
typedef function<void(Directives::Entry const&)> DirectivesCallback;
typedef function<bool(Directives::Entry const&)> AbortableDirectivesCallback;
typedef function<void(Directives::Entry const&, Directives const&)> DirectivesCallback;
typedef function<bool(Directives::Entry const&, Directives const&)> AbortableDirectivesCallback;
void forEach(DirectivesCallback callback) const;
bool forEachAbortable(AbortableDirectivesCallback callback) const;

View File

@ -4,6 +4,7 @@
#include "StarLexicalCast.hpp"
#include "StarColor.hpp"
#include "StarImage.hpp"
#include "StarStringView.hpp"
namespace Star {
@ -145,9 +146,13 @@ FadeToColorImageOperation::FadeToColorImageOperation(Vec3B color, float amount)
}
}
ImageOperation imageOperationFromString(String const& string) {
ImageOperation imageOperationFromString(StringView string) {
try {
auto bits = string.splitAny("=;");
List<StringView> bits;
string.forEachSplitAnyView("=;", [&](StringView split, size_t, size_t) {
bits.emplace_back(split);
});
String type = bits.at(0);
if (type == "hueshift") {
@ -184,7 +189,7 @@ ImageOperation imageOperationFromString(String const& string) {
else
operation.mode = AlphaMaskImageOperation::Subtractive;
operation.maskImages = bits.at(1).split('+');
operation.maskImages = String(bits.at(1)).split('+');
if (bits.size() > 2)
operation.offset[0] = lexicalCast<int>(bits.at(2));
@ -202,7 +207,7 @@ ImageOperation imageOperationFromString(String const& string) {
else
operation.mode = BlendImageOperation::Screen;
operation.blendImages = bits.at(1).split('+');
operation.blendImages = String(bits.at(1)).split('+');
if (bits.size() > 2)
operation.offset[0] = lexicalCast<int>(bits.at(2));
@ -328,22 +333,19 @@ String imageOperationToString(ImageOperation const& operation) {
return "";
}
void parseImageOperations(String const& params, function<void(ImageOperation&&)> outputter) {
for (auto const& op : params.split('?')) {
void parseImageOperations(StringView params, function<void(ImageOperation&&)> outputter) {
params.forEachSplitView("?", [&](StringView op, size_t, size_t) {
if (!op.empty())
outputter(imageOperationFromString(op));
}
});
}
List<ImageOperation> parseImageOperations(String const& params) {
auto split = params.split('?');
List<ImageOperation> parseImageOperations(StringView params) {
List<ImageOperation> operations;
operations.reserve(split.size());
for (auto const& op : split) {
params.forEachSplitView("?", [&](StringView op, size_t, size_t) {
if (!op.empty())
operations.append(imageOperationFromString(op));
}
});
return operations;
}

View File

@ -137,14 +137,14 @@ typedef Variant<ErrorImageOperation, HueShiftImageOperation, SaturationShiftImag
ScanLinesImageOperation, SetColorImageOperation, ColorReplaceImageOperation, AlphaMaskImageOperation, BlendImageOperation,
MultiplyImageOperation, BorderImageOperation, ScaleImageOperation, CropImageOperation, FlipImageOperation> ImageOperation;
ImageOperation imageOperationFromString(String const& string);
ImageOperation imageOperationFromString(StringView string);
String imageOperationToString(ImageOperation const& operation);
void parseImageOperations(String const& params, function<void(ImageOperation&&)> outputter);
void parseImageOperations(StringView params, function<void(ImageOperation&&)> outputter);
// Each operation is assumed to be separated by '?', with parameters
// separated by ';' or '='
List<ImageOperation> parseImageOperations(String const& params);
List<ImageOperation> parseImageOperations(StringView params);
// Each operation separated by '?', returns string with leading '?'
String printImageOperations(List<ImageOperation> const& operations);

View File

@ -378,8 +378,10 @@ List<Directives> jsonToDirectivesList(Json const& v) {
Json jsonFromDirectivesList(List<Directives> const& v) {
JsonArray result;
for (auto& e : v)
result.push_back(e.toString());
for (auto& e : v) {
if (e)
result.push_back(*e.stringPtr());
}
return result;
}

View File

@ -2,6 +2,7 @@
#define STAR_LEXICAL_CAST_HPP
#include "StarString.hpp"
#include "StarStringView.hpp"
#include "StarMaybe.hpp"
#include <sstream>
@ -14,9 +15,9 @@ STAR_EXCEPTION(BadLexicalCast, StarException);
// Very simple basic lexical cast using stream input. Always operates in the
// "C" locale.
template <typename Type>
Maybe<Type> maybeLexicalCast(std::string const& s, std::ios_base::fmtflags flags = std::ios_base::boolalpha) {
Maybe<Type> maybeLexicalCast(StringView s, std::ios_base::fmtflags flags = std::ios_base::boolalpha) {
Type result;
std::istringstream stream(s);
std::istringstream stream(std::string(s.utf8()));
stream.flags(flags);
stream.imbue(std::locale::classic());
@ -28,21 +29,11 @@ Maybe<Type> maybeLexicalCast(std::string const& s, std::ios_base::fmtflags flags
if (stream >> ch)
return {};
return result;
return move(result);
}
template <typename Type>
Maybe<Type> maybeLexicalCast(char const* s, std::ios_base::fmtflags flags = std::ios_base::boolalpha) {
return maybeLexicalCast<Type>(std::string(s), flags);
}
template <typename Type>
Maybe<Type> maybeLexicalCast(String const& s, std::ios_base::fmtflags flags = std::ios_base::boolalpha) {
return maybeLexicalCast<Type>(s.utf8(), flags);
}
template <typename Type>
Type lexicalCast(std::string const& s, std::ios_base::fmtflags flags = std::ios_base::boolalpha) {
Type lexicalCast(StringView s, std::ios_base::fmtflags flags = std::ios_base::boolalpha) {
auto m = maybeLexicalCast<Type>(s, flags);
if (m)
return m.take();
@ -50,16 +41,6 @@ Type lexicalCast(std::string const& s, std::ios_base::fmtflags flags = std::ios_
throw BadLexicalCast(strf("Lexical cast failed on '%s'", s));
}
template <typename Type>
Type lexicalCast(char const* s, std::ios_base::fmtflags flags = std::ios_base::boolalpha) {
return lexicalCast<Type>(std::string(s), flags);
}
template <typename Type>
Type lexicalCast(String const& s, std::ios_base::fmtflags flags = std::ios_base::boolalpha) {
return lexicalCast<Type>(s.utf8(), flags);
}
template <class Type>
std::string toString(Type const& t, std::ios_base::fmtflags flags = std::ios_base::boolalpha) {
std::stringstream ss;

View File

@ -12,6 +12,7 @@ namespace Star {
STAR_CLASS(StringList);
STAR_CLASS(String);
STAR_CLASS(StringView);
STAR_EXCEPTION(StringException, StarException);
@ -268,6 +269,13 @@ public:
friend std::ostream& operator<<(std::ostream& os, String const& s);
friend std::istream& operator>>(std::istream& is, String& s);
// String view functions
String(StringView s);
String(std::string_view s);
String& operator+=(StringView s);
String& operator+=(std::string_view s);
private:
int compare(size_t selfOffset,
size_t selfLen,

View File

@ -0,0 +1,429 @@
#include "StarStringView.hpp"
namespace Star {
// To string
String::String(StringView s) : m_string(s.utf8()) {}
String::String(std::string_view s) : m_string(s) {}
String& String::operator+=(StringView s) {
m_string += s.utf8();
return *this;
}
String& String::operator+=(std::string_view s) {
m_string += s;
return *this;
}
StringView::StringView() {}
StringView::StringView(StringView const& s) : m_view(s.m_view) {}
StringView::StringView(StringView&& s) noexcept : m_view(move(s.m_view)) {};
StringView::StringView(String const& s) : m_view(s.utf8()) {};
StringView::StringView(char const* s) : m_view(s) {};
StringView::StringView(char const* s, size_t n) : m_view(s, n) {};
StringView::StringView(std::string_view const& s) : m_view(s) {};
StringView::StringView(std::string_view&& s) noexcept : m_view(move(s)) {};
StringView::StringView(std::string const& s) : m_view(s) {}
StringView::StringView(Char const* s) : m_view((char const*)s, sizeof(*s)) {}
StringView::StringView(Char const* s, size_t n) : m_view((char const*)s, n * sizeof(*s)) {};
std::string_view const& StringView::utf8() const {
return m_view;
}
std::string_view StringView::takeUtf8() {
return take(m_view);
}
ByteArray StringView::utf8Bytes() const {
return ByteArray(m_view.data(), m_view.size());
}
char const* StringView::utf8Ptr() const {
return m_view.data();
}
size_t StringView::utf8Size() const {
return m_view.size();
}
StringView::const_iterator StringView::begin() const {
return const_iterator(m_view.begin());
}
StringView::const_iterator StringView::end() const {
return const_iterator(m_view.end());
}
size_t StringView::size() const {
return utf8Length(m_view.data(), m_view.size());
}
size_t StringView::length() const {
return size();
}
bool StringView::empty() const {
return m_view.empty();
}
StringView::Char StringView::operator[](size_t index) const {
auto it = begin();
for (size_t i = 0; i < index; ++i)
++it;
return *it;
}
StringView::Char StringView::at(size_t i) const {
if (i > size())
throw OutOfRangeException(strf("Out of range in StringView::at(%s)", i));
return operator[](i);
}
bool StringView::endsWith(StringView end, CaseSensitivity cs) const {
auto endsize = end.size();
if (endsize == 0)
return true;
auto mysize = size();
if (endsize > mysize)
return false;
return compare(mysize - endsize, NPos, end, 0, NPos, cs) == 0;
}
bool StringView::endsWith(Char end, CaseSensitivity cs) const {
if (size() == 0)
return false;
return String::charEqual(end, operator[](size() - 1), cs);
}
bool StringView::beginsWith(StringView beg, CaseSensitivity cs) const {
auto begSize = beg.size();
if (begSize == 0)
return true;
auto mysize = size();
if (begSize > mysize)
return false;
return compare(0, begSize, beg, 0, NPos, cs) == 0;
}
bool StringView::beginsWith(Char beg, CaseSensitivity cs) const {
if (size() == 0)
return false;
return String::charEqual(beg, operator[](0), cs);
}
void StringView::forEachSplitAnyView(StringView chars, SplitCallback callback) const {
if (chars.empty())
return;
size_t beg = 0;
while (true) {
size_t end = m_view.find_first_of(chars.m_view, beg);
if (end == NPos) {
callback(m_view.substr(beg), beg, end);
break;
}
callback(m_view.substr(beg, end - beg), beg, end - beg);
beg = end + 1;
}
}
void StringView::forEachSplitView(StringView pattern, SplitCallback callback) const {
if (pattern.empty())
return;
size_t beg = 0;
while (true) {
size_t end = m_view.find(pattern.m_view, beg);
if (end == NPos) {
callback(m_view.substr(beg), beg, end);
break;
}
callback(m_view.substr(beg, end - beg), beg, end - beg);
beg = end + pattern.m_view.size();
}
}
bool StringView::hasChar(Char c) const {
for (Char ch : *this)
if (ch == c)
return true;
return false;
}
bool StringView::hasCharOrWhitespace(Char c) const {
return empty() ? String::isSpace(c) : hasChar(c);
}
size_t StringView::find(Char c, size_t pos, CaseSensitivity cs) const {
auto it = begin();
for (size_t i = 0; i < pos; ++i) {
if (it == end())
break;
++it;
}
while (it != end()) {
if (String::charEqual(c, *it, cs))
return pos;
++pos;
++it;
}
return NPos;
}
size_t StringView::find(StringView str, size_t pos, CaseSensitivity cs) const {
if (str.empty())
return 0;
auto it = begin();
for (size_t i = 0; i < pos; ++i) {
if (it == end())
break;
++it;
}
const_iterator sit = str.begin();
const_iterator mit = it;
while (it != end()) {
if (String::charEqual(*sit, *mit, cs)) {
do {
++mit;
++sit;
if (sit == str.end())
return pos;
else if (mit == end())
break;
} while (String::charEqual(*sit, *mit, cs));
sit = str.begin();
}
++pos;
mit = ++it;
}
return NPos;
}
size_t StringView::findLast(Char c, CaseSensitivity cs) const {
auto it = begin();
size_t found = NPos;
size_t pos = 0;
while (it != end()) {
if (String::charEqual(c, *it, cs))
found = pos;
++pos;
++it;
}
return found;
}
size_t StringView::findLast(StringView str, CaseSensitivity cs) const {
if (str.empty())
return 0;
size_t pos = 0;
auto it = begin();
size_t result = NPos;
const_iterator sit = str.begin();
const_iterator mit = it;
while (it != end()) {
if (String::charEqual(*sit, *mit, cs)) {
do {
++mit;
++sit;
if (sit == str.end()) {
result = pos;
break;
}
if (mit == end())
break;
} while (String::charEqual(*sit, *mit, cs));
sit = str.begin();
}
++pos;
mit = ++it;
}
return result;
}
size_t StringView::findFirstOf(StringView pattern, size_t beg) const {
auto it = begin();
size_t i;
for (i = 0; i < beg; ++i)
++it;
while (it != end()) {
if (pattern.hasCharOrWhitespace(*it))
return i;
++it;
++i;
}
return NPos;
}
size_t StringView::findFirstNotOf(StringView pattern, size_t beg) const {
auto it = begin();
size_t i;
for (i = 0; i < beg; ++i)
++it;
while (it != end()) {
if (!pattern.hasCharOrWhitespace(*it))
return i;
++it;
++i;
}
return NPos;
}
size_t StringView::findNextBoundary(size_t index, bool backwards) const {
starAssert(index <= size());
if (!backwards && (index == size()))
return index;
if (backwards) {
if (index == 0)
return 0;
index--;
}
Char c = this->at(index);
while (!String::isSpace(c)) {
if (backwards && (index == 0))
return 0;
index += backwards ? -1 : 1;
if (index == size())
return size();
c = this->at(index);
}
while (String::isSpace(c)) {
if (backwards && (index == 0))
return 0;
index += backwards ? -1 : 1;
if (index == size())
return size();
c = this->at(index);
}
if (backwards && !(index == size()))
return index + 1;
return index;
}
bool StringView::contains(StringView s, CaseSensitivity cs) const {
return find(s, 0, cs) != NPos;
}
int StringView::compare(StringView s, CaseSensitivity cs) const {
if (cs == CaseSensitivity::CaseSensitive)
return m_view.compare(s.m_view);
else
return compare(0, NPos, s, 0, NPos, cs);
}
bool StringView::equals(StringView s, CaseSensitivity cs) const {
return compare(s, cs) == 0;
}
bool StringView::equalsIgnoreCase(StringView s) const {
return compare(s, CaseSensitivity::CaseInsensitive) == 0;
}
StringView StringView::substr(size_t position, size_t n) const {
auto len = size();
if (position > len)
throw OutOfRangeException(strf("out of range in StringView::substr(%s, %s)", position, n));
if (position == 0 && n >= len)
return *this;
String ret;
ret.reserve(std::min(n, len - position));
auto it = begin();
std::advance(it, position);
for (size_t i = 0; i < n; ++i) {
if (it == end())
break;
ret.append(*it);
++it;
}
return ret;
}
int StringView::compare(size_t selfOffset, size_t selfLen, StringView other,
size_t otherOffset, size_t otherLen, CaseSensitivity cs) const {
auto selfIt = begin();
auto otherIt = other.begin();
while (selfOffset > 0 && selfIt != end()) {
++selfIt;
--selfOffset;
}
while (otherOffset > 0 && otherIt != other.end()) {
++otherIt;
--otherLen;
}
while (true) {
if ((selfIt == end() || selfLen == 0) && (otherIt == other.end() || otherLen == 0))
return 0;
else if (selfIt == end() || selfLen == 0)
return -1;
else if (otherIt == other.end() || otherLen == 0)
return 1;
auto c1 = *selfIt;
auto c2 = *otherIt;
if (cs == CaseSensitivity::CaseInsensitive) {
c1 = String::toLower(c1);
c2 = String::toLower(c2);
}
if (c1 < c2)
return -1;
else if (c2 < c1)
return 1;
++selfIt;
++otherIt;
--selfLen;
--otherLen;
}
}
StringView& StringView::operator=(StringView s) {
m_view = s.m_view;
return *this;
}
bool operator==(StringView s1, StringView s2) {
return s1.m_view == s2.m_view;
}
bool operator!=(StringView s1, StringView s2) {
return s1.m_view != s2.m_view;
}
bool operator<(StringView s1, StringView s2) {
return s1.m_view < s2.m_view;
}
std::ostream& operator<<(std::ostream& os, StringView& s) {
os << s.utf8();
return os;
}
}

View File

@ -0,0 +1,117 @@
#ifndef STAR_STRING_VIEW_HPP
#define STAR_STRING_VIEW_HPP
#include "StarString.hpp"
namespace Star {
STAR_CLASS(StringView);
STAR_CLASS(String);
// This is a StringView version of Star::String
// I literally just copy-pasted it all from there
class StringView {
public:
typedef String::Char Char;
typedef U8ToU32Iterator<std::string_view::const_iterator> const_iterator;
typedef Char value_type;
typedef value_type const& const_reference;
using CaseSensitivity = String::CaseSensitivity;
StringView();
StringView(StringView const& s);
StringView(StringView&& s) noexcept;
StringView(String const& s);
// These assume utf8 input
StringView(char const* s);
StringView(char const* s, size_t n);
StringView(std::string_view const& s);
StringView(std::string_view&& s) noexcept;
StringView(std::string const& s);
StringView(Char const* s);
StringView(Char const* s, size_t n);
// const& to internal utf8 data
std::string_view const& utf8() const;
std::string_view takeUtf8();
ByteArray utf8Bytes() const;
// Pointer to internal utf8 data, null-terminated.
char const* utf8Ptr() const;
size_t utf8Size() const;
const_iterator begin() const;
const_iterator end() const;
size_t size() const;
size_t length() const;
bool empty() const;
Char operator[](size_t index) const;
// Throws StringException if i out of range.
Char at(size_t i) const;
bool endsWith(StringView end, CaseSensitivity cs = CaseSensitivity::CaseSensitive) const;
bool endsWith(Char end, CaseSensitivity cs = CaseSensitivity::CaseSensitive) const;
bool beginsWith(StringView beg, CaseSensitivity cs = CaseSensitivity::CaseSensitive) const;
bool beginsWith(Char beg, CaseSensitivity cs = CaseSensitivity::CaseSensitive) const;
typedef function<void(StringView, size_t, size_t)> SplitCallback;
void forEachSplitAnyView(StringView pattern, SplitCallback) const;
void forEachSplitView(StringView pattern, SplitCallback) const;
bool hasChar(Char c) const;
// Identical to hasChar, except, if string is empty, tests if c is
// whitespace.
bool hasCharOrWhitespace(Char c) const;
size_t find(Char c, size_t beg = 0, CaseSensitivity cs = CaseSensitivity::CaseSensitive) const;
size_t find(StringView s, size_t beg = 0, CaseSensitivity cs = CaseSensitivity::CaseSensitive) const;
size_t findLast(Char c, CaseSensitivity cs = CaseSensitivity::CaseSensitive) const;
size_t findLast(StringView s, CaseSensitivity cs = CaseSensitivity::CaseSensitive) const;
// If pattern is empty, finds first whitespace
size_t findFirstOf(StringView chars = "", size_t beg = 0) const;
// If pattern is empty, finds first non-whitespace
size_t findFirstNotOf(StringView chars = "", size_t beg = 0) const;
// finds the the start of the next 'boundary' in a string. used for quickly
// scanning a string
size_t findNextBoundary(size_t index, bool backwards = false) const;
bool contains(StringView s, CaseSensitivity cs = CaseSensitivity::CaseSensitive) const;
int compare(StringView s, CaseSensitivity cs = CaseSensitivity::CaseSensitive) const;
bool equals(StringView s, CaseSensitivity cs = CaseSensitivity::CaseSensitive) const;
// Synonym for equals(s, String::CaseInsensitive)
bool equalsIgnoreCase(StringView s) const;
StringView substr(size_t position, size_t n = NPos) const;
StringView& operator=(StringView s);
friend bool operator==(StringView s1, StringView s2);
friend bool operator!=(StringView s1, StringView s2);
friend bool operator<(StringView s1, StringView s2);
friend std::ostream& operator<<(std::ostream& os, StringView& s);
private:
int compare(size_t selfOffset,
size_t selfLen,
StringView other,
size_t otherOffset,
size_t otherLen,
CaseSensitivity cs) const;
std::string_view m_view;
};
}
#endif

View File

@ -500,3 +500,4 @@ SET (star_game_SOURCES
)
ADD_LIBRARY (star_game OBJECT ${star_game_SOURCES} ${star_game_HEADERS})
TARGET_PRECOMPILE_HEADERS (star_game REUSE_FROM star_core)

View File

@ -71,15 +71,15 @@ Json HumanoidIdentity::toJson() const {
{"gender", GenderNames.getRight(gender)},
{"hairGroup", hairGroup},
{"hairType", hairType},
{"hairDirectives", hairDirectives.toString()},
{"bodyDirectives", bodyDirectives.toString()},
{"emoteDirectives", emoteDirectives.toString()},
{"hairDirectives", hairDirectives.string()},
{"bodyDirectives", bodyDirectives.string()},
{"emoteDirectives", emoteDirectives.string()},
{"facialHairGroup", facialHairGroup},
{"facialHairType", facialHairType},
{"facialHairDirectives", facialHairDirectives.toString()},
{"facialHairDirectives", facialHairDirectives.string()},
{"facialMaskGroup", facialMaskGroup},
{"facialMaskType", facialMaskType},
{"facialMaskDirectives", facialMaskDirectives.toString()},
{"facialMaskDirectives", facialMaskDirectives.string()},
{"personalityIdle", personality.idle},
{"personalityArmIdle", personality.armIdle},
{"personalityHeadOffset", jsonFromVec2F(personality.headOffset)},

View File

@ -120,20 +120,21 @@ RectU ImageMetadataDatabase::nonEmptyRegion(AssetPath const& path) const {
AssetPath ImageMetadataDatabase::filterProcessing(AssetPath const& path) {
AssetPath newPath = { path.basePath, path.subPath, {} };
List<Directives::Entry> filtered;
path.directives.forEach([&](auto const& entry) {
String filtered;
path.directives.forEach([&](auto const& entry, Directives const& directives) {
ImageOperation const& operation = entry.operation;
if (!(operation.is<HueShiftImageOperation>() ||
operation.is<SaturationShiftImageOperation>() ||
operation.is<BrightnessMultiplyImageOperation>() ||
operation.is<FadeToColorImageOperation>() ||
operation.is<ScanLinesImageOperation>() ||
operation.is<SetColorImageOperation>())) {
filtered.emplace_back(entry);
if (!(operation.is<HueShiftImageOperation>() ||
operation.is<SaturationShiftImageOperation>() ||
operation.is<BrightnessMultiplyImageOperation>() ||
operation.is<FadeToColorImageOperation>() ||
operation.is<ScanLinesImageOperation>() ||
operation.is<SetColorImageOperation>())) {
filtered += "?";
filtered += entry.string(*directives.shared);
}
});
});
newPath.directives.append(move(filtered));
newPath.directives = move(filtered);
return newPath;
}
@ -230,7 +231,7 @@ Vec2U ImageMetadataDatabase::calculateImageSize(AssetPath const& path) const {
OperationSizeAdjust osa(imageSize);
bool complete = path.directives.forEachAbortable([&](auto const& entry) -> bool {
bool complete = path.directives.forEachAbortable([&](auto const& entry, Directives const& directives) -> bool {
entry.operation.call(osa);
return !osa.hasError;
});

View File

@ -41,7 +41,7 @@ ParallaxLayer::ParallaxLayer(Json const& store) : ParallaxLayer() {
Json ParallaxLayer::store() const {
return JsonObject{
{"textures", jsonFromStringList(textures)},
{"directives", directives.toString()},
{"directives", directives.string()},
{"parallaxValue", jsonFromVec2F(parallaxValue)},
{"repeat", jsonFromVec2B(repeat)},
{"tileLimitTop", jsonFromMaybe(tileLimitTop)},
@ -59,11 +59,14 @@ Json ParallaxLayer::store() const {
void ParallaxLayer::addImageDirectives(Directives const& newDirectives) {
if (newDirectives) { // TODO: Move to Directives +=
if (directives) {
List<Directives::Entry> newEntries = *directives.entries;
for (auto const& entry : *newDirectives.entries)
newEntries.push_back(entry);
String newString;
directives = move(newEntries);
for (auto const& entry : newDirectives.shared->entries) {
newString += "+";
newString += entry.string(*newDirectives.shared);
}
directives = move(newString);
}
else
directives = newDirectives;