408 lines
6.9 KiB
C++
408 lines
6.9 KiB
C++
#include "String.h"
|
|
|
|
#include "Char.h"
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
|
|
String::String()
|
|
{
|
|
m_data.push_back('\0');
|
|
}
|
|
|
|
String::String(size_t size, char c)
|
|
{
|
|
m_data.resize(size, c);
|
|
m_data.push_back('\0');
|
|
}
|
|
|
|
String::String(const Vector<Byte>& data)
|
|
{
|
|
append(data);
|
|
}
|
|
|
|
String::String(const char* data)
|
|
{
|
|
append(data);
|
|
}
|
|
|
|
String String::fmt(const char* fmt, ...)
|
|
{
|
|
String ret;
|
|
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
|
|
char format_delim = '%';
|
|
bool in_format = false;
|
|
while(*fmt != '\0')
|
|
{
|
|
if (*fmt == format_delim)
|
|
{
|
|
in_format = true;
|
|
}
|
|
else if(in_format && *fmt == 's')
|
|
{
|
|
in_format = false;
|
|
const auto s = va_arg(args, char*);
|
|
ret.append(s);
|
|
}
|
|
else if(in_format && *fmt == 'd')
|
|
{
|
|
in_format = false;
|
|
const auto i = va_arg(args, int);
|
|
ret += to_string(i);
|
|
}
|
|
else if(in_format && *fmt == 'c')
|
|
{
|
|
in_format = false;
|
|
const auto c = va_arg(args, int);
|
|
ret +=c;
|
|
}
|
|
else
|
|
{
|
|
ret += *fmt;
|
|
}
|
|
++fmt;
|
|
}
|
|
va_end(args);
|
|
return ret;
|
|
}
|
|
|
|
void String::append(const char* data)
|
|
{
|
|
if (data == nullptr)
|
|
{
|
|
m_data.push_back('\0');
|
|
return;
|
|
}
|
|
|
|
auto loc = data;
|
|
bool first=true;
|
|
while(*loc != '\0')
|
|
{
|
|
if (!m_data.empty() && first)
|
|
{
|
|
m_data[m_data.size() - 1] = *loc;
|
|
}
|
|
else
|
|
{
|
|
m_data.push_back(*loc);
|
|
}
|
|
first = false;
|
|
loc++;
|
|
}
|
|
m_data.push_back('\0');
|
|
}
|
|
|
|
void String::eraseIf(erasePredicate func)
|
|
{
|
|
size_t count{0};
|
|
for(size_t idx=0; idx<size(); idx++)
|
|
{
|
|
if (!func(m_data[idx]))
|
|
{
|
|
m_data[count] = m_data[idx];
|
|
count++;
|
|
}
|
|
}
|
|
m_data[count] = '\0';
|
|
}
|
|
|
|
bool String::is_whitespace() const
|
|
{
|
|
if (empty())
|
|
{
|
|
return true;
|
|
}
|
|
for(size_t idx=0;idx<size();idx++)
|
|
{
|
|
if (!Char::is_space(m_data[idx]))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
const Vector<char>& String::data() const
|
|
{
|
|
return m_data;
|
|
}
|
|
|
|
bool String::empty() const
|
|
{
|
|
return m_data.empty() || m_data[0] == '\0';
|
|
}
|
|
|
|
void String::to_bytes(Vector<Byte>& bytes) const
|
|
{
|
|
if (m_data.empty())
|
|
{
|
|
return;
|
|
}
|
|
for(size_t idx=0; idx<m_data.size() - 1; idx++)
|
|
{
|
|
bytes.push_back(m_data[idx]);
|
|
}
|
|
}
|
|
|
|
size_t String::length(const char* arr)
|
|
{
|
|
size_t len{0};
|
|
while(*arr != '\0')
|
|
{
|
|
arr++;
|
|
len++;
|
|
}
|
|
return len;
|
|
}
|
|
|
|
void String::append(const Vector<Byte>& data)
|
|
{
|
|
if (data.empty())
|
|
{
|
|
m_data.push_back('\0');
|
|
return;
|
|
}
|
|
|
|
if (m_data.size() == 1 && m_data[0] == '\0')
|
|
{
|
|
m_data.clear();
|
|
}
|
|
|
|
for(const auto c : data)
|
|
{
|
|
if(c == '\0')
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
m_data.push_back(static_cast<char>(c));
|
|
}
|
|
}
|
|
m_data.push_back('\0');
|
|
}
|
|
|
|
Pair<String, String> String::rsplit(char c) const
|
|
{
|
|
if (const auto index = rindex(c); index.valid())
|
|
{
|
|
String left;
|
|
slice(0, index.value(), left);
|
|
|
|
String right;
|
|
slice(index.value() + 1, size(), right);
|
|
return {left, right};
|
|
}
|
|
return {*this, {}};
|
|
}
|
|
|
|
bool String::slice(size_t idx, String& out) const
|
|
{
|
|
if (idx >= m_data.size())
|
|
{
|
|
return false;
|
|
}
|
|
auto ok = m_data.slice(idx, out.m_data);
|
|
if (!ok)
|
|
{
|
|
return ok;
|
|
}
|
|
out.m_data.push_back('\0');
|
|
return true;
|
|
}
|
|
|
|
bool String::slice(size_t start, size_t end, String& out) const
|
|
{
|
|
if (end >= m_data.size())
|
|
{
|
|
return false;
|
|
}
|
|
auto ok = m_data.slice(start, end, out.m_data);
|
|
if (!ok)
|
|
{
|
|
return ok;
|
|
}
|
|
out.m_data.push_back('\0');
|
|
return true;
|
|
}
|
|
|
|
Index String::rindex(char c) const
|
|
{
|
|
if (m_data.size() <= 2)
|
|
{
|
|
return {};
|
|
}
|
|
for(size_t idx=m_data.size()-2; idx >= 0; idx--)
|
|
{
|
|
if (m_data[idx] == c)
|
|
{
|
|
return Index(idx);
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
const char* String::raw() const
|
|
{
|
|
return m_data.data();
|
|
}
|
|
|
|
size_t String::size() const
|
|
{
|
|
return m_data.size() - 1;
|
|
}
|
|
|
|
void String::reverse()
|
|
{
|
|
if (m_data.size() == 1)
|
|
{
|
|
return;
|
|
}
|
|
for(size_t idx=0; idx<(m_data.size()-1)/2; idx++)
|
|
{
|
|
const auto ridx = m_data.size() - 2 - idx;
|
|
const auto tmp0 = m_data[idx];
|
|
m_data[idx] = m_data[ridx];
|
|
m_data[ridx] = tmp0;
|
|
}
|
|
}
|
|
|
|
void String::split(Vector<String>& output, char delimiter) const
|
|
{
|
|
String working_string;
|
|
bool last_was_non_delimiter{ false };
|
|
for (size_t idx = 0; idx<size(); idx++)
|
|
{
|
|
const auto c = m_data[idx];
|
|
if (c == delimiter)
|
|
{
|
|
if (last_was_non_delimiter)
|
|
{
|
|
output.push_back(working_string);
|
|
working_string = "";
|
|
last_was_non_delimiter = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
last_was_non_delimiter = true;
|
|
working_string += c;
|
|
}
|
|
}
|
|
if (!working_string.empty())
|
|
{
|
|
output.push_back(working_string);
|
|
}
|
|
}
|
|
|
|
String String::to_string(size_t input)
|
|
{
|
|
String conv;
|
|
auto input_cpy = input;
|
|
while(input_cpy > 0)
|
|
{
|
|
const auto rem = input_cpy % 10;
|
|
conv += static_cast<char>(48 + rem);
|
|
input_cpy /= 10;
|
|
}
|
|
conv.reverse();
|
|
return conv;
|
|
}
|
|
|
|
char String::operator[](size_t idx) const
|
|
{
|
|
return m_data[idx];
|
|
}
|
|
|
|
String& String::operator<<(const char* body)
|
|
{
|
|
append(body);
|
|
return *this;
|
|
}
|
|
|
|
String& String::operator<<(const String& body)
|
|
{
|
|
*this += body;
|
|
return *this;
|
|
}
|
|
|
|
bool String::operator==(const String& other) const
|
|
{
|
|
return m_data == other.m_data;
|
|
}
|
|
|
|
bool String::operator!=(const String& other) const
|
|
{
|
|
return !(*this == other);
|
|
}
|
|
|
|
String& String::operator<<(size_t idx)
|
|
{
|
|
*this += to_string(idx);
|
|
return *this;
|
|
}
|
|
|
|
String& String::operator<<(const int idx)
|
|
{
|
|
*this += to_string(idx);
|
|
return *this;
|
|
}
|
|
|
|
void String::push_back(char c)
|
|
{
|
|
if (m_data.empty())
|
|
{
|
|
m_data.push_back('\0');
|
|
}
|
|
m_data.push_back('\0');
|
|
m_data[m_data.size()-2] = c;
|
|
}
|
|
|
|
void String::to_lower()
|
|
{
|
|
if (empty())
|
|
{
|
|
return;
|
|
}
|
|
for(size_t idx=0; idx<size(); idx++)
|
|
{
|
|
m_data[idx] = Char::to_lower(m_data[idx]);
|
|
}
|
|
}
|
|
|
|
String String::to_lower(const String& input)
|
|
{
|
|
String ret = input;
|
|
ret.to_lower();
|
|
return ret;
|
|
}
|
|
|
|
String& String::operator+=(const String& str)
|
|
{
|
|
if (m_data.empty())
|
|
{
|
|
m_data = str.m_data;
|
|
}
|
|
else
|
|
{
|
|
m_data.pop_back();
|
|
m_data.extend(str.m_data);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
String String::operator+(const String& str) const
|
|
{
|
|
auto ret = *this;
|
|
ret += str;
|
|
return ret;
|
|
}
|
|
|
|
String& String::operator+=(char c)
|
|
{
|
|
push_back(c);
|
|
return *this;
|
|
}
|
|
|