diff --git a/source/base/StarAssets.cpp b/source/base/StarAssets.cpp index ae2d087..320f813 100644 --- a/source/base/StarAssets.cpp +++ b/source/base/StarAssets.cpp @@ -844,7 +844,7 @@ shared_ptr Assets::loadImage(AssetPath const& path) const { return {}; StringMap 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::loadImage(AssetPath const& path) const { return unlockDuring([&]() { auto newData = make_shared(); 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()) std::rethrow_exception(error->exception); else diff --git a/source/core/CMakeLists.txt b/source/core/CMakeLists.txt index 34d890f..5e4f090 100644 --- a/source/core/CMakeLists.txt +++ b/source/core/CMakeLists.txt @@ -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 diff --git a/source/core/StarAssetPath.cpp b/source/core/StarAssetPath.cpp index 82eca93..90116bb 100644 --- a/source/core/StarAssetPath.cpp +++ b/source/core/StarAssetPath.cpp @@ -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; diff --git a/source/core/StarColor.cpp b/source/core/StarColor.cpp index 02b359a..915620d 100644 --- a/source/core/StarColor.cpp +++ b/source/core/StarColor.cpp @@ -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 cbytes; if (s.utf8Size() == 3) { diff --git a/source/core/StarColor.hpp b/source/core/StarColor.hpp index 68bcc10..5af2ffa 100644 --- a/source/core/StarColor.hpp +++ b/source/core/StarColor.hpp @@ -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(); diff --git a/source/core/StarConfig.hpp b/source/core/StarConfig.hpp index e7a5ff0..f070df4 100644 --- a/source/core/StarConfig.hpp +++ b/source/core/StarConfig.hpp @@ -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 diff --git a/source/core/StarDirectives.cpp b/source/core/StarDirectives.cpp index c7c35fc..fd4ee46 100644 --- a/source/core/StarDirectives.cpp +++ b/source/core/StarDirectives.cpp @@ -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&& newEntries) { - entries = std::make_shared const>(move(newEntries)); - String newString; - string = move(buildString(newString)); +Directives::Shared::Shared(List&& 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 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 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(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&& 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()) 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; } diff --git a/source/core/StarDirectives.hpp b/source/core/StarDirectives.hpp index 822d619..1455dd0 100644 --- a/source/core/StarDirectives.hpp +++ b/source/core/StarDirectives.hpp @@ -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 entries; + String string; + size_t hash = 0; + + bool empty() const; + Shared(List&& givenEntries, String&& givenString); + }; + Directives(); Directives(String const& directives); Directives(String&& directives); Directives(const char* directives); - Directives(List&& 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 const> entries; - size_t hash = 0; - String string; + std::shared_ptr 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&& entries); void clear(); DirectivesGroup& operator+=(Directives const& other); @@ -63,8 +74,8 @@ public: String toString() const; void addToString(String& string) const; - typedef function DirectivesCallback; - typedef function AbortableDirectivesCallback; + typedef function DirectivesCallback; + typedef function AbortableDirectivesCallback; void forEach(DirectivesCallback callback) const; bool forEachAbortable(AbortableDirectivesCallback callback) const; diff --git a/source/core/StarImageProcessing.cpp b/source/core/StarImageProcessing.cpp index 56ac130..0330a2d 100644 --- a/source/core/StarImageProcessing.cpp +++ b/source/core/StarImageProcessing.cpp @@ -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 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(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(bits.at(2)); @@ -328,22 +333,19 @@ String imageOperationToString(ImageOperation const& operation) { return ""; } -void parseImageOperations(String const& params, function outputter) { - for (auto const& op : params.split('?')) { +void parseImageOperations(StringView params, function outputter) { + params.forEachSplitView("?", [&](StringView op, size_t, size_t) { if (!op.empty()) outputter(imageOperationFromString(op)); - } + }); } -List parseImageOperations(String const& params) { - auto split = params.split('?'); +List parseImageOperations(StringView params) { List 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; } diff --git a/source/core/StarImageProcessing.hpp b/source/core/StarImageProcessing.hpp index ed560af..0b78408 100644 --- a/source/core/StarImageProcessing.hpp +++ b/source/core/StarImageProcessing.hpp @@ -137,14 +137,14 @@ typedef Variant ImageOperation; -ImageOperation imageOperationFromString(String const& string); +ImageOperation imageOperationFromString(StringView string); String imageOperationToString(ImageOperation const& operation); -void parseImageOperations(String const& params, function outputter); +void parseImageOperations(StringView params, function outputter); // Each operation is assumed to be separated by '?', with parameters // separated by ';' or '=' -List parseImageOperations(String const& params); +List parseImageOperations(StringView params); // Each operation separated by '?', returns string with leading '?' String printImageOperations(List const& operations); diff --git a/source/core/StarJsonExtra.cpp b/source/core/StarJsonExtra.cpp index aed4989..d683ca1 100644 --- a/source/core/StarJsonExtra.cpp +++ b/source/core/StarJsonExtra.cpp @@ -378,8 +378,10 @@ List jsonToDirectivesList(Json const& v) { Json jsonFromDirectivesList(List 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; } diff --git a/source/core/StarLexicalCast.hpp b/source/core/StarLexicalCast.hpp index 6e6c66e..2831f95 100644 --- a/source/core/StarLexicalCast.hpp +++ b/source/core/StarLexicalCast.hpp @@ -2,6 +2,7 @@ #define STAR_LEXICAL_CAST_HPP #include "StarString.hpp" +#include "StarStringView.hpp" #include "StarMaybe.hpp" #include @@ -14,9 +15,9 @@ STAR_EXCEPTION(BadLexicalCast, StarException); // Very simple basic lexical cast using stream input. Always operates in the // "C" locale. template -Maybe maybeLexicalCast(std::string const& s, std::ios_base::fmtflags flags = std::ios_base::boolalpha) { +Maybe 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 maybeLexicalCast(std::string const& s, std::ios_base::fmtflags flags if (stream >> ch) return {}; - return result; + return move(result); } template -Maybe maybeLexicalCast(char const* s, std::ios_base::fmtflags flags = std::ios_base::boolalpha) { - return maybeLexicalCast(std::string(s), flags); -} - -template -Maybe maybeLexicalCast(String const& s, std::ios_base::fmtflags flags = std::ios_base::boolalpha) { - return maybeLexicalCast(s.utf8(), flags); -} - -template -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(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 -Type lexicalCast(char const* s, std::ios_base::fmtflags flags = std::ios_base::boolalpha) { - return lexicalCast(std::string(s), flags); -} - -template -Type lexicalCast(String const& s, std::ios_base::fmtflags flags = std::ios_base::boolalpha) { - return lexicalCast(s.utf8(), flags); -} - template std::string toString(Type const& t, std::ios_base::fmtflags flags = std::ios_base::boolalpha) { std::stringstream ss; diff --git a/source/core/StarString.hpp b/source/core/StarString.hpp index 0f30fd2..ddc7926 100644 --- a/source/core/StarString.hpp +++ b/source/core/StarString.hpp @@ -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, diff --git a/source/core/StarStringView.cpp b/source/core/StarStringView.cpp new file mode 100644 index 0000000..1bea5e5 --- /dev/null +++ b/source/core/StarStringView.cpp @@ -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; +} + +} \ No newline at end of file diff --git a/source/core/StarStringView.hpp b/source/core/StarStringView.hpp new file mode 100644 index 0000000..928c37a --- /dev/null +++ b/source/core/StarStringView.hpp @@ -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 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 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 \ No newline at end of file diff --git a/source/game/CMakeLists.txt b/source/game/CMakeLists.txt index 1ff95ff..7328601 100644 --- a/source/game/CMakeLists.txt +++ b/source/game/CMakeLists.txt @@ -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) \ No newline at end of file diff --git a/source/game/StarHumanoid.cpp b/source/game/StarHumanoid.cpp index b33df4b..8e571e5 100644 --- a/source/game/StarHumanoid.cpp +++ b/source/game/StarHumanoid.cpp @@ -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)}, diff --git a/source/game/StarImageMetadataDatabase.cpp b/source/game/StarImageMetadataDatabase.cpp index d15d802..e47c841 100644 --- a/source/game/StarImageMetadataDatabase.cpp +++ b/source/game/StarImageMetadataDatabase.cpp @@ -120,20 +120,21 @@ RectU ImageMetadataDatabase::nonEmptyRegion(AssetPath const& path) const { AssetPath ImageMetadataDatabase::filterProcessing(AssetPath const& path) { AssetPath newPath = { path.basePath, path.subPath, {} }; - List 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() || - operation.is() || - operation.is() || - operation.is() || - operation.is() || - operation.is())) { - filtered.emplace_back(entry); + if (!(operation.is() || + operation.is() || + operation.is() || + operation.is() || + operation.is() || + operation.is())) { + 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; }); diff --git a/source/game/StarParallax.cpp b/source/game/StarParallax.cpp index a950086..f895d83 100644 --- a/source/game/StarParallax.cpp +++ b/source/game/StarParallax.cpp @@ -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 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;