add String: a lightweight std::wstring<>-like class
minor fixes
This commit is contained in:
		
							parent
							
								
									8eb3f9887c
								
							
						
					
					
						commit
						be74b49196
					
				|  | @ -14,6 +14,7 @@ | ||||||
|     <ClInclude Include="..\..\src\common\IntrusivePtr.hpp" /> |     <ClInclude Include="..\..\src\common\IntrusivePtr.hpp" /> | ||||||
|     <ClInclude Include="..\..\src\common\noncopyable.hpp" /> |     <ClInclude Include="..\..\src\common\noncopyable.hpp" /> | ||||||
|     <ClInclude Include="..\..\src\common\Singleton.hpp" /> |     <ClInclude Include="..\..\src\common\Singleton.hpp" /> | ||||||
|  |     <ClInclude Include="..\..\src\common\String.h" /> | ||||||
|     <ClInclude Include="..\..\src\config.h" /> |     <ClInclude Include="..\..\src\config.h" /> | ||||||
|     <ClInclude Include="..\..\src\core\Action.h" /> |     <ClInclude Include="..\..\src\core\Action.h" /> | ||||||
|     <ClInclude Include="..\..\src\core\ActionGroup.h" /> |     <ClInclude Include="..\..\src\core\ActionGroup.h" /> | ||||||
|  | @ -77,7 +78,6 @@ | ||||||
|     <ClInclude Include="..\..\src\utils\File.h" /> |     <ClInclude Include="..\..\src\utils\File.h" /> | ||||||
|     <ClInclude Include="..\..\src\utils\Path.h" /> |     <ClInclude Include="..\..\src\utils\Path.h" /> | ||||||
|     <ClInclude Include="..\..\src\utils\ResLoader.h" /> |     <ClInclude Include="..\..\src\utils\ResLoader.h" /> | ||||||
|     <ClInclude Include="..\..\src\utils\string.h" /> |  | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <ClCompile Include="..\..\src\audio\audio.cpp" /> |     <ClCompile Include="..\..\src\audio\audio.cpp" /> | ||||||
|  | @ -86,6 +86,7 @@ | ||||||
|     <ClCompile Include="..\..\src\audio\Player.cpp" /> |     <ClCompile Include="..\..\src\audio\Player.cpp" /> | ||||||
|     <ClCompile Include="..\..\src\audio\Transcoder.cpp" /> |     <ClCompile Include="..\..\src\audio\Transcoder.cpp" /> | ||||||
|     <ClCompile Include="..\..\src\audio\Voice.cpp" /> |     <ClCompile Include="..\..\src\audio\Voice.cpp" /> | ||||||
|  |     <ClCompile Include="..\..\src\common\String.cpp" /> | ||||||
|     <ClCompile Include="..\..\src\core\Action.cpp" /> |     <ClCompile Include="..\..\src\core\Action.cpp" /> | ||||||
|     <ClCompile Include="..\..\src\core\ActionGroup.cpp" /> |     <ClCompile Include="..\..\src\core\ActionGroup.cpp" /> | ||||||
|     <ClCompile Include="..\..\src\core\ActionTween.cpp" /> |     <ClCompile Include="..\..\src\core\ActionTween.cpp" /> | ||||||
|  | @ -127,7 +128,6 @@ | ||||||
|     <ClCompile Include="..\..\src\utils\File.cpp" /> |     <ClCompile Include="..\..\src\utils\File.cpp" /> | ||||||
|     <ClCompile Include="..\..\src\utils\Path.cpp" /> |     <ClCompile Include="..\..\src\utils\Path.cpp" /> | ||||||
|     <ClCompile Include="..\..\src\utils\ResLoader.cpp" /> |     <ClCompile Include="..\..\src\utils\ResLoader.cpp" /> | ||||||
|     <ClCompile Include="..\..\src\utils\string.cpp" /> |  | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|   <ItemGroup Label="ProjectConfigurations"> |   <ItemGroup Label="ProjectConfigurations"> | ||||||
|     <ProjectConfiguration Include="Debug|Win32"> |     <ProjectConfiguration Include="Debug|Win32"> | ||||||
|  |  | ||||||
|  | @ -118,9 +118,6 @@ | ||||||
|     <ClInclude Include="..\..\src\core\Transform.hpp"> |     <ClInclude Include="..\..\src\core\Transform.hpp"> | ||||||
|       <Filter>core</Filter> |       <Filter>core</Filter> | ||||||
|     </ClInclude> |     </ClInclude> | ||||||
|     <ClInclude Include="..\..\src\utils\string.h"> |  | ||||||
|       <Filter>utils</Filter> |  | ||||||
|     </ClInclude> |  | ||||||
|     <ClInclude Include="..\..\src\core\Geometry.h"> |     <ClInclude Include="..\..\src\core\Geometry.h"> | ||||||
|       <Filter>core</Filter> |       <Filter>core</Filter> | ||||||
|     </ClInclude> |     </ClInclude> | ||||||
|  | @ -249,6 +246,9 @@ | ||||||
|     <ClInclude Include="..\..\src\core\helper.h"> |     <ClInclude Include="..\..\src\core\helper.h"> | ||||||
|       <Filter>core</Filter> |       <Filter>core</Filter> | ||||||
|     </ClInclude> |     </ClInclude> | ||||||
|  |     <ClInclude Include="..\..\src\common\String.h"> | ||||||
|  |       <Filter>common</Filter> | ||||||
|  |     </ClInclude> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <ClCompile Include="..\..\src\core\Animation.cpp"> |     <ClCompile Include="..\..\src\core\Animation.cpp"> | ||||||
|  | @ -323,9 +323,6 @@ | ||||||
|     <ClCompile Include="..\..\src\core\logs.cpp"> |     <ClCompile Include="..\..\src\core\logs.cpp"> | ||||||
|       <Filter>core</Filter> |       <Filter>core</Filter> | ||||||
|     </ClCompile> |     </ClCompile> | ||||||
|     <ClCompile Include="..\..\src\utils\string.cpp"> |  | ||||||
|       <Filter>utils</Filter> |  | ||||||
|     </ClCompile> |  | ||||||
|     <ClCompile Include="..\..\src\core\Geometry.cpp"> |     <ClCompile Include="..\..\src\core\Geometry.cpp"> | ||||||
|       <Filter>core</Filter> |       <Filter>core</Filter> | ||||||
|     </ClCompile> |     </ClCompile> | ||||||
|  | @ -395,5 +392,8 @@ | ||||||
|     <ClCompile Include="..\..\src\core\Layer.cpp"> |     <ClCompile Include="..\..\src\core\Layer.cpp"> | ||||||
|       <Filter>core</Filter> |       <Filter>core</Filter> | ||||||
|     </ClCompile> |     </ClCompile> | ||||||
|  |     <ClCompile Include="..\..\src\common\String.cpp"> | ||||||
|  |       <Filter>common</Filter> | ||||||
|  |     </ClCompile> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
| </Project> | </Project> | ||||||
|  | @ -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 <cstring> | ||||||
|  | 
 | ||||||
