diff --git a/loader/include/Geode/c++stl/string.hpp b/loader/include/Geode/c++stl/string.hpp index b9f6f1439..723bbe280 100644 --- a/loader/include/Geode/c++stl/string.hpp +++ b/loader/include/Geode/c++stl/string.hpp @@ -63,21 +63,366 @@ namespace gd { geode::stl::StringData m_data; friend geode::stl::StringImpl; public: + using size_type = size_t; + using difference_type = ptrdiff_t; + using value_type = char; + using reference = char&; + using const_reference = char const&; + using pointer = char*; + using const_pointer = char const*; + using iterator = std::string::iterator; + using const_iterator = std::string::const_iterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + static const size_t npos = -1; + string(); - string(string const&); - // string(string&&); - string(char const*); - string(char const*, size_t); - string(std::string const&); - // tried to add a string_view ctor, but got overload errors :( + string(string const& str); + string(string&& other) noexcept; + string(char const* str); + string(char const* str, size_t count); + string(size_t count, char c); + string(std::string const& str); + string(std::initializer_list ilist); + string(string const& other, size_t pos); + string(string const& other, size_t pos, size_t count); + string(string&& other, size_t pos); + string(string&& other, size_t pos, size_t count); + + template + requires std::convertible_to + string(T const& other) { + this->assign(std::string_view(other)); + } + + template + requires std::convertible_to + string(T const& other, size_t pos, size_t count) { + this->assign(std::string_view(other), pos, count); + } + + template + string(It first, It last) : string(std::to_address(first), std::distance(first, last)) {} + + template + string(std::from_range_t, Range&& range) : string(std::ranges::begin(range), std::ranges::end(range)) {} + ~string(); - string& operator=(string const&); - string& operator=(string&&); - string& operator=(char const*); - string& operator=(std::string const&); + string& assign(string const& other); + string& assign(char const* other, size_t count); + + string& assign(string&& other) { + this->swap(other); + other.clear(); + return *this; + } + + string& assign(char const* other) { + this->assign(other, std::char_traits::length(other)); + return *this; + } + + string& assign(string const& other, size_t pos, size_t count = npos) { + if (pos > other.size()) { + throw std::out_of_range("gd::string::assign"); + } + return this->assign(other.data() + pos, std::min(count, other.size() - pos)); + } + + string& assign(std::initializer_list ilist) { + return this->assign(ilist.begin(), ilist.size()); + } + + string& assign(size_t count, char c) { + this->clear(); + this->resize(count, c); + return *this; + } + + template + requires std::convertible_to + string& assign(T const& other) { + std::string_view view(other); + return this->assign(view.data(), view.size()); + } + + template + requires std::convertible_to + string& assign(T const& other, size_t pos, size_t count = npos) { + std::string_view view(other); + if (pos > view.size()) { + throw std::out_of_range("gd::string::assign"); + } + return this->assign(view.data() + pos, std::min(count, view.size() - pos)); + } + + template + string& assign(It first, It last) { + return this->assign(std::to_address(first), std::distance(first, last)); + } + + template + string& assign_range(Range&& range) { + return this->assign(std::ranges::begin(range), std::ranges::end(range)); + } + + string& operator=(string const& other); + string& operator=(string&& other) noexcept; + string& operator=(char const* other); + string& operator=(std::string const& other); + + string& operator=(char other) { + return this->assign(1, other); + } + + string& operator=(std::initializer_list ilist) { + return this->assign(ilist); + } + + template + requires std::convertible_to + string& operator=(T const& other) { + return this->assign(std::string_view(other)); + } void clear(); + void swap(string& other); + + void reserve(size_t capacity); + void resize(size_t size, char fill = '\0'); + void shrink_to_fit(); + + void push_back(char c) { + this->append(1, c); + } + + void pop_back() { + this->resize(this->size() - 1); + } + + size_t copy(char* buffer, size_t count, size_t pos = 0) const { + if (pos > this->size()) { + throw std::out_of_range("gd::string::copy"); + } + size_t toCopy = std::min(count, this->size() - pos); + std::memcpy(buffer, this->data() + pos, toCopy); + return toCopy; + } + + template + void resize_and_overwrite(size_t newSize, Operation op) { + this->resize(newSize); + this->erase(op(this->data(), newSize)); + } + + string& append(char const* other, size_t count); + string& append(size_t count, char c); + + string& append(char const* other) { + return this->append(other, std::char_traits::length(other)); + } + + string& append(string const& other) { + return this->append(other.data(), other.size()); + } + + string& append(string const& other, size_t pos, size_t count = npos) { + if (pos > other.size()) { + throw std::out_of_range("gd::string::append"); + } + return this->append(other.data() + pos, std::min(count, other.size() - pos)); + } + + string& append(std::initializer_list ilist) { + return this->append(ilist.begin(), ilist.size()); + } + + template + requires std::convertible_to + string& append(T const& other) { + std::string_view view(other); + return this->append(view.data(), view.size()); + } + + template + requires std::convertible_to + string& append(T const& other, size_t pos, size_t count = npos) { + std::string_view view(other); + if (pos > view.size()) { + throw std::out_of_range("gd::string::append"); + } + return this->append(view.data() + pos, std::min(count, view.size() - pos)); + } + + template + string& append(It first, It last) { + return this->append(std::to_address(first), std::distance(first, last)); + } + + template + string& append_range(Range&& range) { + return this->append(std::ranges::begin(range), std::ranges::end(range)); + } + + string& operator+=(string const& other) { + return this->append(other); + } + + string& operator+=(char const* other) { + return this->append(other); + } + + string& operator+=(char c) { + return this->append(1, c); + } + + string& operator+=(std::initializer_list ilist) { + return this->append(ilist); + } + + template + requires std::convertible_to + string& operator+=(T const& other) { + return this->append(std::string_view(other)); + } + + string& insert(size_t pos, char const* other, size_t count); + iterator insert(const_iterator pos, size_t count, char c); + iterator insert(const_iterator pos, std::initializer_list ilist); + string& insert(size_t pos, size_t count, char c); + + string& insert(size_t pos, char const* other) { + return this->insert(pos, other, std::char_traits::length(other)); + } + + string& insert(size_t pos, string const& other) { + return this->insert(pos, other.data(), other.size()); + } + + string& insert(size_t pos, string const& other, size_t pos2, size_t count = npos) { + if (pos2 > other.size()) { + throw std::out_of_range("gd::string::insert"); + } + return this->insert(pos, other.data() + pos2, std::min(count, other.size() - pos2)); + } + + iterator insert(const_iterator pos, char c) { + return this->insert(pos, 1, c); + } + + template + iterator insert(const_iterator pos, It first, It last) { + size_t index = pos - this->begin(); + this->insert(index, std::to_address(first), std::distance(first, last)); + return this->begin() + index; + } + + template + requires std::convertible_to + string& insert(size_t pos, T const& other) { + std::string_view view(other); + return this->insert(pos, view.data(), view.size()); + } + + template + requires std::convertible_to + string& insert(size_t pos, T const& other, size_t pos2, size_t count = npos) { + std::string_view view(other); + if (pos2 > view.size()) { + throw std::out_of_range("gd::string::insert"); + } + return this->insert(pos, view.data() + pos2, std::min(count, view.size() - pos2)); + } + + string& erase(size_t pos = 0, size_t count = npos); + iterator erase(const_iterator first, const_iterator last); + + iterator erase(const_iterator pos) { + return this->erase(pos, pos + 1); + } + + string& replace(size_t pos, size_t count, char const* other, size_t count2); + string& replace(const_iterator first, const_iterator last, char const* other, size_t count2); + string& replace(size_t pos, size_t count, size_t count2, char c); + string& replace(const_iterator first, const_iterator last, size_t count2, char c); + + string& replace(size_t pos, size_t count, string const& other) { + return this->replace(pos, count, other.data(), other.size()); + } + + string& replace(const_iterator first, const_iterator last, string const& other) { + return this->replace(first, last, other.data(), other.size()); + } + + string& replace(size_t pos, size_t count, string const& other, size_t pos2, size_t count2 = npos) { + if (pos2 > other.size()) { + throw std::out_of_range("gd::string::replace"); + } + return this->replace(pos, count, other.data() + pos2, std::min(count2, other.size() - pos2)); + } + + string& replace(size_t pos, size_t count, char const* other) { + return this->replace(pos, count, other, std::char_traits::length(other)); + } + + string& replace(const_iterator first, const_iterator last, char const* other) { + return this->replace(first, last, other, std::char_traits::length(other)); + } + + string& replace(const_iterator first, const_iterator last, std::initializer_list ilist) { + return this->replace(first, last, ilist.begin(), ilist.size()); + } + + template + string& replace(const_iterator first, const_iterator last, It first2, It last2) { + this->replace(first, last, std::to_address(first2), std::distance(first2, last2)); + return *this; + } + + template + requires std::convertible_to + string& replace(size_t pos, size_t count, T const& other) { + if (pos > this->size()) { + throw std::out_of_range("gd::string::replace"); + } + std::string_view view(other); + return this->replace(pos, count, view.data(), view.size()); + } + + template + requires std::convertible_to + string& replace(const_iterator first, const_iterator last, T const& other) { + std::string_view view(other); + return this->replace(first, last, view.data(), view.size()); + } + + template + requires std::convertible_to + string& replace(size_t pos, size_t count, T const& other, size_t pos2, size_t count2 = npos) { + if (pos > this->size()) { + throw std::out_of_range("gd::string::replace"); + } + std::string_view view(other); + if (pos2 > view.size()) { + throw std::out_of_range("gd::string::replace"); + } + return this->replace(pos, count, view.data() + pos2, std::min(count2, view.size() - pos2)); + } + + template + string& replace_with_range(const_iterator first, const_iterator last, Range&& range) { + return this->replace(first, last, std::ranges::begin(range), std::ranges::end(range)); + } + + string substr(size_t pos = 0, size_t count = npos) const& { + return string(*this, pos, count); + } + + string substr(size_t pos = 0, size_t count = npos) && { + return string(std::move(*this), pos, count); + } char& at(size_t pos); char const& at(size_t pos) const; @@ -85,6 +430,22 @@ namespace gd { char& operator[](size_t pos); char const& operator[](size_t pos) const; + char& front() { + return this->data()[0]; + } + + char const& front() const { + return this->data()[0]; + } + + char& back() { + return this->data()[this->size() - 1]; + } + + char const& back() const { + return this->data()[this->size() - 1]; + } + char* data(); char const* data() const; char const* c_str() const; @@ -93,6 +454,85 @@ namespace gd { size_t capacity() const; bool empty() const; + size_t length() const { + return this->size(); + } + + size_t max_size() const { + return (npos - sizeof(geode::stl::StringData::Internal) - 1) / 4; + } + + iterator begin(); + const_iterator begin() const; + iterator end(); + const_iterator end() const; + + const_iterator cbegin() const { + return this->begin(); + } + + const_iterator cend() const { + return this->end(); + } + + reverse_iterator rbegin() { + return reverse_iterator(this->end()); + } + + const_reverse_iterator rbegin() const { + return const_reverse_iterator(this->end()); + } + + const_reverse_iterator crbegin() const { + return this->rbegin(); + } + + reverse_iterator rend() { + return reverse_iterator(this->begin()); + } + + const_reverse_iterator rend() const { + return const_reverse_iterator(this->begin()); + } + + const_reverse_iterator crend() const { + return this->rend(); + } + + int compare(string const& other) const { + return std::string_view(*this).compare(other); + } + int compare(size_t pos, size_t count, string const& other) const { + return std::string_view(*this).compare(pos, count, other); + } + int compare(size_t pos, size_t count, string const& other, size_t pos2, size_t count2 = npos) const { + return std::string_view(*this).compare(pos, count, other, pos2, count2); + } + int compare(char const* other) const { + return std::string_view(*this).compare(other); + } + int compare(size_t pos, size_t count, char const* other) const { + return std::string_view(*this).compare(pos, count, other); + } + int compare(size_t pos, size_t count, char const* other, size_t count2) const { + return std::string_view(*this).compare(pos, count, other, count2); + } + template + requires std::convertible_to + int compare(T const& other) const { + return std::string_view(*this).compare(std::string_view(other)); + } + template + requires std::convertible_to + int compare(size_t pos, size_t count, T const& other) const { + return std::string_view(*this).compare(pos, count, std::string_view(other)); + } + template + requires std::convertible_to + int compare(size_t pos, size_t count, T const& other, size_t pos2, size_t count2 = npos) const { + return std::string_view(*this).compare(pos, count, std::string_view(other), pos2, count2); + } + bool operator==(string const& other) const; bool operator==(std::string_view other) const; bool operator==(char const* other) const { @@ -110,10 +550,235 @@ namespace gd { return *this <=> std::string_view(other); } + bool starts_with(std::string_view prefix) const { + return std::string_view(*this).starts_with(prefix); + } + bool starts_with(char const* prefix) const { + return std::string_view(*this).starts_with(prefix); + } + bool starts_with(char prefix) const { + return std::string_view(*this).starts_with(prefix); + } + + bool ends_with(std::string_view suffix) const { + return std::string_view(*this).ends_with(suffix); + } + bool ends_with(char const* suffix) const { + return std::string_view(*this).ends_with(suffix); + } + bool ends_with(char suffix) const { + return std::string_view(*this).ends_with(suffix); + } + + bool contains(std::string_view str) const { + return std::string_view(*this).find(str) != npos; + } + bool contains(char const* str) const { + return std::string_view(*this).find(str) != npos; + } + bool contains(char c) const { + return std::string_view(*this).find(c) != npos; + } + + size_t find(string const& str, size_t pos = 0) const { + return std::string_view(*this).find(str, pos); + } + size_t find(char const* str, size_t pos = 0) const { + return std::string_view(*this).find(str, pos); + } + size_t find(char const* str, size_t pos, size_t count) const { + return std::string_view(*this).find(str, pos, count); + } + size_t find(char c, size_t pos = 0) const { + return std::string_view(*this).find(c, pos); + } + template + requires std::convertible_to + size_t find(T const& other, size_t pos = 0) const { + return std::string_view(*this).find(std::string_view(other), pos); + } + + size_t rfind(string const& str, size_t pos = npos) const { + return std::string_view(*this).rfind(str, pos); + } + size_t rfind(char const* str, size_t pos = npos) const { + return std::string_view(*this).rfind(str, pos); + } + size_t rfind(char const* str, size_t pos, size_t count) const { + return std::string_view(*this).rfind(str, pos, count); + } + size_t rfind(char c, size_t pos = npos) const { + return std::string_view(*this).rfind(c, pos); + } + template + requires std::convertible_to + size_t rfind(T const& other, size_t pos = npos) const { + return std::string_view(*this).rfind(std::string_view(other), pos); + } + + size_t find_first_of(string const& str, size_t pos = 0) const { + return std::string_view(*this).find_first_of(str, pos); + } + size_t find_first_of(char const* str, size_t pos = 0) const { + return std::string_view(*this).find_first_of(str, pos); + } + size_t find_first_of(char const* str, size_t pos, size_t count) const { + return std::string_view(*this).find_first_of(str, pos, count); + } + size_t find_first_of(char c, size_t pos = 0) const { + return std::string_view(*this).find_first_of(c, pos); + } + template + requires std::convertible_to + size_t find_first_of(T const& other, size_t pos = 0) const { + return std::string_view(*this).find_first_of(std::string_view(other), pos); + } + + size_t find_last_of(string const& str, size_t pos = npos) const { + return std::string_view(*this).find_last_of(str, pos); + } + size_t find_last_of(char const* str, size_t pos = npos) const { + return std::string_view(*this).find_last_of(str, pos); + } + size_t find_last_of(char const* str, size_t pos, size_t count) const { + return std::string_view(*this).find_last_of(str, pos, count); + } + size_t find_last_of(char c, size_t pos = npos) const { + return std::string_view(*this).find_last_of(c, pos); + } + template + requires std::convertible_to + size_t find_last_of(T const& other, size_t pos = npos) const { + return std::string_view(*this).find_last_of(std::string_view(other), pos); + } + + size_t find_first_not_of(string const& str, size_t pos = 0) const { + return std::string_view(*this).find_first_not_of(str, pos); + } + size_t find_first_not_of(char const* str, size_t pos = 0) const { + return std::string_view(*this).find_first_not_of(str, pos); + } + size_t find_first_not_of(char const* str, size_t pos, size_t count) const { + return std::string_view(*this).find_first_not_of(str, pos, count); + } + size_t find_first_not_of(char c, size_t pos = 0) const { + return std::string_view(*this).find_first_not_of(c, pos); + } + template + requires std::convertible_to + size_t find_first_not_of(T const& other, size_t pos = 0) const { + return std::string_view(*this).find_first_not_of(std::string_view(other), pos); + } + + size_t find_last_not_of(string const& str, size_t pos = npos) const { + return std::string_view(*this).find_last_not_of(str, pos); + } + size_t find_last_not_of(char const* str, size_t pos = npos) const { + return std::string_view(*this).find_last_not_of(str, pos); + } + size_t find_last_not_of(char const* str, size_t pos, size_t count) const { + return std::string_view(*this).find_last_not_of(str, pos, count); + } + size_t find_last_not_of(char c, size_t pos = npos) const { + return std::string_view(*this).find_last_not_of(c, pos); + } + template + requires std::convertible_to + size_t find_last_not_of(T const& other, size_t pos = npos) const { + return std::string_view(*this).find_last_not_of(std::string_view(other), pos); + } + operator std::string() const; operator std::string_view() const; }; + inline string operator+(string const& lhs, string const& rhs) { + string out = lhs; + out += rhs; + return out; + } + + inline string operator+(string const& lhs, char const* rhs) { + string out = lhs; + out += rhs; + return out; + } + + inline string operator+(char const* lhs, string const& rhs) { + string out = lhs; + out += rhs; + return out; + } + + inline string operator+(string const& lhs, char rhs) { + string out = lhs; + out += rhs; + return out; + } + + inline string operator+(char lhs, string const& rhs) { + string out = string(1, lhs); + out += rhs; + return out; + } + + inline string operator+(string const& lhs, std::type_identity_t rhs) { + string out = lhs; + out += rhs; + return out; + } + + inline string operator+(std::type_identity_t lhs, string const& rhs) { + string out = string(lhs); + out += rhs; + return out; + } + + inline string operator+(string&& lhs, string const& rhs) { + lhs += rhs; + return std::move(lhs); + } + + inline string operator+(string const& lhs, string&& rhs) { + rhs.insert(0, lhs); + return std::move(rhs); + } + + inline string operator+(string&& lhs, string&& rhs) { + lhs += rhs; + return std::move(lhs); + } + + inline string operator+(string&& lhs, char const* rhs) { + lhs += rhs; + return std::move(lhs); + } + + inline string operator+(char const* lhs, string&& rhs) { + rhs.insert(0, lhs); + return std::move(rhs); + } + + inline string operator+(string&& lhs, char rhs) { + lhs += rhs; + return std::move(lhs); + } + + inline string operator+(char lhs, string&& rhs) { + rhs.insert(0, 1, lhs); + return std::move(rhs); + } + + inline string operator+(string&& lhs, std::type_identity_t rhs) { + lhs += rhs; + return std::move(lhs); + } + + inline string operator+(std::type_identity_t lhs, string&& rhs) { + rhs.insert(0, lhs); + return std::move(rhs); + } + inline std::string_view format_as(gd::string const& str) { return std::string_view(str); } diff --git a/loader/src/c++stl/string-impl.hpp b/loader/src/c++stl/string-impl.hpp index 003355933..aae8439b6 100644 --- a/loader/src/c++stl/string-impl.hpp +++ b/loader/src/c++stl/string-impl.hpp @@ -13,12 +13,22 @@ namespace geode::stl { void free(); char* getStorage(); - void setStorage(std::string_view); + void setStorage(std::string_view str); + void setStorage(StringData& other); + void swapStorage(StringData& other); size_t getSize(); - void setSize(size_t); + void setSize(size_t size, char fill); size_t getCapacity(); - void setCapacity(size_t); + void setCapacity(size_t capacity); + + void append(std::string_view str); + void append(size_t count, char c); + void insert(size_t pos, std::string_view str); + void insert(size_t pos, size_t count, char c); + void erase(size_t pos, size_t count); + void replace(size_t pos, size_t count, std::string_view str); + void replace(size_t pos, size_t count, size_t count2, char c); }; } \ No newline at end of file diff --git a/loader/src/c++stl/string.cpp b/loader/src/c++stl/string.cpp index d0ca51215..ee78b1017 100644 --- a/loader/src/c++stl/string.cpp +++ b/loader/src/c++stl/string.cpp @@ -20,15 +20,13 @@ namespace gd { } string::string(string const& str) { - impl.setStorage(str); + impl.setStorage(intoMutRef(str.m_data)); } - // string::string(string&& other) { - // // TODO: do this better :-) - // impl.setStorage(other); - // implFor(other).free(); - // implFor(other).setEmpty(); - // } + string::string(string&& other) noexcept { + impl.swapStorage(other.m_data); + implFor(other).setEmpty(); + } string::string(char const* str) { impl.setStorage(str); @@ -38,23 +36,74 @@ namespace gd { impl.setStorage(std::string_view(str, size)); } + string::string(size_t count, char c) { + impl.setSize(count, c); + } + string::string(std::string const& str) { impl.setStorage(str); } + string::string(std::initializer_list ilist) { + impl.setStorage(std::string_view(ilist.begin(), ilist.size())); + } + + string::string(string const& other, size_t pos) { + if (pos > other.size()) { + throw std::out_of_range("gd::string::string"); + } + impl.setStorage(std::string_view(other.data() + pos, other.size() - pos)); + } + + string::string(string const& other, size_t pos, size_t count) { + if (pos > other.size()) { + throw std::out_of_range("gd::string::string"); + } + impl.setStorage(std::string_view(other.data() + pos, std::min(count, other.size() - pos))); + } + + string::string(string&& other, size_t pos) { + if (pos > other.size()) { + throw std::out_of_range("gd::string::string"); + } + other.erase(0, pos); + impl.swapStorage(other.m_data); + implFor(other).setEmpty(); + } + + string::string(string&& other, size_t pos, size_t count) { + if (pos > other.size()) { + throw std::out_of_range("gd::string::string"); + } + other.erase(0, pos); + other.erase(std::min(count, other.size() - pos)); + impl.swapStorage(other.m_data); + implFor(other).setEmpty(); + } + string::~string() { this->clear(); } + string& string::assign(string const& other) { + impl.setStorage(intoMutRef(other.m_data)); + return *this; + } + + string& string::assign(char const* other, size_t count) { + impl.setStorage(std::string_view(other, count)); + return *this; + } + string& string::operator=(string const& other) { - if (this != &other) { - impl.setStorage(other); + // check if the strings share the same storage + if (this->m_data.m_data != other.m_data.m_data) { + impl.setStorage(intoMutRef(other.m_data)); } return *this; } - string& string::operator=(string&& other) { - // TODO: do this better :-) - impl.setStorage(other); + string& string::operator=(string&& other) noexcept { + impl.swapStorage(other.m_data); implFor(other).setEmpty(); return *this; } @@ -62,15 +111,107 @@ namespace gd { impl.setStorage(other); return *this; } - string& string::operator=(std::string const& other) { - impl.setStorage(other); - return *this; - } void string::clear() { impl.setEmpty(); } + void string::swap(string& other) { + impl.swapStorage(other.m_data); + } + + void string::reserve(size_t capacity) { + impl.setCapacity(impl.getSize() + capacity); + } + + void string::resize(size_t size, char fill) { + impl.setSize(size, fill); + } + + void string::shrink_to_fit() { + impl.setCapacity(this->size()); + } + + string& string::append(char const* other, size_t count) { + impl.append(std::string_view(other, count)); + return *this; + } + + string& string::append(size_t count, char c) { + impl.append(count, c); + return *this; + } + + string& string::insert(size_t pos, char const* other, size_t count) { + if (pos > this->size()) { + throw std::out_of_range("gd::string::insert"); + } + impl.insert(pos, std::string_view(other, count)); + return *this; + } + + string::iterator string::insert(const_iterator pos, size_t count, char c) { + size_t index = pos - this->cbegin(); + impl.insert(index, count, c); + return this->begin() + index; + } + + string::iterator string::insert(const_iterator pos, std::initializer_list ilist) { + size_t index = pos - this->cbegin(); + impl.insert(index, std::string_view(ilist.begin(), ilist.size())); + return this->begin() + index; + } + + string& string::insert(size_t pos, size_t count, char c) { + if (pos > this->size()) { + throw std::out_of_range("gd::string::insert"); + } + impl.insert(pos, count, c); + return *this; + } + + string& string::erase(size_t pos, size_t count) { + if (pos > this->size()) { + throw std::out_of_range("gd::string::erase"); + } + impl.erase(pos, count); + return *this; + } + + string::iterator string::erase(const_iterator first, const_iterator last) { + size_t index = first - this->cbegin(); + impl.erase(index, last - first); + return this->begin() + index; + } + + string& string::replace(size_t pos, size_t count, char const* other, size_t count2) { + if (pos > this->size()) { + throw std::out_of_range("gd::string::replace"); + } + impl.replace(pos, count, std::string_view(other, count2)); + return *this; + } + + string& string::replace(const_iterator first, const_iterator last, char const* other, size_t count2) { + size_t index = first - this->cbegin(); + impl.replace(index, last - first, std::string_view(other, count2)); + return *this; + } + + string& string::replace(size_t pos, size_t count, size_t count2, char c) { + if (pos > this->size()) { + throw std::out_of_range("gd::string::replace"); + } + impl.replace(pos, count, count2, c); + return *this; + } + + string& string::replace(const_iterator first, const_iterator last, size_t count2, char c) { + size_t index = first - this->cbegin(); + impl.replace(index, last - first, count2, c); + return *this; + } + char& string::at(size_t pos) { if (pos >= this->size()) throw std::out_of_range("gd::string::at"); @@ -91,6 +232,19 @@ namespace gd { size_t string::capacity() const { return impl.getCapacity(); } bool string::empty() const { return this->size() == 0; } + string::iterator makeIter(char* ptr) { + return *reinterpret_cast(&ptr); + } + + string::const_iterator makeIter(char const* ptr) { + return *reinterpret_cast(&ptr); + } + + string::iterator string::begin() { return makeIter(this->data()); } + string::const_iterator string::begin() const { return makeIter(this->data()); } + string::iterator string::end() { return makeIter(this->data() + this->size()); } + string::const_iterator string::end() const { return makeIter(this->data() + this->size()); } + bool string::operator==(string const& other) const { return std::string_view(*this) == std::string_view(other); } diff --git a/loader/src/platform/android/gdstdlib.cpp b/loader/src/platform/android/gdstdlib.cpp index 43dd55bed..5e1b62f39 100644 --- a/loader/src/platform/android/gdstdlib.cpp +++ b/loader/src/platform/android/gdstdlib.cpp @@ -101,8 +101,7 @@ namespace geode::stl { char* StringImpl::getStorage() { return reinterpret_cast(data.m_data); } - // TODO: add a copyFrom(string const&) to take advantage - // of gnustl refcounted strings + void StringImpl::setStorage(std::string_view str) { this->free(); @@ -125,17 +124,130 @@ namespace geode::stl { this->getStorage()[str.size()] = 0; } + void StringImpl::setStorage(StringData& str) { + this->free(); + + auto& internal = str.m_data[-1]; + + if (str.m_data[-1].m_refcount < 0) { + this->setStorage(std::string_view(reinterpret_cast(str.m_data), internal.m_size)); + } else { + if (str.m_data != emptyInternalString()) { + ++internal.m_refcount; + } + data.m_data = &internal; + } + } + + void StringImpl::swapStorage(StringData& other) { + std::swap(this->data.m_data, other.m_data); + } + size_t StringImpl::getSize() { return data.m_data[-1].m_size; } - void StringImpl::setSize(size_t size) { - // TODO: implement this, remember its copy-on-write... + void StringImpl::setSize(size_t size, char fill) { + if (size > this->getCapacity()) { + this->setCapacity(size); + } + + if (size > this->getSize()) { + std::memset(this->getStorage() + this->getSize(), fill, size - this->getSize()); + } + + data.m_data[-1].m_size = size; + this->getStorage()[size] = 0; } size_t StringImpl::getCapacity() { return data.m_data[-1].m_capacity; } void StringImpl::setCapacity(size_t cap) { - // TODO: implement this, remember its copy-on-write... + if (cap == this->getCapacity()) return; + + StringData::Internal internal; + internal.m_size = this->getSize(); + internal.m_capacity = cap; + internal.m_refcount = 0; + + // use char* so we can do easy pointer arithmetic with it + auto* buffer = static_cast(gd::operatorNew(cap + 1 + sizeof(internal))); + std::memcpy(buffer, &internal, sizeof(internal)); + std::memcpy(buffer + sizeof(internal), this->getStorage(), this->getSize()); + + this->free(); + data.m_data = reinterpret_cast(buffer + sizeof(internal)); + + this->getStorage()[this->getSize()] = 0; + } + + void StringImpl::append(std::string_view str) { + if (str.size() == 0) return; + + size_t oldSize = this->getSize(); + this->setSize(oldSize + str.size(), 0); + std::memcpy(this->getStorage() + oldSize, str.data(), str.size()); + } + + void StringImpl::append(size_t count, char c) { + if (count == 0) return; + + this->setSize(this->getSize() + count, c); + } + + void StringImpl::insert(size_t pos, std::string_view str) { + if (str.size() == 0) return; + + size_t oldSize = this->getSize(); + this->setSize(oldSize + str.size(), 0); + std::memmove(this->getStorage() + pos + str.size(), this->getStorage() + pos, oldSize - pos); + std::memcpy(this->getStorage() + pos, str.data(), str.size()); + } + + void StringImpl::insert(size_t pos, size_t count, char c) { + if (count == 0) return; + + size_t oldSize = this->getSize(); + this->setSize(oldSize + count, 0); + std::memmove(this->getStorage() + pos + count, this->getStorage() + pos, oldSize - pos); + std::memset(this->getStorage() + pos, c, count); + } + + void StringImpl::erase(size_t pos, size_t count) { + if (count == 0) return; + + size_t oldSize = this->getSize(); + count = std::min(count, oldSize - pos); + + std::memmove(this->getStorage() + pos, this->getStorage() + pos + count, oldSize - pos - count); + this->setSize(oldSize - count, 0); + } + + void StringImpl::replace(size_t pos, size_t count, std::string_view other) { + if (count == 0 && other.size() == 0) return; + + size_t oldSize = this->getSize(); + count = std::min(count, oldSize - pos); + + if (other.size() > count) { + this->setSize(oldSize + other.size() - count, 0); + } + + std::memmove(this->getStorage() + pos + other.size(), this->getStorage() + pos + count, oldSize - pos - count); + std::memcpy(this->getStorage() + pos, other.data(), other.size()); + } + + void StringImpl::replace(size_t pos, size_t count, size_t count2, char c) { + if (count == 0 && count2 == 0) return; + + size_t oldSize = this->getSize(); + count = std::min(count, oldSize - pos); + + if (count2 > count) { + this->setSize(oldSize + count2 - count, 0); + } + + std::memmove(this->getStorage() + pos + count2, this->getStorage() + pos + count, oldSize - pos - count); + std::memset(this->getStorage() + pos, c, count2); } }