BAN: Make String and StringView header only

This allows linking with libc without having to link ban
This commit is contained in:
2024-05-23 15:43:26 +03:00
parent e22821799b
commit 06f4b0b29a
16 changed files with 444 additions and 554 deletions

View File

@@ -1,280 +0,0 @@
#include <BAN/String.h>
#include <BAN/New.h>
namespace BAN
{
String::String()
{
}
String::String(const String& other)
{
*this = other;
}
String::String(String&& other)
{
*this = move(other);
}
String::String(StringView other)
{
*this = other;
}
String::~String()
{
clear();
}
String& String::operator=(const String& other)
{
clear();
MUST(ensure_capacity(other.size()));
memcpy(data(), other.data(), other.size() + 1);
m_size = other.size();
return *this;
}
String& String::operator=(String&& other)
{
clear();
if (other.has_sso())
memcpy(data(), other.data(), other.size() + 1);
else
{
m_storage.general_storage = other.m_storage.general_storage;
m_has_sso = false;
}
m_size = other.m_size;
other.m_size = 0;
other.m_storage.sso_storage = SSOStorage();
other.m_has_sso = true;
return *this;
}
String& String::operator=(StringView other)
{
clear();
MUST(ensure_capacity(other.size()));
memcpy(data(), other.data(), other.size());
m_size = other.size();
data()[m_size] = '\0';
return *this;
}
ErrorOr<void> String::push_back(char c)
{
TRY(ensure_capacity(m_size + 1));
data()[m_size] = c;
m_size++;
data()[m_size] = '\0';
return {};
}
ErrorOr<void> String::insert(char c, size_type index)
{
ASSERT(index <= m_size);
TRY(ensure_capacity(m_size + 1));
memmove(data() + index + 1, data() + index, m_size - index);
data()[index] = c;
m_size++;
data()[m_size] = '\0';
return {};
}
ErrorOr<void> String::insert(StringView str, size_type index)
{
ASSERT(index <= m_size);
TRY(ensure_capacity(m_size + str.size()));
memmove(data() + index + str.size(), data() + index, m_size - index);
memcpy(data() + index, str.data(), str.size());
m_size += str.size();
data()[m_size] = '\0';
return {};
}
ErrorOr<void> String::append(StringView str)
{
TRY(ensure_capacity(m_size + str.size()));
memcpy(data() + m_size, str.data(), str.size());
m_size += str.size();
data()[m_size] = '\0';
return {};
}
void String::pop_back()
{
ASSERT(m_size > 0);
m_size--;
data()[m_size] = '\0';
}
void String::remove(size_type index)
{
ASSERT(index < m_size);
memcpy(data() + index, data() + index + 1, m_size - index);
m_size--;
data()[m_size] = '\0';
}
void String::clear()
{
if (!has_sso())
{
deallocator(m_storage.general_storage.data);
m_storage.sso_storage = SSOStorage();
m_has_sso = true;
}
m_size = 0;
data()[m_size] = '\0';
}
bool String::operator==(const String& str) const
{
if (size() != str.size())
return false;
for (size_type i = 0; i < m_size; i++)
if (data()[i] != str.data()[i])
return false;
return true;
}
bool String::operator==(StringView str) const
{
if (size() != str.size())
return false;
for (size_type i = 0; i < m_size; i++)
if (data()[i] != str.data()[i])
return false;
return true;
}
bool String::operator==(const char* cstr) const
{
for (size_type i = 0; i < m_size; i++)
if (data()[i] != cstr[i])
return false;
if (cstr[size()] != '\0')
return false;
return true;
}
ErrorOr<void> String::resize(size_type new_size, char init_c)
{
if (m_size == new_size)
return {};
// expanding
if (m_size < new_size)
{
TRY(ensure_capacity(new_size));
memset(data() + m_size, init_c, new_size - m_size);
m_size = new_size;
data()[m_size] = '\0';
return {};
}
m_size = new_size;
data()[m_size] = '\0';
return {};
}
ErrorOr<void> String::reserve(size_type new_size)
{
TRY(ensure_capacity(new_size));
return {};
}
ErrorOr<void> String::shrink_to_fit()
{
if (has_sso())
return {};
if (fits_in_sso())
{
char* data = m_storage.general_storage.data;
m_storage.sso_storage = SSOStorage();
m_has_sso = true;
memcpy(this->data(), data, m_size + 1);
deallocator(data);
return {};
}
GeneralStorage& storage = m_storage.general_storage;
if (storage.capacity == m_size)
return {};
char* new_data = (char*)allocator(m_size + 1);
if (new_data == nullptr)
return Error::from_errno(ENOMEM);
memcpy(new_data, storage.data, m_size);
deallocator(storage.data);
storage.capacity = m_size;
storage.data = new_data;
return {};
}
String::size_type String::capacity() const
{
if (has_sso())
return sso_capacity;
return m_storage.general_storage.capacity;
}
char* String::data()
{
if (has_sso())
return m_storage.sso_storage.data;
return m_storage.general_storage.data;
}
const char* String::data() const
{
if (has_sso())
return m_storage.sso_storage.data;
return m_storage.general_storage.data;
}
ErrorOr<void> String::ensure_capacity(size_type new_size)
{
if (m_size >= new_size)
return {};
if (has_sso() && fits_in_sso(new_size))
return {};
char* new_data = (char*)allocator(new_size + 1);
if (new_data == nullptr)
return Error::from_errno(ENOMEM);
if (m_size)
memcpy(new_data, data(), m_size + 1);
if (has_sso())
{
m_storage.general_storage = GeneralStorage();
m_has_sso = false;
}
else
deallocator(m_storage.general_storage.data);
auto& storage = m_storage.general_storage;
storage.capacity = new_size;
storage.data = new_data;
return {};
}
bool String::has_sso() const
{
return m_has_sso;
}
}

