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\noncopyable.hpp" />
|
||||
<ClInclude Include="..\..\src\common\Singleton.hpp" />
|
||||
<ClInclude Include="..\..\src\common\String.h" />
|
||||
<ClInclude Include="..\..\src\config.h" />
|
||||
<ClInclude Include="..\..\src\core\Action.h" />
|
||||
<ClInclude Include="..\..\src\core\ActionGroup.h" />
|
||||
|
|
@ -77,7 +78,6 @@
|
|||
<ClInclude Include="..\..\src\utils\File.h" />
|
||||
<ClInclude Include="..\..\src\utils\Path.h" />
|
||||
<ClInclude Include="..\..\src\utils\ResLoader.h" />
|
||||
<ClInclude Include="..\..\src\utils\string.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\src\audio\audio.cpp" />
|
||||
|
|
@ -86,6 +86,7 @@
|
|||
<ClCompile Include="..\..\src\audio\Player.cpp" />
|
||||
<ClCompile Include="..\..\src\audio\Transcoder.cpp" />
|
||||
<ClCompile Include="..\..\src\audio\Voice.cpp" />
|
||||
<ClCompile Include="..\..\src\common\String.cpp" />
|
||||
<ClCompile Include="..\..\src\core\Action.cpp" />
|
||||
<ClCompile Include="..\..\src\core\ActionGroup.cpp" />
|
||||
<ClCompile Include="..\..\src\core\ActionTween.cpp" />
|
||||
|
|
@ -127,7 +128,6 @@
|
|||
<ClCompile Include="..\..\src\utils\File.cpp" />
|
||||
<ClCompile Include="..\..\src\utils\Path.cpp" />
|
||||
<ClCompile Include="..\..\src\utils\ResLoader.cpp" />
|
||||
<ClCompile Include="..\..\src\utils\string.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
|
|
|
|||
|
|
@ -118,9 +118,6 @@
|
|||
<ClInclude Include="..\..\src\core\Transform.hpp">
|
||||
<Filter>core</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\utils\string.h">
|
||||
<Filter>utils</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\core\Geometry.h">
|
||||
<Filter>core</Filter>
|
||||
</ClInclude>
|
||||
|
|
@ -249,6 +246,9 @@
|
|||
<ClInclude Include="..\..\src\core\helper.h">
|
||||
<Filter>core</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\common\String.h">
|
||||
<Filter>common</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\src\core\Animation.cpp">
|
||||
|
|
@ -323,9 +323,6 @@
|
|||
<ClCompile Include="..\..\src\core\logs.cpp">
|
||||
<Filter>core</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\utils\string.cpp">
|
||||
<Filter>utils</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\core\Geometry.cpp">
|
||||
<Filter>core</Filter>
|
||||
</ClCompile>
|
||||
|
|
@ -395,5 +392,8 @@
|
|||
<ClCompile Include="..\..\src\core\Layer.cpp">
|
||||
<Filter>core</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\common\String.cpp">
|
||||
<Filter>common</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</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>
|
||||
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 "Text.h"
|
||||
#include "render.h"
|
||||
#include "../utils/string.h"
|
||||
#include <sstream>
|
||||
#include <psapi.h>
|
||||
|
||||
|
|
@ -64,19 +63,11 @@ namespace easy2d
|
|||
{
|
||||
E2D_NOT_USED(dt);
|
||||
|
||||
try
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
std::wstringstream ss;
|
||||
ss << "Fps: " << frame_time_.size() << std::endl;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@
|
|||
#include "logs.h"
|
||||
#include "render.h"
|
||||
#include "modules.h"
|
||||
#include "../utils/string.h"
|
||||
|
||||
namespace easy2d
|
||||
{
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#pragma once
|
||||
#include "RefCounter.hpp"
|
||||
#include "../common/Array.h"
|
||||
#include "../common/String.h"
|
||||
#include "../common/IntrusivePtr.hpp"
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
|
@ -44,7 +45,6 @@
|
|||
|
||||
namespace easy2d
|
||||
{
|
||||
using String = std::wstring;
|
||||
using StringStream = std::wstringstream;
|
||||
|
||||
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);
|
||||
WNDCLASSEX wcex = { 0 };
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ namespace easy2d
|
|||
|
||||
public:
|
||||
HRESULT Create(
|
||||
String title,
|
||||
String const& title,
|
||||
int width,
|
||||
int height,
|
||||
LPCWSTR icon,
|
||||
|
|
|
|||
Loading…
Reference in New Issue