xenia/third_party/crunch/crnlib/crn_dynamic_string.cpp

669 lines
14 KiB
C++

// File: crn_dynamic_string.cpp
// See Copyright Notice and license at the end of inc/crnlib.h
#include "crn_core.h"
#include "crn_strutils.h"
namespace crnlib
{
dynamic_string g_empty_dynamic_string;
dynamic_string::dynamic_string(eVarArg dummy, const char* p, ...) :
m_buf_size(0), m_len(0), m_pStr(NULL)
{
dummy;
CRNLIB_ASSERT(p);
va_list args;
va_start(args, p);
format_args(p, args);
va_end(args);
}
dynamic_string::dynamic_string(const char* p) :
m_buf_size(0), m_len(0), m_pStr(NULL)
{
CRNLIB_ASSERT(p);
set(p);
}
dynamic_string::dynamic_string(const char* p, uint len) :
m_buf_size(0), m_len(0), m_pStr(NULL)
{
CRNLIB_ASSERT(p);
set_from_buf(p, len);
}
dynamic_string::dynamic_string(const dynamic_string& other) :
m_buf_size(0), m_len(0), m_pStr(NULL)
{
set(other);
}
void dynamic_string::clear()
{
check();
if (m_pStr)
{
crnlib_delete_array(m_pStr);
m_pStr = NULL;
m_len = 0;
m_buf_size = 0;
}
}
void dynamic_string::empty()
{
truncate(0);
}
void dynamic_string::optimize()
{
if (!m_len)
clear();
else
{
uint min_buf_size = math::next_pow2((uint)m_len + 1);
if (m_buf_size > min_buf_size)
{
char* p = crnlib_new_array<char>(min_buf_size);
memcpy(p, m_pStr, m_len + 1);
crnlib_delete_array(m_pStr);
m_pStr = p;
m_buf_size = static_cast<uint16>(min_buf_size);
check();
}
}
}
int dynamic_string::compare(const char* p, bool case_sensitive) const
{
CRNLIB_ASSERT(p);
const int result = (case_sensitive ? strcmp : crn_stricmp)(get_ptr_priv(), p);
if (result < 0)
return -1;
else if (result > 0)
return 1;
return 0;
}
int dynamic_string::compare(const dynamic_string& rhs, bool case_sensitive) const
{
return compare(rhs.get_ptr_priv(), case_sensitive);
}
dynamic_string& dynamic_string::set(const char* p, uint max_len)
{
CRNLIB_ASSERT(p);
const uint len = math::minimum<uint>(max_len, static_cast<uint>(strlen(p)));
CRNLIB_ASSERT(len < cUINT16_MAX);
if ((!len) || (len >= cUINT16_MAX))
clear();
else if ((m_pStr) && (p >= m_pStr) && (p < (m_pStr + m_buf_size)))
{
if (m_pStr != p)
memmove(m_pStr, p, len);
m_pStr[len] = '\0';
m_len = static_cast<uint16>(len);
}
else if (ensure_buf(len, false))
{
m_len = static_cast<uint16>(len);
memcpy(m_pStr, p, m_len + 1);
}
check();
return *this;
}
dynamic_string& dynamic_string::set(const dynamic_string& other, uint max_len)
{
if (this == &other)
{
if (max_len < m_len)
{
m_pStr[max_len] = '\0';
m_len = static_cast<uint16>(max_len);
}
}
else
{
const uint len = math::minimum<uint>(max_len, other.m_len);
if (!len)
clear();
else if (ensure_buf(len, false))
{
m_len = static_cast<uint16>(len);
memcpy(m_pStr, other.get_ptr_priv(), m_len);
m_pStr[len] = '\0';
}
}
check();
return *this;
}
bool dynamic_string::set_len(uint new_len, char fill_char)
{
if ((new_len >= cUINT16_MAX) || (!fill_char))
{
CRNLIB_ASSERT(0);
return false;
}
uint cur_len = m_len;
if (ensure_buf(new_len, true))
{
if (new_len > cur_len)
memset(m_pStr + cur_len, fill_char, new_len - cur_len);
m_pStr[new_len] = 0;
m_len = static_cast<uint16>(new_len);
check();
}
return true;
}
dynamic_string& dynamic_string::set_from_raw_buf_and_assume_ownership(char *pBuf, uint buf_size_in_chars, uint len_in_chars)
{
CRNLIB_ASSERT(buf_size_in_chars <= cUINT16_MAX);
CRNLIB_ASSERT(math::is_power_of_2(buf_size_in_chars) || (buf_size_in_chars == cUINT16_MAX));
CRNLIB_ASSERT((len_in_chars + 1) <= buf_size_in_chars);
clear();
m_pStr = pBuf;
m_buf_size = static_cast<uint16>(buf_size_in_chars);
m_len = static_cast<uint16>(len_in_chars);
check();
return *this;
}
dynamic_string& dynamic_string::set_from_buf(const void* pBuf, uint buf_size)
{
CRNLIB_ASSERT(pBuf);
if (buf_size >= cUINT16_MAX)
{
clear();
return *this;
}
#ifdef CRNLIB_BUILD_DEBUG
if ((buf_size) && (memchr(pBuf, 0, buf_size) != NULL))
{
CRNLIB_ASSERT(0);
clear();
return *this;
}
#endif
if (ensure_buf(buf_size, false))
{
if (buf_size)
memcpy(m_pStr, pBuf, buf_size);
m_pStr[buf_size] = 0;
m_len = static_cast<uint16>(buf_size);
check();
}
return *this;
}
dynamic_string& dynamic_string::set_char(uint index, char c)
{
CRNLIB_ASSERT(index <= m_len);
if (!c)
truncate(index);
else if (index < m_len)
{
m_pStr[index] = c;
check();
}
else if (index == m_len)
append_char(c);
return *this;
}
dynamic_string& dynamic_string::append_char(char c)
{
if (ensure_buf(m_len + 1))
{
m_pStr[m_len] = c;
m_pStr[m_len + 1] = '\0';
m_len++;
check();
}
return *this;
}
dynamic_string& dynamic_string::truncate(uint new_len)
{
if (new_len < m_len)
{
m_pStr[new_len] = '\0';
m_len = static_cast<uint16>(new_len);
check();
}
return *this;
}
dynamic_string& dynamic_string::tolower()
{
if (m_len)
{
#ifdef _MSC_VER
_strlwr_s(get_ptr_priv(), m_buf_size);
#else
strlwr(get_ptr_priv());
#endif
}
return *this;
}
dynamic_string& dynamic_string::toupper()
{
if (m_len)
{
#ifdef _MSC_VER
_strupr_s(get_ptr_priv(), m_buf_size);
#else
strupr(get_ptr_priv());
#endif
}
return *this;
}
dynamic_string& dynamic_string::append(const char* p)
{
CRNLIB_ASSERT(p);
uint len = static_cast<uint>(strlen(p));
uint new_total_len = m_len + len;
if ((new_total_len) && ensure_buf(new_total_len))
{
memcpy(m_pStr + m_len, p, len + 1);
m_len = static_cast<uint16>(m_len + len);
check();
}
return *this;
}
dynamic_string& dynamic_string::append(const dynamic_string& other)
{
uint len = other.m_len;
uint new_total_len = m_len + len;
if ((new_total_len) && ensure_buf(new_total_len))
{
memcpy(m_pStr + m_len, other.get_ptr_priv(), len + 1);
m_len = static_cast<uint16>(m_len + len);
check();
}
return *this;
}
dynamic_string operator+ (const char* p, const dynamic_string& a)
{
return dynamic_string(p).append(a);
}
dynamic_string operator+ (const dynamic_string& a, const char* p)
{
return dynamic_string(a).append(p);
}
dynamic_string operator+ (const dynamic_string& a, const dynamic_string& b)
{
return dynamic_string(a).append(b);
}
dynamic_string& dynamic_string::format_args(const char* p, va_list args)
{
CRNLIB_ASSERT(p);
const uint cBufSize = 4096;
char buf[cBufSize];
#ifdef _MSC_VER
int l = vsnprintf_s(buf, cBufSize, _TRUNCATE, p, args);
#else
int l = vsnprintf(buf, cBufSize, p, args);
#endif
if (l <= 0)
clear();
else if (ensure_buf(l, false))
{
memcpy(m_pStr, buf, l + 1);
m_len = static_cast<uint16>(l);
check();
}
return *this;
}
dynamic_string& dynamic_string::format(const char* p, ...)
{
CRNLIB_ASSERT(p);
va_list args;
va_start(args, p);
format_args(p, args);
va_end(args);
return *this;
}
dynamic_string& dynamic_string::crop(uint start, uint len)
{
if (start >= m_len)
{
clear();
return *this;
}
len = math::minimum<uint>(len, m_len - start);
if (start)
memmove(get_ptr_priv(), get_ptr_priv() + start, len);
m_pStr[len] = '\0';
m_len = static_cast<uint16>(len);
check();
return *this;
}
dynamic_string& dynamic_string::substring(uint start, uint end)
{
CRNLIB_ASSERT(start <= end);
if (start > end)
return *this;
return crop(start, end - start);
}
dynamic_string& dynamic_string::left(uint len)
{
return substring(0, len);
}
dynamic_string& dynamic_string::mid(uint start, uint len)
{
return crop(start, len);
}
dynamic_string& dynamic_string::right(uint start)
{
return substring(start, get_len());
}
dynamic_string& dynamic_string::tail(uint num)
{
return substring(math::maximum<int>(static_cast<int>(get_len()) - static_cast<int>(num), 0), get_len());
}
dynamic_string& dynamic_string::unquote()
{
if (m_len >= 2)
{
if ( ((*this)[0] == '\"') && ((*this)[m_len - 1] == '\"') )
{
return mid(1, m_len - 2);
}
}
return *this;
}
int dynamic_string::find_left(const char* p, bool case_sensitive) const
{
CRNLIB_ASSERT(p);
const int p_len = (int)strlen(p);
for (int i = 0; i <= (m_len - p_len); i++)
if ((case_sensitive ? strncmp : _strnicmp)(p, &m_pStr[i], p_len) == 0)
return i;
return -1;
}
bool dynamic_string::contains(const char* p, bool case_sensitive) const
{
return find_left(p, case_sensitive) >= 0;
}
uint dynamic_string::count_char(char c) const
{
uint count = 0;
for (uint i = 0; i < m_len; i++)
if (m_pStr[i] == c)
count++;
return count;
}
int dynamic_string::find_left(char c) const
{
for (uint i = 0; i < m_len; i++)
if (m_pStr[i] == c)
return i;
return -1;
}
int dynamic_string::find_right(char c) const
{
for (int i = (int)m_len - 1; i >= 0; i--)
if (m_pStr[i] == c)
return i;
return -1;
}
int dynamic_string::find_right(const char* p, bool case_sensitive) const
{
CRNLIB_ASSERT(p);
const int p_len = (int)strlen(p);
for (int i = m_len - p_len; i >= 0; i--)
if ((case_sensitive ? strncmp : _strnicmp)(p, &m_pStr[i], p_len) == 0)
return i;
return -1;
}
dynamic_string& dynamic_string::trim()
{
int s, e;
for (s = 0; s < (int)m_len; s++)
if (!isspace(m_pStr[s]))
break;
for (e = m_len - 1; e > s; e--)
if (!isspace(m_pStr[e]))
break;
return crop(s, e - s + 1);
}
dynamic_string& dynamic_string::trim_crlf()
{
int s = 0, e;
for (e = m_len - 1; e > s; e--)
if ((m_pStr[e] != 13) && (m_pStr[e] != 10))
break;
return crop(s, e - s + 1);
}
dynamic_string& dynamic_string::remap(int from_char, int to_char)
{
for (uint i = 0; i < m_len; i++)
if (m_pStr[i] == from_char)
m_pStr[i] = (char)to_char;
return *this;
}
#ifdef CRNLIB_BUILD_DEBUG
void dynamic_string::check() const
{
if (!m_pStr)
{
CRNLIB_ASSERT(!m_buf_size && !m_len);
}
else
{
CRNLIB_ASSERT(m_buf_size);
CRNLIB_ASSERT((m_buf_size == cUINT16_MAX) || math::is_power_of_2((uint32)m_buf_size));
CRNLIB_ASSERT(m_len < m_buf_size);
CRNLIB_ASSERT(!m_pStr[m_len]);
#if CRNLIB_SLOW_STRING_LEN_CHECKS
CRNLIB_ASSERT(strlen(m_pStr) == m_len);
#endif
}
}
#endif
bool dynamic_string::ensure_buf(uint len, bool preserve_contents)
{
uint buf_size_needed = len + 1;
CRNLIB_ASSERT(buf_size_needed <= cUINT16_MAX);
if (buf_size_needed <= cUINT16_MAX)
{
if (buf_size_needed > m_buf_size)
expand_buf(buf_size_needed, preserve_contents);
}
return m_buf_size >= buf_size_needed;
}
bool dynamic_string::expand_buf(uint new_buf_size, bool preserve_contents)
{
new_buf_size = math::minimum<uint>(cUINT16_MAX, math::next_pow2(math::maximum<uint>(m_buf_size, new_buf_size)));
if (new_buf_size != m_buf_size)
{
char* p = crnlib_new_array<char>(new_buf_size);
if (preserve_contents)
memcpy(p, get_ptr_priv(), m_len + 1);
crnlib_delete_array(m_pStr);
m_pStr = p;
m_buf_size = static_cast<uint16>(new_buf_size);
if (preserve_contents)
check();
}
return m_buf_size >= new_buf_size;
}
void dynamic_string::swap(dynamic_string& other)
{
utils::swap(other.m_buf_size, m_buf_size);
utils::swap(other.m_len, m_len);
utils::swap(other.m_pStr, m_pStr);
}
int dynamic_string::serialize(void* pBuf, uint buf_size, bool little_endian) const
{
uint buf_left = buf_size;
//if (m_len > cUINT16_MAX)
// return -1;
CRNLIB_ASSUME(sizeof(m_len) == sizeof(uint16));
if (!utils::write_val((uint16)m_len, pBuf, buf_left, little_endian))
return -1;
if (buf_left < m_len)
return -1;
memcpy(pBuf, get_ptr(), m_len);
buf_left -= m_len;
return buf_size - buf_left;
}
int dynamic_string::deserialize(const void* pBuf, uint buf_size, bool little_endian)
{
uint buf_left = buf_size;
if (buf_left < sizeof(uint16)) return -1;
uint16 l;
if (!utils::read_obj(l, pBuf, buf_left, little_endian))
return -1;
if (buf_left < l)
return -1;
set_from_buf(pBuf, l);
buf_left -= l;
return buf_size - buf_left;
}
void dynamic_string::translate_lf_to_crlf()
{
if (find_left(0x0A) < 0)
return;
dynamic_string tmp;
tmp.ensure_buf(m_len + 2);
// normal sequence is 0x0D 0x0A (CR LF, \r\n)
int prev_char = -1;
for (uint i = 0; i < get_len(); i++)
{
const int cur_char = (*this)[i];
if ((cur_char == 0x0A) && (prev_char != 0x0D))
tmp.append_char(0x0D);
tmp.append_char(cur_char);
prev_char = cur_char;
}
swap(tmp);
}
} // namespace crnlib