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