mirror of
https://github.com/NoelFB/blah.git
synced 2025-04-11 01:26:05 +08:00
429 lines
7.8 KiB
C++
429 lines
7.8 KiB
C++
#include <blah/containers/str.h>
|
|
#include <string.h> // for strcpy etc
|
|
#include <stdarg.h> // for format methods
|
|
#include <stdio.h> // for sprintf
|
|
#include <cctype> // toupper
|
|
|
|
using namespace Blah;
|
|
|
|
const uint16_t UTF8_LEAD_SURROGATE_MIN = 0xd800u;
|
|
const uint16_t UTF8_LEAD_SURROGATE_MAX = 0xdbffu;
|
|
const uint16_t UTF8_TRAIL_SURROGATE_MIN = 0xdc00u;
|
|
const uint16_t UTF8_TRAIL_SURROGATE_MAX = 0xdfffu;
|
|
const uint16_t UTF8_LEAD_OFFSET = UTF8_LEAD_SURROGATE_MIN - (0x10000 >> 10);
|
|
const uint32_t UTF8_SURROGATE_OFFSET = 0x10000u - (UTF8_LEAD_SURROGATE_MIN << 10) - UTF8_TRAIL_SURROGATE_MIN;
|
|
|
|
char Str::empty_buffer[1] = { '\0' };
|
|
|
|
bool Str::operator==(const Str& rhs) const
|
|
{
|
|
return strcmp(cstr(), rhs.cstr()) == 0;
|
|
}
|
|
|
|
bool Str::operator!=(const Str& rhs) const
|
|
{
|
|
return strcmp(cstr(), rhs.cstr()) != 0;
|
|
}
|
|
|
|
bool Str::operator==(const char* rhs) const
|
|
{
|
|
return strcmp(cstr(), rhs) == 0;
|
|
}
|
|
|
|
bool Str::operator!=(const char* rhs) const
|
|
{
|
|
return strcmp(cstr(), rhs) != 0;
|
|
}
|
|
|
|
void Str::reserve(int size)
|
|
{
|
|
int buffer_length = size + 1;
|
|
if (buffer_length > m_capacity)
|
|
{
|
|
if (m_capacity <= 0)
|
|
m_capacity = 16;
|
|
|
|
while (m_capacity < buffer_length)
|
|
m_capacity *= 2;
|
|
|
|
// expand from local buffer
|
|
if (m_buffer == nullptr)
|
|
{
|
|
char* local = data();
|
|
m_buffer = new char[m_capacity];
|
|
strncpy(m_buffer, local, m_local_size);
|
|
m_buffer[m_local_size] = '\0';
|
|
}
|
|
// expand from empty buffer
|
|
else if (m_buffer == empty_buffer)
|
|
{
|
|
m_buffer = new char[m_capacity];
|
|
m_buffer[0] = '\0';
|
|
}
|
|
// expand from existing heap buffer
|
|
else
|
|
{
|
|
char* new_buffer = new char[m_capacity];
|
|
memcpy(new_buffer, m_buffer, m_length);
|
|
new_buffer[m_length] = '\0';
|
|
delete[] m_buffer;
|
|
m_buffer = new_buffer;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Str::set_length(int len)
|
|
{
|
|
reserve(len);
|
|
data()[len] = '\0';
|
|
m_length = len;
|
|
}
|
|
|
|
Str& Str::append(char c)
|
|
{
|
|
reserve(m_length + 1);
|
|
data()[m_length++] = c;
|
|
data()[m_length] = '\0';
|
|
return *this;
|
|
}
|
|
|
|
Str& Str::append(uint32_t c)
|
|
{
|
|
// one octet
|
|
if (c < 0x80)
|
|
{
|
|
append((char)c);
|
|
}
|
|
// two octets
|
|
else if (c < 0x800)
|
|
{
|
|
append((char)((c >> 6) | 0xc0));
|
|
append((char)((c & 0x3f) | 0x80));
|
|
}
|
|
// three octets
|
|
else if (c < 0x10000)
|
|
{
|
|
append((char)((c >> 12) | 0xe0));
|
|
append((char)(((c >> 6) & 0x3f) | 0x80));
|
|
append((char)((c & 0x3f) | 0x80));
|
|
}
|
|
// four octets
|
|
else
|
|
{
|
|
append((char)((c >> 18) | 0xf0));
|
|
append((char)(((c >> 12) & 0x3f) | 0x80));
|
|
append((char)(((c >> 6) & 0x3f) | 0x80));
|
|
append((char)((c & 0x3f) | 0x80));
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
Str& Str::append(const char* start, const char* end)
|
|
{
|
|
if (end == nullptr)
|
|
end = start + strlen(start);
|
|
|
|
int add = (int)(end - start);
|
|
if (add <= 0)
|
|
return *this;
|
|
|
|
reserve(m_length + add);
|
|
memcpy(data() + m_length, start, add);
|
|
m_length += add;
|
|
data()[m_length] = '\0';
|
|
|
|
return *this;
|
|
}
|
|
|
|
Str& Str::append(const Str& str, int start, int end)
|
|
{
|
|
if (end < 0) end = str.m_length;
|
|
if (end > str.m_length) end = str.m_length;
|
|
if (start < 0) start = 0;
|
|
if (start > end) start = end;
|
|
|
|
return append(str.begin() + start, str.begin() + end);
|
|
}
|
|
|
|
Str& Str::append_fmt(const char* fmt, ...)
|
|
{
|
|
int add, diff;
|
|
|
|
// determine arg m_length
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
add = vsnprintf(NULL, 0, fmt, args);
|
|
va_end(args);
|
|
|
|
// reserve
|
|
reserve(m_length + add);
|
|
diff = m_capacity - m_length;
|
|
if (diff <= 0) diff = 0;
|
|
|
|
// print out
|
|
va_start(args, fmt);
|
|
vsnprintf(data() + m_length, (size_t)diff, fmt, args);
|
|
va_end(args);
|
|
|
|
// increment size
|
|
m_length += add;
|
|
data()[m_length] = '\0';
|
|
|
|
return *this;
|
|
}
|
|
|
|
Str& Str::append_utf16(const uint16_t* start, const uint16_t* end, bool swapEndian)
|
|
{
|
|
while (start != end)
|
|
{
|
|
uint16_t next = (*start++);
|
|
if (swapEndian)
|
|
next = ((next & 0xff) << 8 | ((next & 0xff00) >> 8));
|
|
|
|
uint32_t cp = 0xffff & next;
|
|
|
|
if ((cp >= UTF8_LEAD_SURROGATE_MIN && cp <= UTF8_LEAD_SURROGATE_MAX))
|
|
{
|
|
next = (*start++);
|
|
if (swapEndian)
|
|
next = ((next & 0xff) << 8 | ((next & 0xff00) >> 8));
|
|
|
|
uint32_t trail = 0xffff & next;
|
|
cp = (cp << 10) + trail + UTF8_SURROGATE_OFFSET;
|
|
}
|
|
|
|
append(cp);
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
Str& Str::trim()
|
|
{
|
|
const char* s = begin();
|
|
const char* e = end() - 1;
|
|
|
|
while (isspace(*s) && s != e)
|
|
s++;
|
|
while (isspace(*e) && s != e)
|
|
e--;
|
|
|
|
set(s, e + 1);
|
|
return *this;
|
|
}
|
|
|
|
bool Str::starts_with(const char* str, bool ignoreCase) const
|
|
{
|
|
int len = (int)strlen(str);
|
|
if (len > m_length || len <= 0)
|
|
return false;
|
|
|
|
const char* a = data();
|
|
const char* b = str;
|
|
|
|
if (ignoreCase)
|
|
{
|
|
for (int n = 0; n < len; n++)
|
|
if (tolower(a[n]) != tolower(b[n]))
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
for (int n = 0; n < len; n++)
|
|
if (a[n] != b[n])
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Str::contains(const char* str, bool ignoreCase) const
|
|
{
|
|
int len = (int)strlen(str);
|
|
if (len > m_length || len <= 0)
|
|
return false;
|
|
|
|
const char* a = data();
|
|
const char* b = str;
|
|
|
|
for (int start = 0; start < m_length - len + 1; start++)
|
|
{
|
|
bool match = true;
|
|
|
|
if (ignoreCase)
|
|
{
|
|
for (int n = 0; n < len && match; n++)
|
|
if (tolower(a[start + n]) != tolower(b[n]))
|
|
match = false;
|
|
}
|
|
else
|
|
{
|
|
for (int n = 0; n < len && match; n++)
|
|
if (a[start + n] != b[n])
|
|
match = false;
|
|
}
|
|
|
|
if (match)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Str::ends_with(const char* str, bool ignoreCase) const
|
|
{
|
|
int len = (int)strlen(str);
|
|
if (len > m_length || len <= 0)
|
|
return false;
|
|
|
|
const char* a = data();
|
|
const char* b = str;
|
|
|
|
if (ignoreCase)
|
|
{
|
|
for (int n = m_length - len, i = 0; n < m_length; n++, i++)
|
|
if (tolower(a[n]) != tolower(b[i]))
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
for (int n = m_length - len, i = 0; n < m_length; n++, i++)
|
|
if (a[n] != b[i])
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int Str::first_index_of(char ch) const
|
|
{
|
|
const char* ptr = data();
|
|
for (int n = 0; n < m_length; n++)
|
|
if (ptr[n] == ch)
|
|
return n;
|
|
return -1;
|
|
}
|
|
|
|
int Str::last_index_of(char ch) const
|
|
{
|
|
const char* ptr = data();
|
|
for (int n = m_length - 1; n >= 0; n--)
|
|
if (ptr[n] == ch)
|
|
return n;
|
|
return -1;
|
|
}
|
|
|
|
String Str::substr(int start) const
|
|
{
|
|
if (start < 0) start = 0;
|
|
if (start > m_length) start = m_length;
|
|
|
|
return String(data() + start);
|
|
}
|
|
|
|
String Str::substr(int start, int end) const
|
|
{
|
|
if (start < 0) start = 0;
|
|
if (start > m_length) start = m_length;
|
|
if (end < 0) end = m_length + end;
|
|
if (end < start) end = start;
|
|
if (end > m_length) end = m_length;
|
|
|
|
return Str(data() + start, data() + end);
|
|
}
|
|
|
|
Vector<String> Str::split(char ch) const
|
|
{
|
|
Vector<String> result;
|
|
|
|
const char* ptr = data();
|
|
int last = 0;
|
|
int index = 1;
|
|
|
|
while (index < m_length)
|
|
{
|
|
if (ptr[index] == ch)
|
|
{
|
|
result.push_back(substr(last, index));
|
|
last = index + 1;
|
|
}
|
|
index++;
|
|
}
|
|
|
|
if (last < index)
|
|
result.push_back(substr(last, index));
|
|
|
|
return result;
|
|
}
|
|
|
|
Str& Str::replace(const Str& os, const Str& ns)
|
|
{
|
|
for (int i = 0; i < m_length - os.m_length + 1; i++)
|
|
{
|
|
if (strcmp(data() + i, os.data()) == 0)
|
|
{
|
|
if (ns.m_length > os.m_length)
|
|
reserve(ns.m_length - os.m_length);
|
|
|
|
memcpy(data() + i + os.m_length, data() + i + ns.m_length, m_length - i - os.m_length);
|
|
memcpy(data() + i, ns.cstr(), ns.m_length);
|
|
set_length(m_length + ns.m_length - os.m_length);
|
|
i += os.m_length - 1;
|
|
}
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
Str& Str::replace(char c, char r)
|
|
{
|
|
char* ptr = data();
|
|
for (int n = 0; n < m_length; n++)
|
|
if (ptr[n] == c)
|
|
ptr[n] = r;
|
|
return *this;
|
|
}
|
|
|
|
void Str::clear()
|
|
{
|
|
if (m_capacity > 0)
|
|
data()[0] = '\0';
|
|
m_length = 0;
|
|
}
|
|
|
|
void Str::dispose()
|
|
{
|
|
if (m_buffer != nullptr && m_buffer != empty_buffer)
|
|
delete[] m_buffer;
|
|
|
|
if (m_local_size > 0)
|
|
{
|
|
m_buffer = nullptr;
|
|
m_capacity = m_local_size;
|
|
data()[0] = '\0';
|
|
}
|
|
else
|
|
{
|
|
m_buffer = empty_buffer;
|
|
m_capacity = 0;
|
|
}
|
|
|
|
m_length = 0;
|
|
}
|
|
|
|
void Str::set(const char* start, const char* end)
|
|
{
|
|
// find the end
|
|
if (end == nullptr)
|
|
end = start + strlen(start);
|
|
|
|
// reserve
|
|
m_length = (int)(end - start);
|
|
reserve(m_length);
|
|
|
|
// copy the data over
|
|
char* ptr = data();
|
|
memcpy(ptr, start, m_length);
|
|
ptr[m_length] = '\0';
|
|
} |