Compare commits
2 Commits
3940f53231
...
1af3ca19ab
Author | SHA1 | Date |
---|---|---|
Bananymous | 1af3ca19ab | |
Bananymous | 09fcc613c7 |
|
@ -1,102 +1,108 @@
|
||||||
#include <BAN/Errors.h>
|
|
||||||
#include <BAN/Math.h>
|
|
||||||
#include <BAN/Move.h>
|
|
||||||
#include <BAN/New.h>
|
|
||||||
#include <BAN/String.h>
|
#include <BAN/String.h>
|
||||||
#include <BAN/StringView.h>
|
#include <BAN/New.h>
|
||||||
|
#include <BAN/Variant.h>
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
namespace BAN
|
namespace BAN
|
||||||
{
|
{
|
||||||
|
|
||||||
String::String()
|
String::String()
|
||||||
{
|
{
|
||||||
MUST(copy_impl(""sv));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String::String(const String& other)
|
String::String(const String& other)
|
||||||
{
|
{
|
||||||
MUST(copy_impl(other.sv()));
|
*this = other;
|
||||||
}
|
}
|
||||||
|
|
||||||
String::String(String&& other)
|
String::String(String&& other)
|
||||||
{
|
{
|
||||||
move_impl(move(other));
|
*this = move(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
String::String(StringView other)
|
String::String(StringView other)
|
||||||
{
|
{
|
||||||
MUST(copy_impl(other));
|
*this = other;
|
||||||
}
|
}
|
||||||
|
|
||||||
String::~String()
|
String::~String()
|
||||||
{
|
{
|
||||||
BAN::deallocator(m_data);
|
clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
String& String::operator=(const String& other)
|
String& String::operator=(const String& other)
|
||||||
{
|
{
|
||||||
MUST(copy_impl(other.sv()));
|
clear();
|
||||||
|
if (!other.fits_in_sso())
|
||||||
|
MUST(ensure_capacity(other.size()));
|
||||||
|
memcpy(data(), other.data(), other.size() + 1);
|
||||||
|
m_size = other.size();
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
String& String::operator=(String&& other)
|
String& String::operator=(String&& other)
|
||||||
{
|
{
|
||||||
BAN::deallocator(m_data);
|
clear();
|
||||||
move_impl(move(other));
|
|
||||||
|
if (other.fits_in_sso())
|
||||||
|
memcpy(data(), other.data(), other.size() + 1);
|
||||||
|
else
|
||||||
|
m_storage = other.m_storage.get<GeneralStorage>();
|
||||||
|
m_size = other.m_size;
|
||||||
|
|
||||||
|
other.m_size = 0;
|
||||||
|
other.m_storage = SSOStorage();
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
String& String::operator=(StringView other)
|
String& String::operator=(StringView other)
|
||||||
{
|
{
|
||||||
MUST(copy_impl(other));
|
clear();
|
||||||
|
if (!fits_in_sso(other.size()))
|
||||||
|
MUST(ensure_capacity(other.size()));
|
||||||
|
memcpy(data(), other.data(), other.size());
|
||||||
|
m_size = other.size();
|
||||||
|
data()[m_size] = '\0';
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<void> String::push_back(char ch)
|
ErrorOr<void> String::push_back(char c)
|
||||||
{
|
{
|
||||||
TRY(ensure_capacity(m_size + 2));
|
TRY(ensure_capacity(m_size + 1));
|
||||||
m_data[m_size] = ch;
|
data()[m_size] = c;
|
||||||
m_size++;
|
m_size++;
|
||||||
m_data[m_size] = '\0';
|
data()[m_size] = '\0';
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<void> String::insert(char ch, size_type index)
|
ErrorOr<void> String::insert(char c, size_type index)
|
||||||
{
|
{
|
||||||
ASSERT(index <= m_size);
|
ASSERT(index <= m_size);
|
||||||
TRY(ensure_capacity(m_size + 1 + 1));
|
TRY(ensure_capacity(m_size + 1));
|
||||||
memmove(m_data + index + 1, m_data + index, m_size - index);
|
memmove(data() + index + 1, data() + index, m_size - index);
|
||||||
m_data[index] = ch;
|
data()[index] = c;
|
||||||
m_size += 1;
|
m_size++;
|
||||||
m_data[m_size] = '\0';
|
data()[m_size] = '\0';
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<void> String::insert(StringView other, size_type index)
|
ErrorOr<void> String::insert(StringView str, size_type index)
|
||||||
{
|
{
|
||||||
ASSERT(index <= m_size);
|
ASSERT(index <= m_size);
|
||||||
TRY(ensure_capacity(m_size + other.size() + 1));
|
TRY(ensure_capacity(m_size + str.size()));
|
||||||
memmove(m_data + index + other.size(), m_data + index, m_size - index);
|
memmove(data() + index + str.size(), data() + index, m_size - index);
|
||||||
memcpy(m_data + index, other.data(), other.size());
|
memcpy(data() + index, str.data(), str.size());
|
||||||
m_size += other.size();
|
m_size += str.size();
|
||||||
m_data[m_size] = '\0';
|
data()[m_size] = '\0';
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<void> String::append(StringView other)
|
ErrorOr<void> String::append(StringView str)
|
||||||
{
|
{
|
||||||
TRY(ensure_capacity(m_size + other.size() + 1));
|
TRY(ensure_capacity(m_size + str.size()));
|
||||||
memcpy(m_data + m_size, other.data(), other.size());
|
memcpy(data() + m_size, str.data(), str.size());
|
||||||
m_size += other.size();
|
m_size += str.size();
|
||||||
m_data[m_size] = '\0';
|
data()[m_size] = '\0';
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorOr<void> String::append(const String& string)
|
|
||||||
{
|
|
||||||
TRY(append(string.sv()));
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,159 +110,161 @@ namespace BAN
|
||||||
{
|
{
|
||||||
ASSERT(m_size > 0);
|
ASSERT(m_size > 0);
|
||||||
m_size--;
|
m_size--;
|
||||||
m_data[m_size] = '\0';
|
data()[m_size] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
void String::remove(size_type index)
|
void String::remove(size_type index)
|
||||||
{
|
{
|
||||||
erase(index, 1);
|
ASSERT(index < m_size);
|
||||||
}
|
memcpy(data() + index, data() + index + 1, m_size - index);
|
||||||
|
m_size--;
|
||||||
void String::erase(size_type index, size_type count)
|
data()[m_size] = '\0';
|
||||||
{
|
|
||||||
ASSERT(index + count <= m_size);
|
|
||||||
memmove(m_data + index, m_data + index + count, m_size - index - count);
|
|
||||||
m_size -= count;
|
|
||||||
m_data[m_size] = '\0';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void String::clear()
|
void String::clear()
|
||||||
{
|
{
|
||||||
|
if (!has_sso())
|
||||||
|
{
|
||||||
|
deallocator(m_storage.get<GeneralStorage>().data);
|
||||||
|
m_storage = SSOStorage();
|
||||||
|
}
|
||||||
m_size = 0;
|
m_size = 0;
|
||||||
m_data[0] = '\0';
|
data()[m_size] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
char String::operator[](size_type index) const
|
bool String::operator==(StringView str) const
|
||||||
{
|
{
|
||||||
ASSERT(index < m_size);
|
if (size() != str.size())
|
||||||
return m_data[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
char& String::operator[](size_type index)
|
|
||||||
{
|
|
||||||
ASSERT(index < m_size);
|
|
||||||
return m_data[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::operator==(const String& other) const
|
|
||||||
{
|
|
||||||
if (m_size != other.m_size)
|
|
||||||
return false;
|
return false;
|
||||||
return memcmp(m_data, other.m_data, m_size) == 0;
|
for (size_type i = 0; i < m_size; i++)
|
||||||
}
|
if (data()[i] != str.data()[i])
|
||||||
|
|
||||||
bool String::operator==(StringView other) const
|
|
||||||
{
|
|
||||||
if (m_size != other.size())
|
|
||||||
return false;
|
|
||||||
return memcmp(m_data, other.data(), m_size) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::operator==(const char* other) const
|
|
||||||
{
|
|
||||||
for (size_type i = 0; i <= m_size; i++)
|
|
||||||
if (m_data[i] != other[i])
|
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<void> String::resize(size_type size, char ch)
|
bool String::operator==(const char* cstr) const
|
||||||
{
|
{
|
||||||
if (size < m_size)
|
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)
|
||||||
{
|
{
|
||||||
m_data[size] = '\0';
|
TRY(ensure_capacity(new_size));
|
||||||
m_size = size;
|
memset(data() + m_size, init_c, new_size - m_size);
|
||||||
|
m_size = new_size;
|
||||||
|
data()[m_size] = '\0';
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
else if (size > m_size)
|
|
||||||
|
// shrink general -> sso
|
||||||
|
if (!has_sso() && fits_in_sso(new_size))
|
||||||
{
|
{
|
||||||
TRY(ensure_capacity(size + 1));
|
char* data = m_storage.get<GeneralStorage>().data;
|
||||||
for (size_type i = m_size; i < size; i++)
|
m_storage = SSOStorage();
|
||||||
m_data[i] = ch;
|
memcpy(m_storage.get<SSOStorage>().storage, data, new_size);
|
||||||
m_data[size] = '\0';
|
deallocator(data);
|
||||||
m_size = size;
|
|
||||||
}
|
}
|
||||||
m_size = size;
|
|
||||||
|
m_size = new_size;
|
||||||
|
data()[m_size] = '\0';
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<void> String::reserve(size_type size)
|
ErrorOr<void> String::reserve(size_type new_size)
|
||||||
{
|
{
|
||||||
TRY(ensure_capacity(size));
|
TRY(ensure_capacity(new_size));
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<void> String::shrink_to_fit()
|
ErrorOr<void> String::shrink_to_fit()
|
||||||
{
|
{
|
||||||
size_type temp = m_capacity;
|
if (has_sso())
|
||||||
m_capacity = 0;
|
return {};
|
||||||
auto error_or = ensure_capacity(m_size);
|
|
||||||
if (error_or.is_error())
|
if (fits_in_sso())
|
||||||
{
|
{
|
||||||
m_capacity = temp;
|
char* data = m_storage.get<GeneralStorage>().data;
|
||||||
return error_or;
|
m_storage = SSOStorage();
|
||||||
|
memcpy(m_storage.get<SSOStorage>().storage, data, m_size + 1);
|
||||||
|
deallocator(data);
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GeneralStorage& storage = m_storage.get<GeneralStorage>();
|
||||||
|
if (storage.capacity == m_size)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
char* new_data = (char*)allocator(m_size + 1);
|
||||||
|
if (new_data == nullptr)
|
||||||
|
return BAN::Error::from_errno(ENOMEM);
|
||||||
|
|
||||||
|
memcpy(new_data, storage.data, m_size);
|
||||||
|
deallocator(storage.data);
|
||||||
|
|
||||||
|
storage.capacity = m_size;
|
||||||
|
storage.data = new_data;
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
StringView String::sv() const
|
|
||||||
{
|
|
||||||
return StringView(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::empty() const
|
|
||||||
{
|
|
||||||
return m_size == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
String::size_type String::size() const
|
|
||||||
{
|
|
||||||
return m_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
String::size_type String::capacity() const
|
String::size_type String::capacity() const
|
||||||
{
|
{
|
||||||
return m_capacity;
|
if (has_sso())
|
||||||
|
return sso_capacity;
|
||||||
|
return m_storage.get<GeneralStorage>().capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* String::data()
|
||||||
|
{
|
||||||
|
if (has_sso())
|
||||||
|
return m_storage.get<SSOStorage>().storage;
|
||||||
|
return m_storage.get<GeneralStorage>().data;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* String::data() const
|
const char* String::data() const
|
||||||
{
|
{
|
||||||
return m_data;
|
if (has_sso())
|
||||||
|
return m_storage.get<SSOStorage>().storage;
|
||||||
|
return m_storage.get<GeneralStorage>().data;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<void> String::ensure_capacity(size_type size)
|
ErrorOr<void> String::ensure_capacity(size_type new_size)
|
||||||
{
|
{
|
||||||
if (m_capacity >= size)
|
if (m_size >= new_size || fits_in_sso(new_size))
|
||||||
return {};
|
return {};
|
||||||
size_type new_cap = BAN::Math::max<size_type>(size, m_capacity * 2);
|
|
||||||
void* new_data = BAN::allocator(new_cap);
|
char* new_data = (char*)allocator(new_size + 1);
|
||||||
if (new_data == nullptr)
|
if (new_data == nullptr)
|
||||||
return Error::from_errno(ENOMEM);
|
return BAN::Error::from_errno(ENOMEM);
|
||||||
if (m_data)
|
|
||||||
memcpy(new_data, m_data, m_size + 1);
|
memcpy(new_data, data(), m_size + 1);
|
||||||
BAN::deallocator(m_data);
|
|
||||||
m_data = (char*)new_data;
|
if (has_sso())
|
||||||
m_capacity = new_cap;
|
m_storage = GeneralStorage();
|
||||||
|
else
|
||||||
|
deallocator(m_storage.get<GeneralStorage>().data);
|
||||||
|
|
||||||
|
auto& storage = m_storage.get<GeneralStorage>();
|
||||||
|
storage.capacity = new_size;
|
||||||
|
storage.data = new_data;
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<void> String::copy_impl(StringView other)
|
bool String::has_sso() const
|
||||||
{
|
{
|
||||||
TRY(ensure_capacity(other.size() + 1));
|
return m_storage.has<SSOStorage>();
|
||||||
memcpy(m_data, other.data(), other.size());
|
|
||||||
m_size = other.size();
|
|
||||||
m_data[m_size] = '\0';
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
void String::move_impl(String&& other)
|
|
||||||
{
|
|
||||||
m_data = other.m_data;
|
|
||||||
m_size = other.m_size;
|
|
||||||
m_capacity = other.m_capacity;
|
|
||||||
|
|
||||||
other.m_data = nullptr;
|
|
||||||
other.m_size = 0;
|
|
||||||
other.m_capacity = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Traits.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
namespace BAN
|
namespace BAN
|
||||||
|
@ -14,5 +15,6 @@ namespace BAN
|
||||||
class StringView;
|
class StringView;
|
||||||
template<typename> class Vector;
|
template<typename> class Vector;
|
||||||
template<typename> class LinkedList;
|
template<typename> class LinkedList;
|
||||||
|
template<typename... Ts> requires (!is_const_v<Ts> && ...) class Variant;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <BAN/Errors.h>
|
#include <BAN/Errors.h>
|
||||||
#include <BAN/ForwardList.h>
|
|
||||||
#include <BAN/Formatter.h>
|
#include <BAN/Formatter.h>
|
||||||
|
#include <BAN/ForwardList.h>
|
||||||
#include <BAN/Hash.h>
|
#include <BAN/Hash.h>
|
||||||
#include <BAN/Iterators.h>
|
#include <BAN/Iterators.h>
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ namespace BAN
|
||||||
using size_type = size_t;
|
using size_type = size_t;
|
||||||
using iterator = IteratorSimple<char, String>;
|
using iterator = IteratorSimple<char, String>;
|
||||||
using const_iterator = ConstIteratorSimple<char, String>;
|
using const_iterator = ConstIteratorSimple<char, String>;
|
||||||
|
static constexpr size_type sso_capacity = 15;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
String();
|
String();
|
||||||
|
@ -34,29 +35,26 @@ namespace BAN
|
||||||
ErrorOr<void> insert(char, size_type);
|
ErrorOr<void> insert(char, size_type);
|
||||||
ErrorOr<void> insert(StringView, size_type);
|
ErrorOr<void> insert(StringView, size_type);
|
||||||
ErrorOr<void> append(StringView);
|
ErrorOr<void> append(StringView);
|
||||||
ErrorOr<void> append(const String&);
|
|
||||||
|
|
||||||
void pop_back();
|
void pop_back();
|
||||||
void remove(size_type);
|
void remove(size_type);
|
||||||
void erase(size_type, size_type);
|
|
||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
const_iterator begin() const { return const_iterator(m_data); }
|
const_iterator begin() const { return const_iterator(data()); }
|
||||||
iterator begin() { return iterator(m_data); }
|
iterator begin() { return iterator(data()); }
|
||||||
const_iterator end() const { return const_iterator(m_data + m_size); }
|
const_iterator end() const { return const_iterator(data() + size()); }
|
||||||
iterator end() { return iterator(m_data + m_size); }
|
iterator end() { return iterator(data() + size()); }
|
||||||
|
|
||||||
char front() const { ASSERT(!empty()); return m_data[0]; }
|
char front() const { ASSERT(m_size > 0); return data()[0]; }
|
||||||
char& front() { ASSERT(!empty()); return m_data[0]; }
|
char& front() { ASSERT(m_size > 0); return data()[0]; }
|
||||||
|
|
||||||
char back() const { ASSERT(!empty()); return m_data[m_size - 1]; }
|
char back() const { ASSERT(m_size > 0); return data()[m_size - 1]; }
|
||||||
char& back() { ASSERT(!empty()); return m_data[m_size - 1]; }
|
char& back() { ASSERT(m_size > 0); return data()[m_size - 1]; }
|
||||||
|
|
||||||
char operator[](size_type) const;
|
char operator[](size_type index) const { ASSERT(index < m_size); return data()[index]; }
|
||||||
char& operator[](size_type);
|
char& operator[](size_type index) { ASSERT(index < m_size); return data()[index]; }
|
||||||
|
|
||||||
bool operator==(const String&) const;
|
|
||||||
bool operator==(StringView) const;
|
bool operator==(StringView) const;
|
||||||
bool operator==(const char*) const;
|
bool operator==(const char*) const;
|
||||||
|
|
||||||
|
@ -64,24 +62,37 @@ namespace BAN
|
||||||
ErrorOr<void> reserve(size_type);
|
ErrorOr<void> reserve(size_type);
|
||||||
ErrorOr<void> shrink_to_fit();
|
ErrorOr<void> shrink_to_fit();
|
||||||
|
|
||||||
StringView sv() const;
|
StringView sv() const { return StringView(data(), size()); }
|
||||||
|
|
||||||
bool empty() const;
|
bool empty() const { return m_size == 0; }
|
||||||
size_type size() const;
|
size_type size() const { return m_size; }
|
||||||
size_type capacity() const;
|
size_type capacity() const;
|
||||||
|
|
||||||
|
char* data();
|
||||||
const char* data() const;
|
const char* data() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ErrorOr<void> ensure_capacity(size_type);
|
ErrorOr<void> ensure_capacity(size_type);
|
||||||
|
|
||||||
ErrorOr<void> copy_impl(StringView);
|
bool has_sso() const;
|
||||||
void move_impl(String&&);
|
|
||||||
|
bool fits_in_sso() const { return fits_in_sso(m_size); }
|
||||||
|
static bool fits_in_sso(size_type size) { return size < sso_capacity; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
char* m_data = nullptr;
|
struct SSOStorage
|
||||||
size_type m_capacity = 0;
|
{
|
||||||
size_type m_size = 0;
|
char storage[sso_capacity + 1] {};
|
||||||
|
};
|
||||||
|
struct GeneralStorage
|
||||||
|
{
|
||||||
|
size_type capacity { 0 };
|
||||||
|
char* data;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
Variant<SSOStorage, GeneralStorage> m_storage { SSOStorage() };
|
||||||
|
size_type m_size { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
|
|
Loading…
Reference in New Issue