// 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(min_buf_size); memcpy(p, m_pStr, m_len + 1); crnlib_delete_array(m_pStr); m_pStr = p; m_buf_size = static_cast(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(max_len, static_cast(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(len); } else if (ensure_buf(len, false)) { m_len = static_cast(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(max_len); } } else { const uint len = math::minimum(max_len, other.m_len); if (!len) clear(); else if (ensure_buf(len, false)) { m_len = static_cast(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(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(buf_size_in_chars); m_len = static_cast(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(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(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(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(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(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(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(len, m_len - start); if (start) memmove(get_ptr_priv(), get_ptr_priv() + start, len); m_pStr[len] = '\0'; m_len = static_cast(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(static_cast(get_len()) - static_cast(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(cUINT16_MAX, math::next_pow2(math::maximum(m_buf_size, new_buf_size))); if (new_buf_size != m_buf_size) { char* p = crnlib_new_array(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(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