View File

@@ -1,196 +1,11 @@
#include <BAN/String.h>
#include <BAN/StringView.h>
#include <BAN/Vector.h>
#include <string.h>
namespace BAN
{
StringView::StringView()
{ }
StringView::StringView(const String& other)
: StringView(other.data(), other.size())
{ }
StringView::StringView(const char* string, size_type len)
{
if (len == size_type(-1))
len = strlen(string);
m_data = string;
m_size = len;
}
char StringView::operator[](size_type index) const
{
ASSERT(index < m_size);
return m_data[index];
}
bool StringView::operator==(const String& other) const
{
if (m_size != other.size())
return false;
return memcmp(m_data, other.data(), m_size) == 0;
}
bool StringView::operator==(StringView other) const
{
if (m_size != other.m_size)
return false;
return memcmp(m_data, other.m_data, m_size) == 0;
}
bool StringView::operator==(const char* other) const
{
if (memcmp(m_data, other, m_size))
return false;
return other[m_size] == '\0';
}
StringView StringView::substring(size_type index, size_type len) const
{
ASSERT(index <= m_size);
if (len == size_type(-1))
len = m_size - index;
ASSERT(len <= m_size - index); // weird order to avoid overflow
StringView result;
result.m_data = m_data + index;
result.m_size = len;
return result;
}
ErrorOr<Vector<StringView>> StringView::split(char delim, bool allow_empties) const
{
size_type count = 0;
{
size_type start = 0;
for (size_type i = 0; i < m_size; i++)
{
if (m_data[i] == delim)
{
if (allow_empties || start != i)
count++;
start = i + 1;
}
}
if (start != m_size)
count++;
}
Vector<StringView> result;
TRY(result.reserve(count));
size_type start = 0;
for (size_type i = 0; i < m_size; i++)
{
if (m_data[i] == delim)
{
if (allow_empties || start != i)
TRY(result.push_back(this->substring(start, i - start)));
start = i + 1;
}
}
if (start < m_size || (start == m_size && allow_empties))
TRY(result.push_back(this->substring(start)));
return result;
}
ErrorOr<Vector<StringView>> StringView::split(bool(*comp)(char), bool allow_empties) const
{
size_type count = 0;
{
size_type start = 0;
for (size_type i = 0; i < m_size; i++)
{
if (comp(m_data[i]))
{
if (allow_empties || start != i)
count++;
start = i + 1;
}
}
if (start != m_size)
count++;
}
Vector<StringView> result;
TRY(result.reserve(count));
size_type start = 0;
for (size_type i = 0; i < m_size; i++)
{
if (comp(m_data[i]))
{
if (allow_empties || start != i)
TRY(result.push_back(this->substring(start, i - start)));
start = i + 1;
}
}
if (start < m_size || (start == m_size && allow_empties))
TRY(result.push_back(this->substring(start)));
return result;
}
char StringView::back() const
{
ASSERT(m_size > 0);
return m_data[m_size - 1];
}
char StringView::front() const
{
ASSERT(m_size > 0);
return m_data[0];
}
BAN::Optional<StringView::size_type> StringView::find(char ch) const
{
for (size_type i = 0; i < m_size; i++)
if (m_data[i] == ch)
return i;
return {};
}
BAN::Optional<StringView::size_type> StringView::find(bool(*comp)(char)) const
{
for (size_type i = 0; i < m_size; i++)
if (comp(m_data[i]))
return i;
return {};
}
bool StringView::contains(char ch) const
{
for (size_type i = 0; i < m_size; i++)
if (m_data[i] == ch)
return true;
return false;
}
StringView::size_type StringView::count(char ch) const
{
size_type result = 0;
for (size_type i = 0; i < m_size; i++)
if (m_data[i] == ch)
result++;
return result;
}
bool StringView::empty() const
{
return m_size == 0;
}
StringView::size_type StringView::size() const
{
return m_size;
}
const char* StringView::data() const
{
return m_data;
}
}