|  | 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<class _Traits> | ||||||
|  | 		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<size_t>(-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<size_t>(matches_end - iter), *second); | ||||||
|  | 				if (!iter) | ||||||
|  | 				{ | ||||||
|  | 					return static_cast<size_t>(-1); | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				if (typename _Traits::compare(iter, second, count) == 0) | ||||||
|  | 				{ | ||||||
|  | 					return static_cast<size_t>(iter - first); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		template<class _Traits> | ||||||
|  | 		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<size_t>(iter - first); | ||||||
|  | 					} | ||||||
|  | 
 | ||||||
|  | 					if (iter == first) | ||||||
|  | 					{ | ||||||
|  | 						break; | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			return static_cast<size_t>(-1); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	String::allocator String::allocator_; | ||||||
|  | 	const String::size_type String::npos = static_cast<size_type>(-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<size_t>(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<String::traits>(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<String::traits>(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<bool*>(&operable_), *const_cast<bool*>(&rhs.operable_)); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	void String::discard_const_data() | ||||||
|  | 	{ | ||||||
|  | 		if (!operable_) | ||||||
|  | 		{ | ||||||
|  | 			// force to enable operability
 | ||||||
|  | 			*const_cast<bool*>(&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); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -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 <string> | ||||||
|  | 
 | ||||||
|  | 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<iterator>; | ||||||
|  | 		using const_reverse_iterator = std::reverse_iterator<const_iterator>; | ||||||
|  | 		using traits = std::char_traits<wchar_t>; | ||||||
|  | 		using allocator = std::allocator<wchar_t>; | ||||||
|  | 
 | ||||||
|  | 		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<size_type>(-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<String::traits::char_type, String::traits>& | ||||||
|  | 		operator<<(std::basic_ostream<String::traits::char_type, String::traits>& os, const String & str) | ||||||
|  | 	{ | ||||||
|  | 		return os << str.c_str(); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	inline std::basic_istream<String::traits::char_type, String::traits>& | ||||||
|  | 		operator>>(std::basic_istream<String::traits::char_type, String::traits>& is, String & str) | ||||||
|  | 	{ | ||||||
|  | 		using Ctype = std::ctype<wchar_t>; | ||||||
|  | 		using IStream = std::basic_istream<wchar_t, String::traits>; | ||||||
|  | 		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<Ctype>(is.getloc()); | ||||||
|  | 			str.erase(); | ||||||
|  | 
 | ||||||
|  | 			try | ||||||
|  | 			{ | ||||||
|  | 				SizeType size = (0 < is.width() && static_cast<SizeType>(is.width()) < str.max_size()) | ||||||
|  | 					? static_cast<SizeType>(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<easy2d::String> | ||||||
|  | 	{ | ||||||
|  | 		size_t operator()(const easy2d::String& key) const | ||||||
|  | 		{ | ||||||
|  | 			return key.hash(); | ||||||
|  | 		} | ||||||
|  | 	}; | ||||||
|  | } | ||||||
|  | @ -68,7 +68,7 @@ namespace easy2d | ||||||
| 			template<int... _Num> | 			template<int... _Num> | ||||||
| 			static inline std::function<_Ret(_Args...)> MakeFunc(_Ty* _Ptr, _Ret(_Ty::*_Func)(_Args...), Seq<_Num...>) | 			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>()...); | ||||||
| 			} | 			} | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -21,7 +21,6 @@ | ||||||
| #include "DebugNode.h" | #include "DebugNode.h" | ||||||
| #include "Text.h" | #include "Text.h" | ||||||
| #include "render.h" | #include "render.h" | ||||||
| #include "../utils/string.h" |  | ||||||
| #include <sstream> | #include <sstream> | ||||||
| #include <psapi.h> | #include <psapi.h> | ||||||
| 
 | 
 | ||||||
|  | @ -64,19 +63,11 @@ namespace easy2d | ||||||
| 	{ | 	{ | ||||||
| 		E2D_NOT_USED(dt); | 		E2D_NOT_USED(dt); | ||||||
| 
 | 
 | ||||||
| 		try |  | ||||||
| 		{ |  | ||||||
| 		frame_time_.push_back(time::Now()); | 		frame_time_.push_back(time::Now()); | ||||||
| 		while (frame_time_.back() - frame_time_.front() >= time::Second) | 		while (frame_time_.back() - frame_time_.front() >= time::Second) | ||||||
| 		{ | 		{ | ||||||
| 			frame_time_.erase(frame_time_.begin()); | 			frame_time_.erase(frame_time_.begin()); | ||||||
| 		} | 		} | ||||||
| 		} |  | ||||||
| 		catch (std::exception& e) |  | ||||||
| 		{ |  | ||||||
| 			debug_text_->SetText(StringMultiByteToWideChar(e.what())); |  | ||||||
| 			return; |  | ||||||
| 		} |  | ||||||
| 		 | 		 | ||||||
| 		std::wstringstream ss; | 		std::wstringstream ss; | ||||||
| 		ss << "Fps: " << frame_time_.size() << std::endl; | 		ss << "Fps: " << frame_time_.size() << std::endl; | ||||||
|  |  | ||||||
|  | @ -22,7 +22,6 @@ | ||||||
| #include "logs.h" | #include "logs.h" | ||||||
| #include "render.h" | #include "render.h" | ||||||
| #include "modules.h" | #include "modules.h" | ||||||
| #include "../utils/string.h" |  | ||||||
| 
 | 
 | ||||||
| namespace easy2d | namespace easy2d | ||||||
| { | { | ||||||
|  |  | ||||||
|  | @ -21,6 +21,7 @@ | ||||||
| #pragma once | #pragma once | ||||||
| #include "RefCounter.hpp" | #include "RefCounter.hpp" | ||||||
| #include "../common/Array.h" | #include "../common/Array.h" | ||||||
|  | #include "../common/String.h" | ||||||
| #include "../common/IntrusivePtr.hpp" | #include "../common/IntrusivePtr.hpp" | ||||||
| #include <set> | #include <set> | ||||||
| #include <map> | #include <map> | ||||||
|  | @ -44,7 +45,6 @@ | ||||||
| 
 | 
 | ||||||
| namespace easy2d | namespace easy2d | ||||||
| { | { | ||||||
| 	using String = std::wstring; |  | ||||||
| 	using StringStream = std::wstringstream; | 	using StringStream = std::wstringstream; | ||||||
| 
 | 
 | ||||||
| 	template<typename Type1, typename Type2> | 	template<typename Type1, typename Type2> | ||||||
|  |  | ||||||
|  | @ -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); | 		HINSTANCE hinst		= GetModuleHandleW(nullptr); | ||||||
| 		WNDCLASSEX wcex		= { 0 }; | 		WNDCLASSEX wcex		= { 0 }; | ||||||
|  |  | ||||||
|  | @ -52,7 +52,7 @@ namespace easy2d | ||||||
| 
 | 
 | ||||||
| 	public: | 	public: | ||||||
| 		HRESULT Create( | 		HRESULT Create( | ||||||
| 			String	title, | 			String const&	title, | ||||||
| 			int				width, | 			int				width, | ||||||
| 			int				height, | 			int				height, | ||||||
| 			LPCWSTR			icon, | 			LPCWSTR			icon, | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue