This is a basic_string implementation that has SSO. It's not done, but the fundamental operations are all there (append, erase, resize, reserve, push_back/pop_back). Proper iterators and my own char_traits impl will be added eventually.
#include <string>
template<typename CharType, typename Traits = std::char_traits<CharType>, typename Allocator = std::allocator<CharType>>
class kbasic_string
{
public:
static const size_t npos = -1;
using iterator = CharType*;
using const_iterator = const CharType*;
friend kbasic_string operator+(const kbasic_string& lhs, const kbasic_string& rhs)
{
return kbasic_string{lhs}.append(rhs);
}
friend kbasic_string operator+(const kbasic_string& lhs, const CharType* rhs)
{
return kbasic_string{lhs}.append(rhs);
}
friend kbasic_string operator+(const kbasic_string& lhs, CharType rhs)
{
return kbasic_string{lhs}.append(rhs);
}
friend std::ostream& operator<<(std::ostream& lhs, const kbasic_string& rhs)
{
lhs.write(rhs.data(), rhs.size());
return lhs;
}
friend std::wostream& operator<<(std::wostream& lhs, const kbasic_string& rhs)
{
lhs.write(rhs.data(), rhs.size());
return lhs;
}
kbasic_string() = default;
kbasic_string(const kbasic_string& other)
{
reserve(other.capacity_);
if (other.size_)
std::copy(other.begin(), other.end() + 1, begin());
size_ = other.size_;
}
kbasic_string(kbasic_string&& other)
{
if (other.on_heap())
{
std::memcpy(&data_, &other.data_, sizeof(CharType*));
std::memset(&other.data_, 0, sizeof(CharType*));
}
else
{
std::copy(other.begin(), other.end() + 1, begin());
}
size_ = other.size_;
capacity_ = other.capacity_;
on_heap_ = other.on_heap_;
other.on_heap_ = false;
}
kbasic_string& operator=(const kbasic_string& other)
{
reserve(other.capacity_);
if (other.size_)
std::copy(other.begin(), other.end() + 1, begin());
size_ = other.size_;
return *this;
}
kbasic_string& operator=(kbasic_string&& other)
{
if (other.on_heap())
{
std::memcpy(&data_, &other.data_, sizeof(CharType*));
std::memset(&other.data_, 0, sizeof(CharType*));
on_heap_ = true;
}
else
{
std::copy(other.begin(), other.end() + 1, begin());
}
size_ = other.size_;
capacity_ = other.capacity_;
other.on_heap_ = false;
return *this;
}
kbasic_string(const CharType* str)
{
size_t size = Traits::length(str);
if (size > capacity_)
reserve(size);
std::copy(str, str + size + 1, data());
size_ = size;
}
kbasic_string& operator=(const CharType* str)
{
size_t size = Traits::length(str);
if (size > capacity_)
reserve(size);
std::copy(str, str + size + 1, data());
size_ = size;
}
void reserve(size_t capacity)
{
if (capacity <= capacity_ || capacity <= 22)
return;
Allocator alloc;
CharType* mem = alloc.allocate(capacity + 1);
if (size_)
std::copy(begin(), end() + 1, mem);
if (on_heap())
alloc.deallocate(data(), capacity_ + 1);
else
on_heap_ = true;
std::memcpy(data_, &mem, sizeof(CharType*));
capacity_ = capacity;
}
void resize(size_t size, CharType app)
{
if (size > size_)
{
reserve(size);
std::fill(end(), end() + (size - size_), app);
*(end() + (size - size_) + 1) = 0;
}
else if (size < size_)
{
erase(end() - (size_ - size), end());
}
else
{
return;
}
size_ = size;
}
void resize(size_t size)
{
resize(size, 0);
}
template<typename Iter, typename = std::enable_if_t<std::_Is_iterator_v<Iter>>>
kbasic_string& erase(Iter first, Iter last)
{
return erase(first - begin(), last - first);
}
kbasic_string& erase(size_t pos, size_t count)
{
if (pos > size_)
throw std::out_of_range("pos is out of range");
std::copy(begin() + pos + count, end() + 1, begin() + pos);
size_ -= count;
return *this;
}
template<typename Iter, typename = std::enable_if_t<std::_Is_iterator_v<Iter>>>
kbasic_string& erase(Iter it)
{
return erase(it - begin(), 1);
}
void pop_back()
{
erase(end() - 1);
}
void clear()
{
erase(begin(), end());
}
kbasic_string& append(const CharType* str, size_t len)
{
if (size_ + len > capacity_)
reserve(size_ + len);
std::copy(str, str + len + 1, begin() + size_);
size_ += len;
return *this;
}
kbasic_string& append(const CharType* str)
{
return append(str, Traits::length(str));
}
kbasic_string& append(const kbasic_string& str)
{
return append(str.data());
}
kbasic_string& append(CharType ch)
{
if (size_ + 1 > capacity_)
reserve(size_ + 1);
iterator prev_end = begin() + size_;
*prev_end = ch;
*(prev_end + 1) = 0;
++size_;
return *this;
}
template<typename Iter, typename = std::enable_if_t<std::_Is_iterator_v<Iter>>>
kbasic_string& append(Iter first, Iter last)
{
if (last - first > capacity_)
reserve(last - first);
std::copy(first, last, begin() + size_);
return *this;
}
void push_back(CharType ch)
{
append(ch);
}
CharType* data() noexcept
{
return on_heap() ? heap_ptr() : data_;
}
const CharType* data() const noexcept
{
return on_heap() ? heap_ptr() : data_;
}
size_t size() const noexcept
{
return size_;
}
size_t length() const noexcept
{
return size_;
}
size_t capacity() const noexcept
{
return capacity_;
}
iterator begin() noexcept
{
return data();
}
const_iterator begin() const noexcept
{
return data();
}
iterator end() noexcept
{
return data() + size_;
}
const_iterator end() const noexcept
{
return data() + size_;
}
bool empty() const noexcept
{
return !size_;
}
CharType& at(size_t n)
{
return data()[n];
}
const CharType& at(size_t n) const
{
return data()[n];
}
CharType& operator[](size_t n)
{
return at(n);
}
const CharType& operator[](size_t n) const
{
return at(n);
}
kbasic_string& operator+=(const kbasic_string& other)
{
return append(other);
}
kbasic_string& operator+=(const CharType* str)
{
return append(str);
}
kbasic_string& operator+=(CharType ch)
{
return append(ch);
}
bool operator==(const kbasic_string& other)
{
return std::equal(begin(), end(), other.begin(), other.end());
}
bool operator==(const CharType* str)
{
return std::equal(begin(), end(), str, str + Traits::length(str));
}
bool operator==(CharType ch)
{
return size_ == 1 && *begin() == ch;
}
bool operator!=(const kbasic_string& other)
{
return !(*this == other);
}
bool operator!=(const CharType* str)
{
return !(*this == str);
}
bool operator!=(CharType ch)
{
return !(*this == ch);
}
~kbasic_string()
{
if (on_heap())
Allocator{}.deallocate(data(), capacity_ + 1);
}
private:
bool on_heap() const
{
return on_heap_;
}
const CharType* heap_ptr() const
{
CharType* ptr = nullptr;
std::memcpy(&ptr, data_, sizeof(CharType*));
return ptr;
}
CharType* heap_ptr()
{
CharType* ptr = nullptr;
std::memcpy(&ptr, data_, sizeof(CharType*));
return ptr;
}
size_t size_ = 0;
size_t capacity_ = 22;
bool on_heap_ = false;
CharType data_[23] = {0};
};