/** This file is a part of rexy's general purpose library Copyright (C) 2020-2022 rexy712 This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef REXY_STRING_BASE_TPP #define REXY_STRING_BASE_TPP #include //move, etc #include //is_nothrow_invokable, is_nothrow_constructible #include //reverse_iterator #include "utility.hpp" //max, memcpy, strlen, constant_iterator #include "detail/string_appender.hpp" #include "algorithm.hpp" #include "compat/to_address.hpp" #include "string_view.hpp" #include "compat/string_base.hpp" namespace rexy{ template constexpr auto string_base::search(basic_string_view sv)const -> const_iterator{ if(sv.length() > length()){ return cend(); } return two_way_search(cbegin(), cend(), sv.cbegin(), sv.cend()); } template constexpr auto string_base::search(basic_string_view sv) -> iterator{ if(sv.length() > length()){ return end(); } return two_way_search(begin(), end(), sv.cbegin(), sv.cend()); } template constexpr auto string_base::search(const_pointer c)const -> const_iterator{ const auto len = rexy::strlen(c); return two_way_search(cbegin(), cend(), c, c + len); } template constexpr auto string_base::search(const_pointer c) -> iterator{ const auto len = rexy::strlen(c); return two_way_search(begin(), end(), c, c + len); } template template constexpr auto string_base::search(const_pointer c, const Searcher& searcher)const -> const_iterator{ const auto len = rexy::strlen(c); return searcher(cbegin(), cend(), c, c + len); } template template constexpr auto string_base::search(const_pointer c, const Searcher& searcher) -> iterator{ const auto len = rexy::strlen(c); return searcher(begin(), end(), c, c + len); } template constexpr auto string_base::rsearch(basic_string_view sv)const -> const_iterator{ if(sv.length() > length()){ return cend(); } return two_way_search(crbegin(), crend(), sv.crbegin(), sv.crend()).base(); } template constexpr auto string_base::rsearch(basic_string_view sv) -> iterator{ if(sv.length() > length()){ return end(); } return two_way_search(rbegin(), rend(), sv.crbegin(), sv.crend()).base() - sv.length(); } template constexpr auto string_base::rsearch(const_pointer c)const -> const_iterator{ const auto len = rexy::strlen(c); return two_way_search(crbegin(), crend(), std::reverse_iterator(c + len), std::reverse_iterator(c)).base() - len; } template constexpr auto string_base::rsearch(const_pointer c) -> iterator{ const auto len = rexy::strlen(c); return two_way_search(rbegin(), rend(), std::reverse_iterator(c + len), std::reverse_iterator(c)).base() - len; } template template constexpr auto string_base::rsearch(const_pointer c, const Searcher& searcher)const -> const_iterator{ const auto len = rexy::strlen(c); return searcher(crbegin(), crend(), std::reverse_iterator(c + len), std::reverse_iterator(c)).base() - len; } template template constexpr auto string_base::rsearch(const_pointer c, const Searcher& searcher) -> iterator{ const auto len = rexy::strlen(c); return searcher(rbegin(), rend(), std::reverse_iterator(c + len), std::reverse_iterator(c)).base() - len; } template constexpr bool string_base::starts_with(basic_string_view sv)const noexcept{ return basic_string_view(data(), length()).starts_with(sv); } template constexpr bool string_base::starts_with(value_type v)const noexcept{ return front() == v; } template constexpr bool string_base::starts_with(const_pointer str)const noexcept{ return starts_with(basic_string_view(str)); } template constexpr bool string_base::ends_with(basic_string_view sv)const noexcept{ return basic_string_view(data(), length()).ends_with(sv); } template constexpr bool string_base::ends_with(value_type v)const noexcept{ return back() == v; } template constexpr bool string_base::ends_with(const_pointer str)const noexcept{ return ends_with(basic_string_view(str)); } template constexpr bool string_base::contains(basic_string_view sv)const noexcept{ return basic_string_view(data(), length()).contains(sv); } template constexpr bool string_base::contains(value_type v)const noexcept{ return basic_string_view(data(), length()).contains(v); } template constexpr bool string_base::contains(const_pointer str)const noexcept{ return contains(basic_string_view(str)); } template constexpr auto string_base::find_first_of(value_type v, size_type start)const -> size_type{ return rexy::find_first_of(*this, &v, start, 1); } template constexpr auto string_base::find_first_of(const_pointer c, size_type pos)const -> size_type{ return rexy::find_first_of(*this, c, pos, rexy::strlen(c)); } template constexpr auto string_base::find_first_of(const_pointer c, size_type pos, size_type size)const -> size_type{ return rexy::find_first_of(*this, c, pos, size); } template constexpr auto string_base::find_last_of(value_type v, size_type start)const -> size_type{ return rexy::find_last_of(*this, &v, start, 1); } template constexpr auto string_base::find_last_of(const_pointer c, size_type pos)const -> size_type{ return rexy::find_last_of(*this, c, pos, rexy::strlen(c)); } template constexpr auto string_base::find_last_of(const_pointer c, size_type pos, size_type size)const -> size_type{ return rexy::find_last_of(*this, c, pos, size); } template constexpr void string_base::clear(void)noexcept{ set_length(0); get_pointer()[0] = 0; } template constexpr auto string_base::create_view(void)const noexcept -> basic_string_view{ return basic_string_view(data(), length()); } template constexpr auto string_base::create_view(const_iterator start, const_iterator fin)const noexcept -> basic_string_view{ return basic_string_view(start, fin); } //allocate string if longer than small string capacity, copy otherwise template REXY_CPP20_CONSTEXPR void basic_string::_copy_construct_string(const_pointer data, size_type len, size_type cap) noexcept(is_nothrow_allocator_v) { if constexpr(string_base::uses_sso()){ if(cap > string_base::short_string_size()){ pointer raw = this->set_long_ptr(this->allocate(sizeof(value_type)*(cap+1))); if(data){ rexy::memcpy(raw, data, sizeof(value_type)*len); } raw[len] = 0; this->set_long_length(len); this->set_long_capacity(cap); }else{ pointer raw = this->set_short_ptr(); if(data){ rexy::memcpy(raw, data, sizeof(value_type)*len); } raw[len] = 0; this->set_short_length(len); this->set_short_capacity(cap); } }else{ if(cap == 0){ return; } pointer raw = this->set_long_ptr(this->allocate(sizeof(value_type)*(cap+1))); if(data){ rexy::memcpy(raw, data, sizeof(value_type)*len); } raw[len] = 0; this->set_length(len); this->set_capacity(cap); } } template constexpr basic_string::basic_string(void)noexcept{} template constexpr basic_string::basic_string(rexy::steal data)noexcept: basic_string(data.value(), data.value() ? rexy::strlen(data.value()) : 0){} template constexpr basic_string::basic_string(rexy::steal data, size_type len)noexcept: string_base(data.value(), len, len){} template constexpr basic_string::basic_string(rexy::steal data, size_type len, size_type cap)noexcept: string_base(data.value(), len, cap){} template REXY_CPP20_CONSTEXPR basic_string::basic_string(const_pointer data, size_type len, size_type cap) noexcept(is_nothrow_allocator_v) { _copy_construct_string(data, len, cap); } template REXY_CPP20_CONSTEXPR basic_string::basic_string(const_pointer data, size_type len) noexcept(is_nothrow_allocator_v): basic_string(data, len, len){} template REXY_CPP20_CONSTEXPR basic_string::basic_string(const_pointer data) noexcept(is_nothrow_allocator_v): basic_string(data, data ? rexy::strlen(data) : 0){} template REXY_CPP20_CONSTEXPR basic_string::basic_string(size_type cap) noexcept(is_nothrow_allocator_v): basic_string(size_type(0), cap){} template REXY_CPP20_CONSTEXPR basic_string::basic_string(size_type len, size_type cap) noexcept(is_nothrow_allocator_v) { _copy_construct_string(nullptr, len, cap); } template REXY_CPP20_CONSTEXPR basic_string::basic_string(const basic_string_view& sv) noexcept(is_nothrow_allocator_v) { _copy_construct_string(sv.c_str(), sv.length(), sv.length()); } template template REXY_CPP20_CONSTEXPR basic_string::basic_string(InputIt start, InputIt fin) noexcept(is_nothrow_allocator_v): basic_string(nullptr, size_type(fin - start)) { auto raw = this->get_pointer(); size_type i = 0; for(auto it = start;it != fin;++it,++i){ raw[i] = *it; } raw[i] = 0; } //normal copy and move ctors template REXY_CPP20_CONSTEXPR basic_string::basic_string(const basic_string& b) noexcept(is_nothrow_allocator_v): detail::hasallocator(b) { _copy_construct_string(b.data(), b.length(), b.length()); } template constexpr basic_string::basic_string(basic_string&& s)noexcept: detail::hasallocator(std::move(s)), string_base(std::move(s)){} template REXY_CPP20_CONSTEXPR basic_string::basic_string(const string_base& b) noexcept(is_nothrow_allocator_v) { _copy_construct_string(b.data(), b.length(), b.length()); } //dtor template REXY_CPP20_CONSTEXPR basic_string::~basic_string(void) noexcept(is_nothrow_allocator_v) { if(this->islong()){ this->deallocate(this->get_pointer(), sizeof(value_type)*(this->capacity()+1)); } } template REXY_CPP20_CONSTEXPR basic_string& basic_string::operator=(const basic_string& s) noexcept(is_nothrow_allocator_v) { if(s.length() < this->capacity()){ rexy::memcpy(this->get_pointer(), s.get_pointer(), sizeof(value_type)*(s.length()+1)); this->set_length(s.length()); return *this; }else{ basic_string tmp(s); return (*this = std::move(tmp)); } } template constexpr basic_string& basic_string::operator=(basic_string&& s)noexcept{ string_base::operator=(std::move(s)); return *this; } template REXY_CPP20_CONSTEXPR basic_string& basic_string::operator=(const string_base& s) noexcept(is_nothrow_allocator_v) { return (*this = basic_string(s)); } //Copy from c string template REXY_CPP20_CONSTEXPR basic_string& basic_string::operator=(const basic_string_view& sv) noexcept(is_nothrow_allocator_v) { return _copy_string(sv.c_str(), sv.length()); } template REXY_CPP20_CONSTEXPR basic_string& basic_string::operator=(const_pointer c) noexcept(is_nothrow_allocator_v) { return _copy_string(c, rexy::strlen(c)); } //Replace managed pointer. Frees existing value template REXY_CPP20_CONSTEXPR void basic_string::reset(pointer val) noexcept(is_nothrow_allocator_v) { reset(val, val ? rexy::strlen(val) : 0); } template REXY_CPP20_CONSTEXPR void basic_string::reset(pointer val, size_type len) noexcept(is_nothrow_allocator_v) { if(this->islong()) this->deallocate(this->get_pointer(),sizeof(value_type)*(this->capacity()+1)); this->set_long_ptr(val); if constexpr(string_base::uses_sso()){ this->set_long_length(len); this->set_long_capacity(len); }else{ this->set_length(len); this->set_capacity(len); } } template REXY_CPP20_CONSTEXPR bool basic_string::reserve(size_type newsize)noexcept(is_nothrow_allocator_v){ if(newsize < this->capacity()) return false; if(!this->islong() && newsize < string_base::short_string_size()) return false; return (*this = basic_string(this->get_pointer(), newsize)).valid(); } template REXY_CPP20_CONSTEXPR void basic_string::shrink_to_fit(void)noexcept(is_nothrow_allocator_v){ if(this->length() == this->capacity()){ return; } *this = basic_string(this->get_pointer(), this->length(), this->length()); } template REXY_CPP20_CONSTEXPR void basic_string::resize(size_type newsize, value_type v)noexcept(is_nothrow_allocator_v){ const auto len = this->length(); const auto copy_count = std::min(newsize, len); const auto insert_count = newsize > len ? newsize - len : 0; basic_string newstr(newsize); auto* ptr = newstr.data(); rexy::memcpy(ptr, this->data(), copy_count * sizeof(value_type)); ptr += copy_count; for(size_type i = 0;i < insert_count;++i){ *ptr++ = v; } *ptr = 0; newstr.set_length(newsize); *this = std::move(newstr); } template REXY_CPP20_CONSTEXPR auto basic_string::insert(size_type pos, size_type insert_count, value_type v)noexcept(is_nothrow_allocator_v) -> basic_string&{ return _insert_impl(pos, constant_iterator{v}, insert_count); } template REXY_CPP20_CONSTEXPR auto basic_string::insert(size_type pos, value_type v)noexcept(is_nothrow_allocator_v) -> basic_string&{ return _insert_impl(pos, &v, 1); } template REXY_CPP20_CONSTEXPR auto basic_string::insert(size_type pos, const_pointer str)noexcept(is_nothrow_allocator_v) -> basic_string&{ return _insert_impl(pos, str, rexy::strlen(str)); } template REXY_CPP20_CONSTEXPR auto basic_string::insert(size_type pos, const_pointer str, size_type insert_count)noexcept(is_nothrow_allocator_v) -> basic_string&{ return _insert_impl(pos, str, insert_count); } template REXY_CPP20_CONSTEXPR auto basic_string::insert(size_type pos, const basic_string& other)noexcept(is_nothrow_allocator_v) -> basic_string&{ return _insert_impl(pos, other.begin(), other.length()); } template REXY_CPP20_CONSTEXPR auto basic_string::insert(size_type pos, const basic_string& other, size_type index_str, size_type count)noexcept(is_nothrow_allocator_v) -> basic_string&{ return _insert_impl(pos, other.begin() + index_str, count); } template REXY_CPP20_CONSTEXPR auto basic_string::insert(const_iterator pos, value_type v)noexcept(is_nothrow_allocator_v) -> basic_string&{ return _insert_impl(pos - this->begin(), &v, 1); } template REXY_CPP20_CONSTEXPR auto basic_string::insert(const_iterator pos, size_type count, value_type v)noexcept(is_nothrow_allocator_v) -> basic_string&{ return insert(pos - this->begin(), count, v); } template template REXY_CPP20_CONSTEXPR auto basic_string::insert(const_iterator pos, InputIt start, InputIt last)noexcept(is_nothrow_allocator_v) -> std::enable_if_t,basic_string&> { return insert(pos - this->begin(), std::move(start), std::move(last)); } template REXY_CPP20_CONSTEXPR auto basic_string::insert(const_iterator pos, std::initializer_list list)noexcept(is_nothrow_allocator_v) -> basic_string&{ return insert(pos, list.begin(), list.end()); } template template REXY_CPP20_CONSTEXPR auto basic_string::insert(size_type pos, const StringView& sv)noexcept(is_nothrow_allocator_v) -> std::enable_if_t> && !std::is_convertible_v,basic_string&> { return insert(pos, sv.begin(), sv.end()); } template template REXY_CPP20_CONSTEXPR auto basic_string::insert(size_type pos, const StringView& sv, size_type index_str, size_type count)noexcept(is_nothrow_allocator_v) -> std::enable_if_t> && !std::is_convertible_v,basic_string&> { return insert(pos, sv.begin() + index_str, sv.begin() + index_str + count); } template template REXY_CPP20_CONSTEXPR auto basic_string::insert(size_type pos, InputIt start, InputIt last)noexcept(is_nothrow_allocator_v) -> std::enable_if_t,basic_string&> { size_type insert_count = 0; for(auto it = start;it != last;++it, ++insert_count){} return _insert_impl(pos, start, insert_count); } template REXY_CPP20_CONSTEXPR auto basic_string::append(const_pointer data, size_type len)noexcept(is_nothrow_allocator_v) -> basic_string&{ return _insert_impl(this->length(), data, len); } template REXY_CPP20_CONSTEXPR auto basic_string::append(const_pointer data)noexcept(is_nothrow_allocator_v) -> basic_string&{ if(data){ append(data, rexy::strlen(data)); } return *this; } template REXY_CPP20_CONSTEXPR auto basic_string::append(const basic_string& other)noexcept(is_nothrow_allocator_v) -> basic_string&{ return _insert_impl(this->length(), other.data(), other.length()); } template template REXY_CPP20_CONSTEXPR auto basic_string::append(InputIt start, InputIt fin)noexcept(is_nothrow_allocator_v) -> std::enable_if_t,basic_string&> { return insert(this->length(), start, fin); } template REXY_CPP20_CONSTEXPR void basic_string::push_back(value_type data) noexcept(is_nothrow_allocator_v) { append(&data, 1); } template constexpr void basic_string::pop_back(void)noexcept{ erase(this->end() - 1); } template constexpr auto basic_string::replace(size_type pos, size_type count, const basic_string& str) -> basic_string&{ return replace(pos, count, str.create_view()); } template constexpr auto basic_string::replace(const_iterator first, const_iterator last, const basic_string& str) -> basic_string&{ return replace(first, last, str.create_view()); } template constexpr auto basic_string::replace(size_type pos, size_type count, const basic_string& str, size_type pos2, size_type count2) -> basic_string&{ return replace(pos, count, str.create_view(), pos2, count2); } template template constexpr auto basic_string::replace(const_iterator first, const_iterator last, InputIt first2, InputIt last2) -> std::enable_if_t,basic_string&> { const size_type len = last - first; size_type count = 0; for(auto it = first2;count < len && it != last2;++count,++it); return _replace_impl(size_type(first - this->begin()), len, first2, count); } template constexpr auto basic_string::replace(size_type pos, size_type count, const_pointer cstr, size_type count2) -> basic_string&{ return _replace_impl(pos, count, cstr, count2); } template constexpr auto basic_string::replace(const_iterator first, const_iterator last, const_pointer cstr, size_type count) -> basic_string&{ return _replace_impl(size_type(first - this->begin()), size_type(last - first), cstr, count); } template constexpr auto basic_string::replace(size_type pos, size_type count, const_pointer cstr) -> basic_string&{ return _replace_impl(pos, count, cstr, rexy::strlen(cstr)); } template constexpr auto basic_string::replace(const_iterator first, const_iterator last, const_pointer cstr) -> basic_string&{ return _replace_impl(size_type(first - this->begin()), size_type(last - first), cstr, rexy::strlen(cstr)); } template constexpr auto basic_string::replace(size_type pos, size_type count, size_type count2, value_type v) -> basic_string&{ return _replace_impl(pos, count, constant_iterator{v}, count2); } template constexpr auto basic_string::replace(const_iterator first, const_iterator last, size_type count2, value_type v) -> basic_string&{ return _replace_impl(size_type(first - this->begin()), size_type(last - first), constant_iterator{v}, count2); } template constexpr auto basic_string::replace(const_iterator first, const_iterator last, std::initializer_list list) -> basic_string&{ return _replace_impl(size_type(first - this->begin()), size_type(last - first), list.begin(), list.size()); } template template constexpr auto basic_string::replace(size_type pos, size_type count, const StringView& sv) -> std::enable_if_t> && !std::is_convertible_v,basic_string&> { return _replace_impl(pos, count, sv.begin(), sv.length()); } template template constexpr auto basic_string::replace(const_iterator first, const_iterator last, const StringView& sv) -> std::enable_if_t> && !std::is_convertible_v,basic_string&> { return _replace_impl(size_type(first - this->begin()), size_type(last - first), sv.begin(), sv.length()); } template template constexpr auto basic_string::replace(size_type pos, size_type count, const StringView& sv, size_type pos2, size_type count2) -> std::enable_if_t> && !std::is_convertible_v,basic_string&> { if(pos2 > sv.length()){ pos2 = sv.length(); } const auto maxlen2 = sv.length() - pos2; if(count2 > maxlen2){ count2 = maxlen2; } return _replace_impl(pos, count, sv.begin() + pos2, maxlen2); } template constexpr auto basic_string::erase(size_type index, size_type count)noexcept -> basic_string&{ const auto len = this->length(); const auto rem_count = std::min(count, len - index); const auto end_pos = index + rem_count; const auto relocate_count = len - end_pos + 1; //include terminator { auto* src = this->get_pointer() + end_pos; auto* dst = this->get_pointer() + index; for(size_type i = 0;i < relocate_count;++i){ *dst++ = *src++; } } this->set_length(len - rem_count); return *this; } template constexpr auto basic_string::erase(const_iterator pos)noexcept -> iterator{ const auto pos_index = pos - this->begin(); erase(pos - this->begin(), 1); return this->begin() + pos_index; } template constexpr auto basic_string::erase(const_iterator first, const_iterator last)noexcept -> iterator{ const auto distance = last - first; const auto start_pos = first - this->begin(); erase(start_pos, distance); return this->begin() + start_pos; } template template REXY_CPP20_CONSTEXPR auto basic_string::substring(size_type start, size_type end)const -> basic_string{ if(start > end || end > this->length()) return {}; const size_type newlen = end - start; basic_string tmp(newlen); tmp.append(this->data() + start, newlen); return tmp; } template REXY_CPP20_CONSTEXPR auto basic_string::substr(size_type start, size_type end)const -> basic_string{ return substring(start, end); } template REXY_CPP20_CONSTEXPR auto basic_string::release(void)noexcept(is_nothrow_allocator_v) -> pointer{ if(this->islong()){ pointer raw = this->get_pointer(); this->set_short_ptr(); this->set_length(0); return raw; } if constexpr(string_base::uses_sso()){ size_type len = this->length(); pointer raw = this->get_pointer(); pointer retval = this->allocate(sizeof(value_type)*len+1); rexy::memcpy(retval, raw, sizeof(value_type)*len); retval[len] = 0; raw[0] = 0; this->set_length(0); return retval; }else{ return nullptr; //not possible to reach } } template REXY_CPP20_CONSTEXPR basic_string& basic_string::_copy_string(const_pointer s, size_type len) noexcept(is_nothrow_allocator_v) { if(!s || !len) return (*this = basic_string(rexy::steal(nullptr), 0, 0)); if(len <= this->length()){ this->set_length(len); pointer raw = this->get_pointer(); rexy::memcpy(raw, s, sizeof(value_type)*len); raw[len] = 0; return *this; } return (*this = basic_string(s, len)); } template template REXY_CPP20_CONSTEXPR auto basic_string::_insert_impl(size_type pos, InputIt start, size_type insert_count)noexcept(is_nothrow_allocator_v) -> basic_string&{ const size_type len = this->length(); const size_type cap = this->capacity(); //add one for null terminator const size_type after_pos_count = (len > pos ? len - pos : 0) + 1; if(insert_count + len <= cap){ auto* dest_ptr = this->data() + len + insert_count; const auto* src_ptr = this->data() + len; for(size_type i = 0;i < after_pos_count;++i){ *dest_ptr-- = *src_ptr--; } dest_ptr = this->data() + pos; for(size_type i = 0;i < insert_count;++i){ *dest_ptr++ = *start++; } this->data()[len + insert_count] = 0; //null terminator this->set_length(len + insert_count); return *this; }else{ basic_string newstr(rexy::max(pos + insert_count, len + insert_count)); auto* ptr = newstr.get_pointer(); rexy::memcpy(ptr, this->get_pointer(), sizeof(value_type) * pos); ptr += pos; for(size_type i = 0;i < insert_count;++i){ *ptr++ = *start++; } rexy::memcpy(ptr, this->get_pointer() + pos, sizeof(value_type) * after_pos_count); newstr.set_length(len + insert_count); return (*this = std::move(newstr)); } } template template constexpr auto basic_string::_replace_impl(size_type pos, size_type count, InputIt src, size_type count2) -> basic_string&{ const auto len = this->length(); if(pos > len){ pos = len; } const auto maxlen = len - pos; if(count > maxlen){ count = maxlen; } const auto real_count = std::min(count, count2); auto* dest_ptr = this->get_pointer() + pos; for(size_type i = 0;i < real_count;++i){ *dest_ptr++ = *src++; } return *this; } template constexpr auto string_cat_expr::length(void)const noexcept -> size_type{ return this->m_l.length() + this->m_r.length(); } template template REXY_CPP20_CONSTEXPR string_cat_expr::operator basic_string::value_type,Alloc>(void) noexcept(std::is_nothrow_constructible, typename basic_string::size_type>::value && std::is_nothrow_invocable>,decltype(*this)>::value) { size_type len = length(); basic_string ret(len); detail::string_appender> append(ret); append(*this); return ret; } } //namespace rexy #endif