From be74b4919606e58e8d40e1c9251fd0aeafe7e5ff Mon Sep 17 00:00:00 2001 From: Nomango <569629550@qq.com> Date: Wed, 13 Mar 2019 11:11:01 +0800 Subject: [PATCH] add String: a lightweight std::wstring<>-like class minor fixes --- project/Easy2D/Easy2D.vcxproj | 4 +- project/Easy2D/Easy2D.vcxproj.filters | 12 +- src/common/String.cpp | 677 ++++++++++++++++++++++++++ src/common/String.h | 297 +++++++++++ src/common/closure.hpp | 2 +- src/core/DebugNode.cpp | 15 +- src/core/Image.cpp | 1 - src/core/helper.h | 2 +- src/core/window.cpp | 2 +- src/core/window.h | 12 +- 10 files changed, 994 insertions(+), 30 deletions(-) create mode 100644 src/common/String.cpp create mode 100644 src/common/String.h diff --git a/project/Easy2D/Easy2D.vcxproj b/project/Easy2D/Easy2D.vcxproj index ef302934..6b7a0681 100644 --- a/project/Easy2D/Easy2D.vcxproj +++ b/project/Easy2D/Easy2D.vcxproj @@ -14,6 +14,7 @@ + @@ -77,7 +78,6 @@ - @@ -86,6 +86,7 @@ + @@ -127,7 +128,6 @@ - diff --git a/project/Easy2D/Easy2D.vcxproj.filters b/project/Easy2D/Easy2D.vcxproj.filters index 9e431f3b..41cce6b1 100644 --- a/project/Easy2D/Easy2D.vcxproj.filters +++ b/project/Easy2D/Easy2D.vcxproj.filters @@ -118,9 +118,6 @@ core - - utils - core @@ -249,6 +246,9 @@ core + + common + @@ -323,9 +323,6 @@ core - - utils - core @@ -395,5 +392,8 @@ core + + common + \ No newline at end of file diff --git a/src/common/String.cpp b/src/common/String.cpp new file mode 100644 index 00000000..f271a5e9 --- /dev/null +++ b/src/common/String.cpp @@ -0,0 +1,677 @@ +// Copyright (c) 2016-2018 Easy2D - Nomango +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include "String.h" +#include + +namespace easy2d +{ + namespace + { + std::string StringWideCharToMultiByte(const wchar_t* wstr) + { + std::string ret; + if (wstr) + { + int len = ::WideCharToMultiByte(CP_ACP, 0, wstr, -1, nullptr, 0, nullptr, FALSE); + if (len) + { + char* str_tmp = new char[len + 1]; + str_tmp[0] = 0; + + len = ::WideCharToMultiByte(CP_ACP, 0, wstr, -1, str_tmp, len + 1, nullptr, FALSE); + + ret = str_tmp; + delete[] str_tmp; + } + } + return ret; + } + + template + const size_t TraitsFind(const typename _Traits::char_type* first, size_t first_size, size_t offset, + const typename _Traits::char_type* second, size_t count) + { + if (count > first_size || offset > first_size - count) + { + return static_cast(-1); + } + + if (count == 0) + { + return offset; + } + + const auto matches_end = first + (first_size - count) + 1; + for (auto iter = first + offset; ; ++iter) + { + iter = typename _Traits::find(iter, static_cast(matches_end - iter), *second); + if (!iter) + { + return static_cast(-1); + } + + if (typename _Traits::compare(iter, second, count) == 0) + { + return static_cast(iter - first); + } + } + } + + template + constexpr size_t TraitsFindLastOf( + const typename _Traits::char_type* first, const size_t first_size, const size_t pos, + const typename _Traits::char_type* second, const size_t count) + { + if (count != 0 && first_size != 0) + { + for (auto iter = first + std::min(pos, first_size - 1); ; --iter) + { + if (typename _Traits::find(second, count, *iter)) + { + return static_cast(iter - first); + } + + if (iter == first) + { + break; + } + } + } + + return static_cast(-1); + } + } + + String::allocator String::allocator_; + const String::size_type String::npos = static_cast(-1); + + String::String() + : str_(nullptr) + , size_(0) + , capacity_(0) + , operable_(true) + { + } + + String::String(const wchar_t * cstr, bool const_str) + : operable_(!const_str) + , size_(0) + , capacity_(0) + , str_(nullptr) + { + if (operable_) + { + assign(cstr, traits::length(cstr)); + } + else + { + const_str_ = cstr; + size_ = traits::length(cstr); + capacity_ = size_; + } + } + + String::String(const wchar_t * cstr, size_type count) + : String() + { + assign(cstr, count); + } + + String::String(std::wstring const & str) + : String(str.c_str(), false) + { + } + + String::String(String const & rhs) + : String(rhs.const_str_, !rhs.operable_) + { + } + + String::String(String const & rhs, size_type pos, size_type count) + : String() + { + assign(rhs, pos, count); + } + + String::String(String && rhs) + : String() + { + swap(rhs); + } + + String::~String() + { + destroy(); + } + + inline String & String::assign(size_type count, const wchar_t ch) + { + discard_const_data(); + + if (count != 0) + { + if (count > capacity_) + { + destroy(); + + capacity_ = size_ = count; + str_ = allocate(capacity_ + 1); + + traits::assign(str_, count, ch); + } + else + { + capacity_ = size_ = count; + traits::assign(str_, count, ch); + } + traits::assign(str_[size_], wchar_t()); + + } + else + { + clear(); + } + return (*this); + } + + inline String & String::assign(const wchar_t * cstr, size_type count) + { + discard_const_data(); + + if (cstr) + { + if (count > capacity_) + { + destroy(); + + capacity_ = size_ = count; + str_ = allocate(capacity_ + 1); + + traits::move(str_, cstr, size_); + } + else + { + capacity_ = size_ = count; + traits::move(str_, cstr, size_); + } + traits::assign(str_[size_], wchar_t()); + } + else + { + clear(); + } + return (*this); + } + + String & String::erase(size_type offset, size_type count) + { + if (count == 0) + return (*this); + + check_offset(offset); + check_operability(); + + count = clamp_suffix_size(offset, count); + + if (count == 0) + { + clear(); + return (*this); + } + + size_type new_size = size_ - count; + iterator erase_at = begin() + offset; + traits::move(erase_at, erase_at + count, new_size - offset + 1); + return (*this); + } + + String & String::insert(size_type index, size_type count, wchar_t ch) + { + if (count == 0) + return (*this); + + if (index >= size()) + return append(count, ch); + + check_operability(); + + wchar_t* const old_ptr = str_; + const size_type old_size = size_; + const size_type old_capacity = capacity_; + const size_type suffix_size = old_size - index + 1; + + size_ = old_size + count; + + if (size_ > old_capacity) + { + capacity_ = size_; + wchar_t* new_ptr = allocate(capacity_ + 1); + + wchar_t* const insert_at = new_ptr + index; + traits::move(new_ptr, old_ptr, index); // (0) - (index) + traits::assign(insert_at, count, ch); // (index) - (index + count) + traits::move(insert_at + count, old_ptr + index, suffix_size); // (index + count) - (old_size - index) + + deallocate(str_, old_capacity); + str_ = new_ptr; + } + else + { + wchar_t* const insert_at = old_ptr + index; + traits::move(insert_at + count, old_ptr + index, suffix_size); + traits::assign(insert_at, count, ch); + } + + return (*this); + } + + String & String::insert(size_type index, const wchar_t * cstr, size_type count) + { + if (count == 0) + return (*this); + + if (index >= size()) + return append(cstr, count); + + check_operability(); + + wchar_t* const old_ptr = str_; + const size_type old_size = size_; + const size_type old_capacity = capacity_; + const size_type suffix_size = old_size - index + 1; + + size_ = old_size + count; + + if (size_ > old_capacity) + { + capacity_ = size_; + wchar_t* new_ptr = allocate(capacity_ + 1); + + wchar_t* const insert_at = new_ptr + index; + traits::move(new_ptr, old_ptr, index); // (0) - (index) + traits::move(insert_at, cstr, count); // (index) - (index + count) + traits::move(insert_at + count, old_ptr + index, suffix_size); // (index + count) - (old_size - index) + + deallocate(str_, old_capacity); + str_ = new_ptr; + } + else + { + wchar_t* const insert_at = old_ptr + index; + traits::move(insert_at + count, old_ptr + index, suffix_size); + traits::move(insert_at, cstr, count); + } + + return (*this); + } + + String & String::insert(size_type index, const String & str, size_type off, size_type count) + { + if (count == 0 || off > str.size()) + return (*this); + + if (index >= size()) + return append(str, off, count); + + check_operability(); + + count = clamp_suffix_size(off, count); + + wchar_t* const old_ptr = str_; + const size_type old_size = size_; + const size_type old_capacity = capacity_; + const size_type suffix_size = old_size - index + 1; + + size_ = old_size + count; + + if (size_ > old_capacity) + { + capacity_ = size_; + wchar_t* new_ptr = allocate(capacity_ + 1); + + wchar_t* const insert_at = new_ptr + index; + traits::move(new_ptr, old_ptr, index); // (0) - (index) + traits::move(insert_at, str.begin() + off, count); // (index) - (index + count) + traits::move(insert_at + count, old_ptr + index, suffix_size); // (index + count) - (old_size - index) + + deallocate(str_, old_capacity); + str_ = new_ptr; + } + else + { + wchar_t* const insert_at = old_ptr + index; + traits::move(insert_at + count, old_ptr + index, suffix_size); + traits::move(insert_at, str.begin() + off, count); + } + + return (*this); + } + + String & String::append(size_type count, wchar_t ch) + { + check_operability(); + + size_t new_size = size_ + count; + size_t new_cap = new_size + 1; + wchar_t* new_str = allocate(new_cap); + + traits::move(new_str, str_, size_); + traits::assign(new_str + size_, count, ch); + traits::assign(new_str[new_size], wchar_t()); + + destroy(); + + str_ = new_str; + size_ = new_size; + capacity_ = new_cap; + return (*this); + } + + String & String::append(const wchar_t * cstr, size_type count) + { + check_operability(); + + size_t new_size = size_ + count; + size_t new_cap = new_size + 1; + wchar_t* new_str = allocate(new_cap); + + traits::move(new_str, str_, size_); + traits::move(new_str + size_, cstr, count); + traits::assign(new_str[new_size], wchar_t()); + + destroy(); + + str_ = new_str; + size_ = new_size; + capacity_ = new_cap; + return (*this); + } + + String & String::append(String const & other, size_type pos, size_type count) + { + check_operability(); + + if (pos >= other.size()) + return (*this); + + count = other.clamp_suffix_size(pos, count); + + size_t new_size = size_ + count; + size_t new_cap = new_size + 1; + wchar_t* new_str = allocate(new_cap); + + traits::move(new_str, str_, size_); + traits::move(new_str + size_, other.begin() + pos, count); + traits::assign(new_str[new_size], wchar_t()); + + destroy(); + + str_ = new_str; + size_ = new_size; + capacity_ = new_cap; + return (*this); + } + + inline void String::reserve(const size_type new_cap) + { + if (new_cap <= capacity_) + return; + + check_operability(); + + wchar_t* new_str = allocate(new_cap); + traits::move(new_str, str_, capacity_); + + destroy(); + + str_ = new_str; + capacity_ = new_cap; + } + + size_t String::hash() const + { + static size_t fnv_prime = 16777619U; + size_t fnv_offset_basis = 2166136261U; + + for (size_t index = 0; index < size_; ++index) + { + fnv_offset_basis ^= static_cast(const_str_[index]); + fnv_offset_basis *= fnv_prime; + } + return fnv_offset_basis; + } + + int String::compare(const wchar_t * const str) const + { + size_type count1 = size(); + size_type count2 = traits::length(str); + size_type rlen = std::min(count1, count2); + + int ret = traits::compare(const_str_, str, rlen); + if (ret != 0) + return ret; + + if (count1 < count2) + return -1; + + if (count1 > count2) + return 1; + + return 0; + } + + String::size_type String::find(const wchar_t ch, size_type offset) const + { + if (offset >= size_) + return String::npos; + + const_iterator citer = traits::find(const_str_ + offset, size_, ch); + return citer ? (citer - cbegin()) : String::npos; + } + + String::size_type String::find(const wchar_t * const str, size_type offset, size_type count) const + { + if (offset >= size_) + return String::npos; + return TraitsFind(const_str_, size_, offset, str, count); + } + + String::size_type String::find_first_of(const wchar_t * const str, size_type offset, size_type count) const + { + if (offset >= size_) + return String::npos; + + const_iterator citer = std::find_first_of(cbegin() + offset, cend(), str, str + count); + return (citer != cend()) ? (citer - cbegin()) : String::npos; + } + + String::size_type String::find_last_of(const wchar_t ch, size_type pos) const + { + if (pos == 0 || pos > size_ || pos == npos) + return npos; + + const_reverse_iterator criter = std::find(crbegin(), crend(), ch); + return (criter != crend()) ? (criter.base() - cbegin()) : String::npos; + } + + String::size_type String::find_last_of(const wchar_t * const str, size_type pos, size_type count) const + { + if (pos == 0 || pos > size_ || pos == npos) + return npos; + + return TraitsFindLastOf(const_str_, size_, pos, str, count); + } + + String & String::replace(size_type pos, size_type count, const wchar_t * cstr, size_type count2) + { + check_offset(pos); + check_operability(); + + count = clamp_suffix_size(pos, count); + if (count == count2) + { + traits::move(str_ + pos, cstr, count2); + return (*this); + } + + wchar_t* new_ptr = nullptr; + wchar_t* const old_ptr = str_; + const size_type old_size = size_; + const size_type old_capacity = capacity_; + const size_type suffix_size = old_size - count - pos + 1; + + if (count < count2 && (old_size + count2 - count) > capacity_) + { + const size_type growth = count2 - count; + size_ = old_size + growth; + capacity_ = size_; + new_ptr = allocate(capacity_ + 1); + + traits::move(new_ptr, old_ptr, pos); + } + else + { + size_ = old_size - (count - count2); + } + + wchar_t* const insert_at = (new_ptr ? new_ptr : old_ptr) + pos; + traits::move(insert_at, cstr, count2); + traits::move(insert_at + count2, old_ptr + count, suffix_size); + + if (new_ptr) + { + deallocate(str_, old_capacity); + str_ = new_ptr; + } + + return (*this); + } + + String & String::replace(size_type pos, size_type count, size_type count2, const wchar_t ch) + { + check_offset(pos); + check_operability(); + + count = clamp_suffix_size(pos, count); + if (count == count2) + { + traits::assign(str_ + pos, count2, ch); + return (*this); + } + + wchar_t* new_ptr = nullptr; + wchar_t* const old_ptr = str_; + const size_type old_size = size_; + const size_type old_capacity = capacity_; + const size_type suffix_size = old_size - count - pos + 1; + + if (count < count2 && (old_size + count2 - count) > capacity_) + { + const size_type growth = count2 - count; + size_ = old_size + growth; + capacity_ = size_; + new_ptr = allocate(capacity_ + 1); + + traits::move(new_ptr, old_ptr, pos); + } + else + { + size_ = old_size - (count - count2); + } + + wchar_t* const insert_at = (new_ptr ? new_ptr : old_ptr) + pos; + traits::assign(insert_at, count2, ch); + traits::move(insert_at + count2, old_ptr + count, suffix_size); + + if (new_ptr) + { + deallocate(str_, old_capacity); + str_ = new_ptr; + } + + return (*this); + } + + std::string String::to_string() const + { + return StringWideCharToMultiByte(str_); + } + + std::wstring String::to_wstring() const + { + return std::wstring(const_str_); + } + + wchar_t * String::allocate(size_type count) + { + return allocator_.allocate(count); + } + + void String::deallocate(wchar_t*& ptr, size_type count) + { + allocator_.deallocate(ptr, count); + ptr = nullptr; + } + + void String::destroy() + { + if (operable_ && str_) + { + deallocate(str_, capacity_); + } + else + { + const_str_ = nullptr; + } + size_ = capacity_ = 0; + } + + void String::swap(String & rhs) + { + std::swap(const_str_, rhs.const_str_); + std::swap(size_, rhs.size_); + std::swap(capacity_, rhs.capacity_); + + // swap const datas + std::swap(*const_cast(&operable_), *const_cast(&rhs.operable_)); + } + + void String::discard_const_data() + { + if (!operable_) + { + // force to enable operability + *const_cast(&operable_) = true; + const_str_ = nullptr; + capacity_ = size_ = 0; + } + } + + void String::check_operability() + { + if (!operable_) + { + // create a new string, then swap it with self + String(const_str_, false).swap(*this); + } + } + +} diff --git a/src/common/String.h b/src/common/String.h new file mode 100644 index 00000000..b27aff9f --- /dev/null +++ b/src/common/String.h @@ -0,0 +1,297 @@ +// Copyright (c) 2016-2018 Easy2D - Nomango +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#pragma once +#include "../macros.h" +#include + +namespace easy2d +{ + // + // String + // Lightweight std::wstring<>-like class + // + class E2D_API String + { + public: + using value_type = wchar_t; + using size_type = size_t; + using iterator = value_type * ; + using const_iterator = const value_type*; + using reference = value_type & ; + using const_reference = const value_type &; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using traits = std::char_traits; + using allocator = std::allocator; + + String(); + String(const wchar_t* cstr, bool const_str = true); + String(const wchar_t* cstr, size_type count); + String(std::wstring const& str); + String(String const& rhs); + String(String const& rhs, size_type pos, size_type count = npos); + String(String && rhs); + ~String(); + + inline const wchar_t* c_str() const { return const_str_ ? const_str_ : L""; } + inline wchar_t at(size_t i) const { return (*this)[i]; } + inline size_type size() const { return size_; } + inline size_type length() const { return size(); } + inline size_type capacity() const { return capacity_; } + inline size_type max_size() const { return (static_cast(-1) / sizeof(value_type)); } + inline bool empty() const { return size_ == 0; } + inline void clear() { discard_const_data(); if (str_) { str_[0] = wchar_t(); } size_ = 0; } + + void reserve(const size_type new_cap = 0); + inline void resize(const size_type new_size, const wchar_t ch = wchar_t()) { check_operability(); if (new_size < size_) str_[size_ = new_size] = wchar_t(); else append(new_size - size_, ch); } + + int compare(const wchar_t* const str) const; + inline int compare(String const& str) const { return compare(str.c_str()); } + + String& append(size_type count, wchar_t ch); + String& append(const wchar_t* cstr, size_type count); + String& append(String const& other, size_type pos, size_type count = npos); + inline String& append(const wchar_t* cstr) { return append(cstr, traits::length(cstr)); } + inline String& append(std::wstring const& str) { return append(str.c_str()); } + inline String& append(String const& other) { return append(other.const_str_, 0, npos); } + + size_type find(const wchar_t ch, size_type offset = 0) const; + size_type find(const wchar_t* const str, size_type offset, size_type count) const; + inline size_type find(String const& str, size_type offset = 0) const { return find(str.c_str(), offset, str.size()); } + inline size_type find(const wchar_t* const str, size_type offset = 0) const { return find(str, offset, traits::length(str)); } + + size_type find_first_of(const wchar_t* const str, size_type offset, size_type count) const; + inline size_type find_first_of(const wchar_t ch, size_type offset = 0) const { return find(ch, offset); } + inline size_type find_first_of(String const& str, size_type offset = 0) const { return find_first_of(str.c_str(), offset, str.size()); } + inline size_type find_first_of(const wchar_t* const str, size_type offset = 0) const { return find_first_of(str, offset, traits::length(str)); } + + size_type find_last_of(const wchar_t ch, size_type pos = npos) const; + size_type find_last_of(const wchar_t* const str, size_type pos, size_type count) const; + inline size_type find_last_of(String const& str, size_type pos = npos) const { return find_first_of(str.c_str(), pos, str.size()); } + inline size_type find_last_of(const wchar_t* const str, size_type pos = npos) const { return find_first_of(str, pos, traits::length(str)); } + + String& replace(size_type pos, size_type count, const wchar_t* cstr, size_type count2); + String& replace(size_type pos, size_type count, size_type count2, const wchar_t ch); + inline String& replace(size_type pos, size_type count, const String& str) { return replace(pos, count, str.c_str(), str.size()); } + inline String& replace(size_type pos, size_type count, const wchar_t* cstr) { return replace(pos, count, cstr, traits::length(cstr)); } + inline String& replace(const_iterator first, const_iterator last, const String& str) { return replace(first, last, str.c_str(), str.size()); } + inline String& replace(const_iterator first, const_iterator last, const wchar_t* cstr) { return replace(first, last, cstr, traits::length(cstr)); } + inline String& replace(const_iterator first, const_iterator last, const wchar_t* cstr, size_type count2) { return replace(first - cbegin(), last - first, cstr, traits::length(cstr)); } + inline String& replace(const_iterator first, const_iterator last, size_type count2, const wchar_t ch) { return replace(first - cbegin(), last - first, count2, ch); } + + String& assign(size_type count, const wchar_t ch); + String& assign(const wchar_t* cstr, size_type count); + inline String& assign(const wchar_t* cstr, bool const_str = true) { String(cstr, const_str).swap(*this); return *this; } + inline String& assign(std::wstring const& str) { String{ str }.swap(*this); return *this; } + inline String& assign(String const& rhs) { String{ rhs }.swap(*this); return *this; } + inline String& assign(String const& rhs, size_type pos, size_type count = npos) { String(rhs, pos, count).swap(*this); return *this; } + + String& erase(size_type offset = 0, size_type count = npos); + iterator erase(const const_iterator where) { size_type off = where - cbegin(); erase(off, 1); return begin() + off; } + iterator erase(const const_iterator first, const const_iterator last) { size_type off = first - cbegin(); erase(first - cbegin(), last - first); return begin() + off; } + + String substr(size_type pos = 0, size_type count = npos) const { return String(*this, pos, count); } + + String& insert(size_type index, size_type count, wchar_t ch); + String& insert(size_type index, const wchar_t* s, size_type count); + String& insert(size_type index, const String& str, size_type off, size_type count = npos); + inline String& insert(size_type index, const wchar_t* s) { return insert(index, s, traits::length(s)); } + inline String& insert(size_type index, const String& str) { return insert(index, str, 0, str.size()); } + inline iterator insert(const_iterator pos, size_type count, wchar_t ch) { size_type off = pos - cbegin(); insert(off, count, ch); return begin() + off; } + inline iterator insert(const_iterator pos, wchar_t ch) { return insert(pos, 1, ch); } + + inline void push_back(const wchar_t ch) { append(1, ch); } + inline wchar_t pop_back() { if (empty()) throw std::out_of_range("pop_back() called on empty string"); check_operability(); wchar_t ch = str_[--size_]; str_[size_] = wchar_t(); return ch; } + + std::string to_string() const; + std::wstring to_wstring() const; + + void swap(String& rhs); + + size_t hash() const; + + public: + inline iterator begin() { check_operability(); return iterator(str_); } + inline const_iterator begin() const { return const_iterator(const_str_); } + inline const_iterator cbegin() const { return begin(); } + inline iterator end() { check_operability(); return iterator(str_ + size_); } + inline const_iterator end() const { return const_iterator(const_str_ + size_); } + inline const_iterator cend() const { return end(); } + inline reverse_iterator rbegin() { check_operability(); return reverse_iterator(end()); } + inline const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } + inline const_reverse_iterator crbegin() const { return rbegin(); } + inline reverse_iterator rend() { check_operability(); return reverse_iterator(begin()); } + inline const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } + inline const_reverse_iterator crend() const { return rend(); } + inline reference front() { if (empty()) throw std::out_of_range("front() called on empty string"); check_operability(); return str_[0]; } + inline const_reference front() const { if (empty()) throw std::out_of_range("front() called on empty string"); return const_str_[0]; } + inline reference back() { if (empty()) throw std::out_of_range("back() called on empty string"); check_operability(); return str_[size_ - 1]; } + inline const_reference back() const { if (empty()) throw std::out_of_range("back() called on empty string"); return const_str_[size_ - 1]; } + + public: + inline operator const wchar_t*() const { return const_str_ ? const_str_ : L""; } + inline operator wchar_t*() { check_operability(); return str_; } + inline wchar_t operator[](size_type off) const { if(off >= size_) throw std::out_of_range("string subscript out of range"); return const_str_[off]; } + inline wchar_t& operator[](size_type off) { if (off >= size_) throw std::out_of_range("string subscript out of range"); check_operability(); return str_[off]; } + + public: + inline const String operator+(const wchar_t ch) const { return String{ *this }.append(1, ch); } + inline const String operator+(const wchar_t* cstr) const { return String{ *this }.append(cstr); } + inline const String operator+(std::wstring const& str) const { return String{ *this }.append(str); } + inline const String operator+(String const& rhs) const { return String{ *this }.append(rhs); } + + inline String& operator+=(const wchar_t ch) { return append(1, ch); } + inline String& operator+=(const wchar_t* cstr) { return append(cstr); } + inline String& operator+=(std::wstring const& str) { return append(str); } + inline String& operator+=(String const& rhs) { return append(rhs); } + + public: + inline String& operator=(const wchar_t* cstr) { if (const_str_ != cstr) String{ cstr }.swap(*this); return *this; } + inline String& operator=(std::wstring const& str) { String{ str }.swap(*this); return *this; } + inline String& operator=(String const& rhs) { if (this != &rhs) String{ rhs }.swap(*this); return *this; } + inline String& operator=(String && rhs) { if (this != &rhs) String{ rhs }.swap(*this); return *this; } + + inline bool operator==(String const& rhs) { return compare(rhs) == 0; } + inline bool operator==(const wchar_t* cstr) { return compare(cstr) == 0; } + + inline bool operator!=(String const& rhs) { return compare(rhs) != 0; } + inline bool operator!=(const wchar_t* cstr) { return compare(cstr) != 0; } + + public: + static const String::size_type npos; + + private: + wchar_t* allocate(size_type count); + void deallocate(wchar_t*& ptr, size_type count); + + void destroy(); + + void discard_const_data(); + void check_operability(); + void check_offset(size_type offset) const { if (offset > size()) throw std::out_of_range("invalid string position"); } + size_type clamp_suffix_size(size_type off, size_type count) const { return std::min(size() - off, count); } + + private: + union + { + struct + { + value_type* str_; + }; + + struct + { + const value_type* const_str_; + }; + }; + size_type size_; + size_type capacity_; + const bool operable_; + + static allocator allocator_; + }; + + + // + // operator+ for String + // + + inline String operator+(const wchar_t* lhs, String const& rhs) { return String{ lhs } + rhs; } + + // + // operator<> for String + // + + inline bool operator<(String const& lhs, String const& rhs) { return lhs.compare(rhs) < 0; } + inline bool operator>(String const& lhs, String const& rhs) { return lhs.compare(rhs) > 0; } + inline bool operator<=(String const& lhs, String const& rhs) { return lhs.compare(rhs) <= 0; } + inline bool operator>=(String const& lhs, String const& rhs) { return lhs.compare(rhs) >= 0; } + + // + // operator<<>> for String + // + + inline std::basic_ostream& + operator<<(std::basic_ostream& os, const String & str) + { + return os << str.c_str(); + } + + inline std::basic_istream& + operator>>(std::basic_istream& is, String & str) + { + using Ctype = std::ctype; + using IStream = std::basic_istream; + using SizeType = typename String::size_type; + + std::ios_base::iostate state = std::ios_base::goodbit; + bool changed = false; + + if (IStream::sentry(is)) + { + const Ctype& ctype_fac = std::use_facet(is.getloc()); + str.erase(); + + try + { + SizeType size = (0 < is.width() && static_cast(is.width()) < str.max_size()) + ? static_cast(is.width()) : str.max_size(); + typename String::traits::int_type meta = is.rdbuf()->sgetc(); + + for (; 0 < size; --size, meta = is.rdbuf()->snextc()) + if (String::traits::eq_int_type(String::traits::eof(), meta)) + { + state |= std::ios_base::eofbit; + break; + } + else if (ctype_fac.is(Ctype::space, String::traits::to_char_type(meta))) + break; + else + { + str.push_back(String::traits::to_char_type(meta)); + changed = true; + } + } + catch (...) + { + is.setstate(std::ios_base::badbit, true); + } + } + + is.width(0); + if (!changed) + state |= std::ios_base::failbit; + is.setstate(state); + return is; + } +} + +namespace std +{ + template<> + struct hash + { + size_t operator()(const easy2d::String& key) const + { + return key.hash(); + } + }; +} diff --git a/src/common/closure.hpp b/src/common/closure.hpp index 793ab423..87860b71 100644 --- a/src/common/closure.hpp +++ b/src/common/closure.hpp @@ -68,7 +68,7 @@ namespace easy2d template static inline std::function<_Ret(_Args...)> MakeFunc(_Ty* _Ptr, _Ret(_Ty::*_Func)(_Args...), Seq<_Num...>) { - return std::bind(_Func, _Ptr, typename std::_Ph<_Num + 1>()...); + return std::bind(_Func, _Ptr, std::_Ph<_Num + 1>()...); } }; } diff --git a/src/core/DebugNode.cpp b/src/core/DebugNode.cpp index b2e20083..930648ce 100644 --- a/src/core/DebugNode.cpp +++ b/src/core/DebugNode.cpp @@ -21,7 +21,6 @@ #include "DebugNode.h" #include "Text.h" #include "render.h" -#include "../utils/string.h" #include #include @@ -64,18 +63,10 @@ namespace easy2d { E2D_NOT_USED(dt); - try + frame_time_.push_back(time::Now()); + while (frame_time_.back() - frame_time_.front() >= time::Second) { - frame_time_.push_back(time::Now()); - while (frame_time_.back() - frame_time_.front() >= time::Second) - { - frame_time_.erase(frame_time_.begin()); - } - } - catch (std::exception& e) - { - debug_text_->SetText(StringMultiByteToWideChar(e.what())); - return; + frame_time_.erase(frame_time_.begin()); } std::wstringstream ss; diff --git a/src/core/Image.cpp b/src/core/Image.cpp index e0de3a80..68c28ee5 100644 --- a/src/core/Image.cpp +++ b/src/core/Image.cpp @@ -22,7 +22,6 @@ #include "logs.h" #include "render.h" #include "modules.h" -#include "../utils/string.h" namespace easy2d { diff --git a/src/core/helper.h b/src/core/helper.h index 99bcbbc2..f7fac7e2 100644 --- a/src/core/helper.h +++ b/src/core/helper.h @@ -21,6 +21,7 @@ #pragma once #include "RefCounter.hpp" #include "../common/Array.h" +#include "../common/String.h" #include "../common/IntrusivePtr.hpp" #include #include @@ -44,7 +45,6 @@ namespace easy2d { - using String = std::wstring; using StringStream = std::wstringstream; template diff --git a/src/core/window.cpp b/src/core/window.cpp index 691a4c65..42032f17 100644 --- a/src/core/window.cpp +++ b/src/core/window.cpp @@ -65,7 +65,7 @@ namespace easy2d } } - HRESULT Window::Create(String title, int width, int height, LPCWSTR icon, bool fullscreen, WNDPROC proc) + HRESULT Window::Create(String const& title, int width, int height, LPCWSTR icon, bool fullscreen, WNDPROC proc) { HINSTANCE hinst = GetModuleHandleW(nullptr); WNDCLASSEX wcex = { 0 }; diff --git a/src/core/window.h b/src/core/window.h index c5564537..96e3efc5 100644 --- a/src/core/window.h +++ b/src/core/window.h @@ -52,12 +52,12 @@ namespace easy2d public: HRESULT Create( - String title, - int width, - int height, - LPCWSTR icon, - bool fullscreen, - WNDPROC proc + String const& title, + int width, + int height, + LPCWSTR icon, + bool fullscreen, + WNDPROC proc ); void Prepare();