#ifndef _WSTRING_BUILDER_9bf1541fdf7efd41b7b39543fd870ac4_
#define _WSTRING_BUILDER_9bf1541fdf7efd41b7b39543fd870ac4_
#include <memory.h>
#include "WString.h"
// 스트링의 m_ptr이 불변임을 확신하는 경우
// capacity보다 높은 자원에 한해서
// 해당 포인터를 그대로 불러와 복사에 낭비되는 자원을 회수합니다.
//#define STRING_BUILDER_COPY_NATIVE
namespace Utility {
using namespace Inherit;
//
// 길이에 상관없이 긴 일련의 문자열 집합을 매우 빠르게 생성하는 알고리즘입니다.
//
class WStringBuilder
{
size_t capacity = 1024;
size_t m_offset;
size_t m_length;
bool m_top;
bool m_nodispose;
wchar_t *m_ptr;
WStringBuilder *m_prev;
// 복사 생성자
WStringBuilder(WStringBuilder& th)
{
m_offset = th.m_offset;
m_length = th.m_length;
m_ptr = th.m_ptr;
m_prev = th.m_prev;
m_nodispose = th.m_nodispose;
}
public:
WStringBuilder()
: m_offset(0)
, m_length(0)
, m_ptr(nullptr)
, m_prev(nullptr)
, m_top(false)
, m_nodispose(false)
{
}
WStringBuilder(size_t capacity)
: WStringBuilder()
{
this->capacity = capacity;
}
WStringBuilder(WString& refer)
: WStringBuilder()
{
Append(refer);
}
WStringBuilder(WString& refer, size_t capacity)
: WStringBuilder(capacity)
{
Append(refer);
}
~WStringBuilder()
{
// 사용자가 알아서 Dispose를 호출하게함
}
void Append(const WString& refer)
{
if (!refer.Empty())
{
if (capacity > refer.Length() + m_length)
{
__Ensure();
wmemcpy_s(m_ptr + m_length, refer.Length(), refer.Reference(), refer.Length());
m_length += refer.Length();
}
else
{
__Expand();
if (capacity <= refer.Length())
{
#ifdef STRING_BUILDER_COPY_NATIVE
m_ptr = (wchar_t *)refer.Reference();
m_nodispose = true;
#else
m_ptr = (wchar_t *)refer.ToArray();
#endif
m_length = refer.Length();
}
else
{
Append(refer);
return;
}
__LinkTo();
}
}
}
void Append(const wchar_t *str, size_t len)
{
if (len > 0)
{
if (capacity > len + m_length)
{
__Ensure();
wmemcpy_s(m_ptr + m_length, len, str, len);
m_length += len;
}
else
{
__Expand();
m_length = len;
m_ptr = new wchar_t[len];
wmemcpy_s(m_ptr, len, str, len);
__LinkTo();
}
}
}
void Append(const wchar_t *str)
{
Append(str, len);
}
// 입력되는 스트링이 매우 클 경우를 확신하는 경우
// 해당 포인터를 그래로 불러와 복사에 낭비되는 자원을 회수합니다.
// 많은 __LinkTo 호출은 많은 할당을 필요함으로, 작은 스트링에선
// 적절하지 않습니다.
// 이 함수는 Append와 같이 사용되어선 안됩니다.
/*void AppendVeryLarge(WString& refer)
{
m_ptr = (wchar_t *)refer.Reference();
m_length = refer.Length();
__LinkTo();
}*/
size_t Length() const
{
return m_offset;
}
WString ToString()
{
size_t len = m_offset + m_length + 1;
wchar_t *newString = new wchar_t[len];
WStringBuilder *iter = this;
for ( ; iter != nullptr; iter = iter->m_prev )
{
wmemcpy_s(newString + iter->m_offset, iter->m_length, iter->m_ptr, iter->m_length);
}
newString[m_offset + m_length] = 0;
StringReferencePutWith with;
return *new WString(newString, m_offset + m_length, with);
}
void Dispose()
{
WStringBuilder *iter = this;
for ( ; iter != nullptr; iter = iter->m_prev )
{
delete[] iter->m_ptr;
iter->m_ptr = nullptr;
}
for ( iter = this; iter->m_prev != nullptr; )
{
WStringBuilder *tmp = iter->m_prev;
iter->m_prev = iter->m_prev->m_prev;
delete tmp;
}
}
private:
void __Ensure()
{
if (m_ptr == nullptr)
{
m_ptr = new wchar_t[capacity];
}
}
bool __Expand()
{
if (m_length > 0)
{
__LinkTo();
return true;
}
return false;
}
// 포인터로 전 StringBuilder와 링크합니다.
void __LinkTo()
{
m_top = false;
m_prev = new WStringBuilder(*this);
m_offset += m_length;
m_ptr = nullptr;
m_length = 0;
m_top = true;
m_nodispose = false;
}
};
}
#endif