From 24ef556ab7f6855f9ca67d298a6378d3246ee5be Mon Sep 17 00:00:00 2001 From: rexy712 Date: Wed, 22 Jun 2022 13:30:00 -0700 Subject: [PATCH] Improve basic_string type to include more functionality of std::string. Also add option to disable SSO if so desired --- CMakeLists.txt | 8 + include/rexy/algorithm.hpp | 4 +- include/rexy/algorithm.tpp | 4 +- include/rexy/compat/cpp17/string_base.hpp | 14 +- include/rexy/compat/cpp20/string_base.hpp | 13 +- include/rexy/rexy.hpp.in | 1 + include/rexy/string_base.hpp | 207 ++++++++---- include/rexy/string_base.tpp | 386 ++++++++++++++-------- include/rexy/string_view.hpp | 29 +- include/rexy/string_view.tpp | 69 +++- src/filerd.cpp | 2 +- tests/basic_string.cpp | 154 ++++++--- 12 files changed, 617 insertions(+), 274 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 43b0d96..6a48916 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,7 @@ include_directories(BEFORE SYSTEM "${INCLUDE_PATH}") cmake_dependent_option(ENABLE_SHARED "Build shared library" ON "NOT BUILD_HEADER_ONLY" OFF) +cmake_dependent_option(ENABLE_SSO "Use small string optimization" ON "NOT BUILD_HEADER_ONLY" ON) option(ENABLE_PROFILING "Enable asan" OFF) option(BUILD_TESTS "Enable testing" OFF) option(BUILD_HEADER_ONLY "Enable header only build" OFF) @@ -42,6 +43,13 @@ else() endif() set_target_properties(rexy PROPERTIES VERSION "${librexy_VERSION_MAJOR}.${librexy_VERSION_MINOR}.${librexy_VERSION_REVISION}") + if(ENABLE_SSO) + set(librexy_ENABLE_SSO 1) + target_compile_options(rexy PRIVATE -DREXY_ENABLE_SSO=1) + else() + set(librexy_ENABLE_SSO 0) + target_compile_options(rexy PRIVATE -DREXY_ENABLE_SSO=0) + endif() if(ENABLE_PROFILING) target_compile_options(rexy PRIVATE -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls) target_link_options(rexy PRIVATE -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls) diff --git a/include/rexy/algorithm.hpp b/include/rexy/algorithm.hpp index afa32e0..1dc72bd 100644 --- a/include/rexy/algorithm.hpp +++ b/include/rexy/algorithm.hpp @@ -33,12 +33,12 @@ namespace rexy{ //Requires Iterators to be LegacyRandomAccessIterators template - constexpr HIter two_way_search(const HIter& hstart, const HIter& hend, const NIter& nstart, const NIter& nend); + constexpr HIter two_way_search(HIter hstart, HIter hend, NIter nstart, NIter nend); //searcher for use with generic search wrappers struct two_way_searcher{ template - constexpr HIter operator()(const HIter& hstart, const HIter& hend, const NIter& nstart, const NIter& nend)const; + constexpr HIter operator()(HIter hstart, HIter hend, NIter nstart, NIter nend)const; }; } diff --git a/include/rexy/algorithm.tpp b/include/rexy/algorithm.tpp index 9def2af..555b1db 100644 --- a/include/rexy/algorithm.tpp +++ b/include/rexy/algorithm.tpp @@ -40,7 +40,7 @@ namespace rexy{ //Requires Iterators to be LegacyRandomAccessIterators template - constexpr HIter two_way_search(const HIter& hstart, const HIter& hend, const NIter& nstart, const NIter& nend){ + constexpr HIter two_way_search(HIter hstart, HIter hend, NIter nstart, NIter nend){ size_t j = 0; size_t i = 0; size_t nlen = nend - nstart; @@ -99,7 +99,7 @@ namespace rexy{ } template - constexpr HIter two_way_searcher::operator()(const HIter& hstart, const HIter& hend, const NIter& nstart, const NIter& nend)const{ + constexpr HIter two_way_searcher::operator()(HIter hstart, HIter hend, NIter nstart, NIter nend)const{ return two_way_search(hstart, hend, nstart, nend); } diff --git a/include/rexy/compat/cpp17/string_base.hpp b/include/rexy/compat/cpp17/string_base.hpp index 80adf38..3db23cd 100644 --- a/include/rexy/compat/cpp17/string_base.hpp +++ b/include/rexy/compat/cpp17/string_base.hpp @@ -24,6 +24,8 @@ #include //forward #include //{false,true}_type, declval, enable_if, remove_reference, decay +#include "../../utility.hpp" //strlen, strncmp + namespace rexy{ #define REXY_HAS_MEMFUN_WITH_RET(type, ret, fun, ...) \ @@ -92,31 +94,29 @@ namespace rexy{ if(left.length() != right.length()){ return false; } - return !detail::string_compare(std::forward(left), std::forward(right), left.length()); + return !rexy::strncmp(left.c_str(), right.c_str(), left.length()+1); } template::value,int> = 0> constexpr bool operator==(Str1&& left, typename std::decay_t::const_pointer right){ if(right == nullptr){ return false; } - const auto rlen = detail::string_len(right); + const auto rlen = rexy::strlen(right); if(rlen != left.length()){ return false; } - const auto minlen = min(left.length(), rlen); - return !detail::string_compare(left.c_str(), right, minlen+1); + return !rexy::strncmp(left.c_str(), right, rlen+1); } template::value,int> = 0> constexpr bool operator==(typename std::decay_t::const_pointer left, Str1&& right){ if(left == nullptr){ return false; } - const auto llen = detail::string_len(left); + const auto llen = rexy::strlen(left); if(llen != right.length()){ return false; } - const auto minlen = min(right.length(), llen); - return !detail::string_compare(right.c_str(), left, minlen+1); + return !rexy::strncmp(left, right.c_str(), llen+1); } template::value,int> = 0> constexpr bool operator!=(Str1&& left, Str2&& right)noexcept{ diff --git a/include/rexy/compat/cpp20/string_base.hpp b/include/rexy/compat/cpp20/string_base.hpp index 03c2faa..ce95b08 100644 --- a/include/rexy/compat/cpp20/string_base.hpp +++ b/include/rexy/compat/cpp20/string_base.hpp @@ -25,6 +25,7 @@ #include //decay, is_nothrow_assignable #include "../../concepts/string.hpp" +#include "../../utility.hpp" //strlen, strncmp namespace rexy{ @@ -34,31 +35,29 @@ namespace rexy{ if(left.length() != right.length()){ return false; } - return !detail::string_compare(std::forward(left), std::forward(right), left.length()); + return !rexy::strncmp(left.c_str(), right.c_str(), left.length()+1); } template constexpr bool operator==(Str1&& left, typename std::decay_t::const_pointer right){ if(right == nullptr){ return false; } - const auto rlen = detail::string_len(right); + const auto rlen = rexy::strlen(right); if(rlen != left.length()){ return false; } - const auto minlen = min(left.length(), rlen); - return !detail::string_compare(left.c_str(), right, minlen+1); + return !rexy::strncmp(left.c_str(), right, rlen+1); } template constexpr bool operator==(typename std::decay_t::const_pointer left, Str1&& right){ if(left == nullptr){ return false; } - const auto llen = detail::string_len(left); + const auto llen = rexy::strlen(left); if(llen != right.length()){ return false; } - const auto minlen = min(right.length(), llen); - return !detail::string_compare(right.c_str(), left, minlen+1); + return !rexy::strncmp(left, right.c_str(), llen+1); } template constexpr bool operator!=(Str1&& left, Str2&& right){ diff --git a/include/rexy/rexy.hpp.in b/include/rexy/rexy.hpp.in index 4ee2b85..1b55172 100644 --- a/include/rexy/rexy.hpp.in +++ b/include/rexy/rexy.hpp.in @@ -30,5 +30,6 @@ #define LIBREXY_VERSION_MINOR @librexy_VERSION_MINOR@ #define LIBREXY_VERSION_REVISION @librexy_VERSION_REVISION@ +#define LIBREXY_ENABLE_SSO @librexy_ENABLE_SSO@ #endif diff --git a/include/rexy/string_base.hpp b/include/rexy/string_base.hpp index e0fd748..1ec5924 100644 --- a/include/rexy/string_base.hpp +++ b/include/rexy/string_base.hpp @@ -41,6 +41,10 @@ namespace rexy{ +#ifndef LIBREXY_ENABLE_SSO + #define LIBREXY_ENABLE_SSO 1 +#endif + template class basic_string_view; @@ -61,6 +65,7 @@ namespace rexy{ using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; +#if LIBREXY_ENABLE_SSO != 0 private: static constexpr size_type EXTRA_SDATA_LEN = 0; @@ -132,6 +137,12 @@ namespace rexy{ constexpr void set_short_capacity(size_type){} constexpr size_type get_long_capacity(void)const{return m_data.l.capacity;} constexpr size_type get_short_capacity(void)const{return MAX_SHORT_LEN;} + constexpr void set_capacity(size_type s){ + if(islong()) + set_long_capacity(s); + else + set_short_capacity(s); + } constexpr void set_length(size_type s){ if(islong()) set_long_length(s); @@ -180,52 +191,134 @@ namespace rexy{ } return *this; } - public: - //Length of string not including null terminator constexpr size_type length(void)const noexcept{ if(islong()) return get_long_length(); else return get_short_length(); } - constexpr size_type size(void)const noexcept{ - return length(); - } constexpr size_type capacity(void)const noexcept{ if(islong()) return get_long_capacity(); else return get_short_capacity(); } + static constexpr bool uses_sso(void){return true;} + static constexpr size_type short_string_size(void){return MAX_SHORT_LEN;} +#else //LIBREXY_ENABLE_SSO + private: + //direct access to current string + pointer m_raw = nullptr; + size_type m_length = 0; + size_type m_capacity = 0; + + protected: + constexpr pointer get_pointer(void){return m_raw;} + constexpr const_pointer get_pointer(void)const{return m_raw;} + constexpr void set_length(size_type s){m_length = s;} + constexpr void set_capacity(size_type s){m_capacity = s;} + constexpr bool islong(void)const{return true;} + constexpr pointer set_short_ptr(void){return m_raw;} + constexpr pointer set_long_ptr(pointer ptr){return m_raw = ptr;} + + protected: + constexpr string_base(void)noexcept = default; + //Initialize without copying + constexpr string_base(pointer data, size_type len, size_type cap)noexcept: + m_raw(data), + m_length(len), + m_capacity(cap){} + constexpr string_base(pointer data, size_type len)noexcept: + string_base(data, len, len){} + //Copy ctor, copy length+capacity+short string, not long string value + constexpr string_base(const string_base& s)noexcept: + m_raw(s.m_raw), + m_length(s.m_length), + m_capacity(s.m_capacity){} + constexpr string_base(string_base&& s)noexcept: + m_raw(std::exchange(s.m_raw, nullptr)), + m_length(s.m_length), + m_capacity(s.m_capacity){} + REXY_CPP20_CONSTEXPR ~string_base(void)noexcept = default; + constexpr string_base& operator=(string_base&& s)noexcept{ + std::swap(m_raw, s.m_raw); + m_length = s.m_length; + m_capacity = s.m_capacity; + return *this; + } + public: + constexpr size_type length(void)const noexcept{ + return m_length; + } + constexpr size_type capacity(void)const noexcept{ + return m_capacity; + } + static constexpr bool uses_sso(void){return false;} + static constexpr size_type short_string_size(void){return 0;} +#endif //LIBREXY_ENABLE_SSO + public: + //Length of string not including null terminator + constexpr size_type size(void)const noexcept{ + return length(); + } + constexpr size_type max_size(void)const noexcept{ + return size_type{-2}; + } //direct access to managed pointer constexpr pointer c_str(void)noexcept{return get_pointer();} constexpr const_pointer c_str(void)const noexcept{return get_pointer();} - constexpr pointer get(void)noexcept{return get_pointer();} - constexpr const_pointer get(void)const noexcept{return get_pointer();} - constexpr operator pointer(void)noexcept{return get_pointer();} - constexpr operator const_pointer(void)const noexcept{return get_pointer();} + constexpr pointer data(void)noexcept{return get_pointer();} + constexpr const_pointer data(void)const noexcept{return get_pointer();} + constexpr operator basic_string_view(void)const noexcept{return basic_string_view(begin(), end());} + //true if m_data is not empty constexpr bool valid(void)const noexcept{return length() > 0;} + constexpr bool empty(void)const noexcept{return length() == 0;} constexpr reference operator[](size_type i)noexcept{return get_pointer()[i];} constexpr const_reference operator[](size_type i)const noexcept{return get_pointer()[i];} + constexpr reference at(size_type i)noexcept{return get_pointer()[i];} + constexpr const_reference at(size_type i)const noexcept{return get_pointer()[i];} - constexpr const_iterator search(const string_base& s)const; + constexpr const_iterator search(basic_string_view sv)const; + constexpr iterator search(basic_string_view sv); constexpr const_iterator search(const_pointer c)const; - constexpr iterator search(const string_base& s); constexpr iterator search(const_pointer c); template - constexpr const_iterator search(const string_base& s, const Searcher& searcher)const; - template constexpr const_iterator search(const_pointer c, const Searcher& searcher)const; template - constexpr iterator search(const string_base& s, const Searcher& searcher); - template constexpr iterator search(const_pointer c, const Searcher& searcher); + + constexpr bool starts_with(basic_string_view sv)const noexcept; + constexpr bool starts_with(value_type v)const noexcept; + constexpr bool starts_with(const_pointer str)const noexcept; + + constexpr bool ends_with(basic_string_view sv)const noexcept; + constexpr bool ends_with(value_type v)const noexcept; + constexpr bool ends_with(const_pointer str)const noexcept; + + constexpr bool contains(basic_string_view sv)const noexcept; + constexpr bool contains(value_type v)const noexcept; + constexpr bool contains(const_pointer str)const noexcept; + + //TODO more compares constexpr bool compare(const string_base& s)const{return *this == s;} + constexpr bool compare(basic_string_view s)const{return *this == s;} constexpr bool compare(const_pointer c)const{return *this == c;} + constexpr size_type find_first_of(value_type v, size_type start = 0)const; + constexpr size_type find_first_of(const_pointer c, size_type pos = 0)const; + constexpr size_type find_first_of(const_pointer c, size_type pos, size_type size)const; + constexpr size_type find_last_of(value_type v, size_type start = 0)const; + constexpr size_type find_last_of(const_pointer c, size_type pos = 0)const; + constexpr size_type find_last_of(const_pointer c, size_type pos, size_type size)const; + + constexpr reference front(void)noexcept{return *get_pointer();} + constexpr const_reference front(void)const noexcept{return *get_pointer();} + constexpr reference back(void)noexcept{return *(get_pointer() + length() - 1);} + constexpr const_reference back(void)const noexcept{return *(get_pointer() + length() - 1);} + constexpr iterator begin(void){return get_pointer();} constexpr const_iterator begin(void)const{return get_pointer();} constexpr iterator end(void){return get_pointer()+length();} @@ -240,8 +333,22 @@ namespace rexy{ constexpr const_reverse_iterator crbegin(void)const{return rbegin();} constexpr const_reverse_iterator crend(void)const{return rend();} - static constexpr bool uses_sso(void){return true;} - static constexpr size_type short_string_size(void){return MAX_SHORT_LEN;} + constexpr void clear(void)noexcept; + + constexpr basic_string_view create_view(void)const noexcept; + constexpr basic_string_view create_view(const_iterator start, const_iterator fin)const noexcept; + + + [[deprecated]] constexpr pointer get(void)noexcept{return get_pointer();} + [[deprecated]] constexpr const_pointer get(void)const noexcept{return get_pointer();} + [[deprecated]] constexpr operator pointer(void)noexcept{return get_pointer();} + [[deprecated]] constexpr operator const_pointer(void)const noexcept{return get_pointer();} + [[deprecated]] constexpr const_iterator search(const string_base& s)const; + [[deprecated]] constexpr iterator search(const string_base& s); + template + [[deprecated]] constexpr const_iterator search(const string_base& s, const Searcher& searcher)const; + template + [[deprecated]] constexpr iterator search(const string_base& s, const Searcher& searcher); }; @@ -270,6 +377,8 @@ namespace rexy{ noexcept(is_nothrow_allocator_v); REXY_CPP20_CONSTEXPR basic_string& _copy_string(const_pointer s, size_type len) noexcept(is_nothrow_allocator_v); + template + REXY_CPP20_CONSTEXPR basic_string& _insert_impl(size_type pos, InputIt start, size_type insert_count)noexcept(is_nothrow_allocator_v); public: constexpr basic_string(void)noexcept; @@ -308,9 +417,9 @@ namespace rexy{ //Replace managed pointer. Frees existing value REXY_CPP20_CONSTEXPR void reset(pointer val = nullptr)noexcept(is_nothrow_allocator_v); REXY_CPP20_CONSTEXPR void reset(pointer val, size_type len)noexcept(is_nothrow_allocator_v); - REXY_CPP20_CONSTEXPR bool resize(size_type newsize)noexcept(is_nothrow_allocator_v); + REXY_CPP20_CONSTEXPR bool reserve(size_type newsize)noexcept(is_nothrow_allocator_v); + REXY_CPP20_CONSTEXPR void shrink_to_fit(void)noexcept(is_nothrow_allocator_v); - //TODO more insert REXY_CPP20_CONSTEXPR basic_string& insert(size_type pos, size_type insert_count, value_type v)noexcept(is_nothrow_allocator_v); REXY_CPP20_CONSTEXPR basic_string& insert(size_type pos, value_type v)noexcept(is_nothrow_allocator_v); REXY_CPP20_CONSTEXPR basic_string& insert(size_type pos, const_pointer str)noexcept(is_nothrow_allocator_v); @@ -319,10 +428,10 @@ namespace rexy{ REXY_CPP20_CONSTEXPR basic_string& insert(size_type pos, const basic_string& other, size_type index_str, size_type count = npos)noexcept(is_nothrow_allocator_v); REXY_CPP20_CONSTEXPR basic_string& insert(const_iterator pos, value_type v)noexcept(is_nothrow_allocator_v); REXY_CPP20_CONSTEXPR basic_string& insert(const_iterator pos, size_type count, value_type v)noexcept(is_nothrow_allocator_v); - template - REXY_CPP20_CONSTEXPR basic_string& insert(const_iterator pos, InIt start, InIt last)noexcept(is_nothrow_allocator_v); - template - REXY_CPP20_CONSTEXPR basic_string& insert(size_type pos, InIt start, InIt last)noexcept(is_nothrow_allocator_v); + template + REXY_CPP20_CONSTEXPR basic_string& insert(const_iterator pos, InputIt start, InputIt last)noexcept(is_nothrow_allocator_v); + template + REXY_CPP20_CONSTEXPR basic_string& insert(size_type pos, InputIt start, InputIt last)noexcept(is_nothrow_allocator_v); REXY_CPP20_CONSTEXPR basic_string& insert(const_iterator pos, std::initializer_list list)noexcept(is_nothrow_allocator_v); template REXY_CPP20_CONSTEXPR auto insert(size_type pos, const StringView& sv)noexcept(is_nothrow_allocator_v) -> @@ -330,16 +439,20 @@ namespace rexy{ template REXY_CPP20_CONSTEXPR auto insert(size_type pos, const StringView& sv, size_type index_str, size_type count = npos)noexcept(is_nothrow_allocator_v) -> std::enable_if_t> && !std::is_convertible_v>; - - REXY_CPP20_CONSTEXPR void push_back(value_type data) - noexcept(is_nothrow_allocator_v); - REXY_CPP20_CONSTEXPR void append(const_pointer data, size_type len) - noexcept(is_nothrow_allocator_v); - REXY_CPP20_CONSTEXPR void append(const_pointer data) - noexcept(is_nothrow_allocator_v); template - REXY_CPP20_CONSTEXPR void append(InputIt start, InputIt fin) - noexcept(is_nothrow_allocator_v); + REXY_CPP20_CONSTEXPR basic_string& append(InputIt start, InputIt fin)noexcept(is_nothrow_allocator_v); + + + REXY_CPP20_CONSTEXPR basic_string& append(const_pointer data, size_type len)noexcept(is_nothrow_allocator_v); + REXY_CPP20_CONSTEXPR basic_string& append(const_pointer data)noexcept(is_nothrow_allocator_v); + REXY_CPP20_CONSTEXPR basic_string& append(const basic_string& other)noexcept(is_nothrow_allocator_v); + + REXY_CPP20_CONSTEXPR void push_back(value_type data)noexcept(is_nothrow_allocator_v); + constexpr void pop_back(void)noexcept; + + constexpr basic_string& erase(size_type index = 0, size_type count = npos)noexcept; + constexpr iterator erase(const_iterator pos)noexcept; + constexpr iterator erase(const_iterator first, const_iterator last)noexcept; template REXY_CPP20_CONSTEXPR basic_string substring(size_type start, size_type end)const; @@ -348,8 +461,8 @@ namespace rexy{ REXY_CPP20_CONSTEXPR pointer release(void)noexcept(is_nothrow_allocator_v); using detail::hasallocator::allocator; - constexpr basic_string_view create_view(void)const noexcept; - constexpr basic_string_view create_view(const_iterator start, const_iterator fin)const noexcept; + + [[deprecated]] REXY_CPP20_CONSTEXPR bool resize(size_type newsize)noexcept(is_nothrow_allocator_v); }; //Like an expression template but not really @@ -386,32 +499,6 @@ namespace rexy{ template string_cat_expr(Left&&,Right&&) -> string_cat_expr; - - namespace detail{ - template - constexpr int string_compare(Left&& left, Right&& right, size_t maxlen){ - for(size_t i = 0;i < maxlen;++i){ - const auto diff = left[i] - right[i]; - if(diff != 0){ - return diff; - } - if(left[i] == 0 || right[i] == 0){ - return diff; - } - } - return 0; - } - template - constexpr size_t string_len(const Str* str){ - if(!str){ - return 0; - } - size_t i; - for(i = 0;str[i] != 0;++i); - return i; - } - } - } #include "string_base.tpp" diff --git a/include/rexy/string_base.tpp b/include/rexy/string_base.tpp index b86237a..fed4426 100644 --- a/include/rexy/string_base.tpp +++ b/include/rexy/string_base.tpp @@ -33,8 +33,18 @@ namespace rexy{ template - constexpr auto string_base::search(const string_base& s)const -> const_iterator{ - return two_way_search(cbegin(), cend(), s.begin(), s.end()); + 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{ @@ -42,59 +52,137 @@ namespace rexy{ return two_way_search(cbegin(), cend(), c, c + len); } template - constexpr auto string_base::search(const string_base& s) -> iterator{ - return two_way_search(begin(), end(), s.begin(), s.end()); - } - 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 string_base& s, const Searcher& searcher)const -> const_iterator{ - return searcher(cbegin(), cend(), s.begin(), s.end()); - } - 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 string_base& s, const Searcher& searcher) -> iterator{ - return searcher(begin(), end(), s.begin(), s.end()); - } - 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 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(cap > this->get_short_capacity()){ - this->set_islong_flag(true); - 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); + 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{ - this->set_islong_flag(false); - pointer raw = this->set_short_ptr(); - if(data) + 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_short_length(len); - this->set_short_capacity(cap); + this->set_length(len); + this->set_capacity(cap); } } @@ -178,7 +266,7 @@ namespace rexy{ noexcept(is_nothrow_allocator_v) { if(this->islong()){ - this->deallocate(this->get_pointer(), sizeof(value_type)*(this->get_long_capacity()+1)); + this->deallocate(this->get_pointer(), sizeof(value_type)*(this->capacity()+1)); } } @@ -233,32 +321,42 @@ namespace rexy{ noexcept(is_nothrow_allocator_v) { if(this->islong()) - this->deallocate(this->get_long_ptr(),sizeof(value_type)*(this->get_long_capacity()+1)); - this->set_islong_flag(true); + this->deallocate(this->get_pointer(),sizeof(value_type)*(this->capacity()+1)); this->set_long_ptr(val); - this->set_long_length(len); - this->set_long_capacity(len); + 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::resize(size_type newsize) - noexcept(is_nothrow_allocator_v) - { + 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 < this->get_short_capacity()) + if(!this->islong() && newsize < string_base::short_string_size()) return false; return (*this = basic_string(this->get_pointer(), newsize)); } + 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 auto basic_string::insert(size_type pos, size_type insert_count, value_type v)noexcept(is_nothrow_allocator_v) -> basic_string&{ struct insert_adapter{ - size_type max; value_type val; constexpr insert_adapter& operator++(void)noexcept{ - --max; + return *this; + } + constexpr insert_adapter operator++(int)noexcept{ return *this; } constexpr value_type operator*(void)const noexcept{ @@ -269,41 +367,40 @@ namespace rexy{ constexpr bool operator!=(const insert_adapter& other)const = default; }; - return insert(pos, insert_adapter{insert_count, v}, insert_adapter{0, v}); + return _insert_impl(pos, insert_adapter{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(pos, size_type{1}, v); + 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&{ - const size_type slen = rexy::strlen(str); - return insert(pos, str, str + slen); + 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(pos, str, str + insert_count); + 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(pos, other.begin(), other.end()); + 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(pos, other.begin() + index_str, other.begin() + index_str + count); + 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(pos, size_type{1}, v); + 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, InIt start, InIt last)noexcept(is_nothrow_allocator_v) -> basic_string&{ + template + REXY_CPP20_CONSTEXPR auto basic_string::insert(const_iterator pos, InputIt start, InputIt last)noexcept(is_nothrow_allocator_v) -> basic_string&{ return insert(pos - this->begin(), std::move(start), std::move(last)); } template @@ -326,29 +423,32 @@ namespace rexy{ } template - template - REXY_CPP20_CONSTEXPR auto basic_string::insert(size_type pos, InIt start, InIt last)noexcept(is_nothrow_allocator_v) -> basic_string&{ - const size_type len = this->length(); + template + REXY_CPP20_CONSTEXPR auto basic_string::insert(size_type pos, InputIt start, InputIt last)noexcept(is_nothrow_allocator_v) -> basic_string&{ size_type insert_count = 0; for(auto it = start;it != last;++it, ++insert_count){} - resize(rexy::max(pos + insert_count, len + insert_count)); - - const size_type move_count = rexy::min(0, len - pos); - const auto ptr = this->get_pointer(); - - for(size_type i = move_count+1;i > 0;--i){ - const size_type index = i + pos; - ptr[index] = ptr[index-1]; + 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)); } - { - size_type i = 0; - for(auto it = start;it != last;++it,++i){ - ptr[pos+i] = *it; - } - } - this->set_length(len + insert_count); 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) -> basic_string&{ + return insert(this->length(), start, fin); + } template REXY_CPP20_CONSTEXPR void basic_string::push_back(value_type data) @@ -356,57 +456,41 @@ namespace rexy{ { append(&data, 1); } + template + constexpr void basic_string::pop_back(void)noexcept{ + erase(this->end() - 1); + } + template - REXY_CPP20_CONSTEXPR void basic_string::append(const_pointer data, size_type len) - noexcept(is_nothrow_allocator_v) - { - size_type mylen = this->length(); - size_type mycap = this->capacity(); - pointer raw = this->get_pointer(); + 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 - if(mylen+len <= mycap){ - rexy::memcpy(raw+mylen, data, sizeof(value_type)*len); - this->set_length(mylen+len); - raw[mylen+len] = 0; - }else{ - auto newsize = max(mylen+len, mycap*2); - basic_string tmp(newsize); - tmp.append(raw, mylen); - tmp.append(data, len); - *this = std::move(tmp); + { + 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 - REXY_CPP20_CONSTEXPR void basic_string::append(const_pointer data) - noexcept(is_nothrow_allocator_v) - { - if(data) - append(data, rexy::strlen(data)); + 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 - template - REXY_CPP20_CONSTEXPR void basic_string::append(InputIt start, InputIt fin) - noexcept(is_nothrow_allocator_v) - { - size_type append_len = fin - start; - - size_type my_len = this->length(); - size_type my_cap = this->capacity(); - pointer raw = this->get_pointer(); - - if(my_len + append_len > my_cap){ - *this = basic_string(raw, my_len, max(my_len+append_len, my_cap*2)); - raw = this->get_pointer(); - my_cap *= 2; - } - - size_type i = 0; - for(auto it = start;it != fin;++it,++i){ - raw[my_len + i] = *it; - } - this->set_length(my_len + append_len); - raw[my_len + i] = 0; + 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; } @@ -428,29 +512,23 @@ namespace rexy{ template REXY_CPP20_CONSTEXPR auto basic_string::release(void)noexcept(is_nothrow_allocator_v) -> pointer{ if(this->islong()){ - pointer raw = this->get_long_ptr(); - this->set_islong_flag(false); + pointer raw = this->get_pointer(); this->set_short_ptr(); - this->set_short_length(0); + this->set_length(0); return raw; } - size_type len = this->get_short_length(); - pointer raw = this->get_short_ptr(); - 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_short_length(0); - return retval; - } - template - constexpr auto basic_string::create_view(void)const noexcept -> basic_string_view{ - const auto ptr = this->get_pointer(); - return basic_string_view(ptr, ptr + this->length()); - } - template - constexpr auto basic_string::create_view(const_iterator start, const_iterator fin)const noexcept -> basic_string_view{ - return basic_string_view(start, fin); + 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 @@ -468,7 +546,29 @@ namespace rexy{ } 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 after_pos_count = len > pos ? len - pos : 0; + 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); + ptr += after_pos_count; + *ptr = 0; //null terminator + newstr.set_length(len + insert_count); + + return (*this = std::move(newstr)); + } template constexpr auto string_cat_expr::length(void)const noexcept -> size_type{ @@ -488,6 +588,30 @@ namespace rexy{ } + template + [[deprecated]] constexpr auto string_base::search(const string_base& s)const -> const_iterator{ + return two_way_search(cbegin(), cend(), s.begin(), s.end()); + } + template + [[deprecated]] constexpr auto string_base::search(const string_base& s) -> iterator{ + return two_way_search(begin(), end(), s.begin(), s.end()); + } + template + template + [[deprecated]] constexpr auto string_base::search(const string_base& s, const Searcher& searcher)const -> const_iterator{ + return searcher(cbegin(), cend(), s.begin(), s.end()); + } + template + template + [[deprecated]] constexpr auto string_base::search(const string_base& s, const Searcher& searcher) -> iterator{ + return searcher(begin(), end(), s.begin(), s.end()); + } + template + [[deprecated]] REXY_CPP20_CONSTEXPR bool basic_string::resize(size_type newsize)noexcept(is_nothrow_allocator_v){ + return reserve(newsize); + } + + } //namespace rexy #endif diff --git a/include/rexy/string_view.hpp b/include/rexy/string_view.hpp index acb957b..1877ff0 100644 --- a/include/rexy/string_view.hpp +++ b/include/rexy/string_view.hpp @@ -70,27 +70,38 @@ namespace rexy{ //Length of string not including null terminator constexpr size_type length(void)const noexcept{return m_length;} constexpr size_type size(void)const noexcept{return m_length;} - constexpr bool empty(void)const noexcept{return m_length == 0;} //direct access to managed pointer constexpr const_pointer c_str(void)const noexcept{return m_data;} constexpr const_pointer data(void)const noexcept{return m_data;} - constexpr const_pointer get(void)const noexcept{return m_data;} - constexpr operator const_pointer(void)const noexcept{return m_data;} //true if m_data is not empty constexpr bool valid(void)const noexcept{return m_length > 0;} + constexpr bool empty(void)const noexcept{return m_length == 0;} constexpr const_reference operator[](size_type i)const noexcept{return m_data[i];} constexpr const_reference at(size_type i)const noexcept{return m_data[i];} - constexpr const_reference front(size_type i)const noexcept{return m_data[0];} - constexpr const_reference back(size_type i)const noexcept{return m_data[m_length-1];} + constexpr const_reference front(void)const noexcept{return m_data[0];} + constexpr const_reference back(void)const noexcept{return m_data[m_length-1];} constexpr const_iterator it_at(size_type i)const noexcept{return m_data + i;} - constexpr const_iterator search(const basic_string_view& s)const; + constexpr const_iterator search(basic_string_view s)const; constexpr const_iterator search(const_pointer c)const; template - constexpr const_iterator search(const basic_string_view& s, const Searcher& searcher)const; + constexpr const_iterator search(basic_string_view s, const Searcher& searcher)const; template constexpr const_iterator search(const_pointer c, const Searcher& searcher)const; + + constexpr bool starts_with(basic_string_view sv)const; + constexpr bool starts_with(value_type v)const; + constexpr bool starts_with(const_pointer str)const; + + constexpr bool ends_with(basic_string_view sv)const; + constexpr bool ends_with(value_type v)const; + constexpr bool ends_with(const_pointer str)const; + + constexpr bool contains(basic_string_view sv)const; + constexpr bool contains(value_type sv)const; + constexpr bool contains(const_pointer str)const; + constexpr bool compare(const basic_string_view& s)const{return *this == s;} constexpr bool compare(const_pointer c)const{return *this == c;} @@ -115,6 +126,10 @@ namespace rexy{ constexpr size_type find_last_of(value_type v, size_type start = 0)const; constexpr size_type find_last_of(const_pointer c, size_type pos = 0)const; constexpr size_type find_last_of(const_pointer c, size_type pos, size_type size)const; + + + [[deprecated]] constexpr const_pointer get(void)const noexcept{return m_data;} + [[deprecated]] constexpr operator const_pointer(void)const noexcept{return m_data;} }; template diff --git a/include/rexy/string_view.tpp b/include/rexy/string_view.tpp index 12c6137..2acff8e 100644 --- a/include/rexy/string_view.tpp +++ b/include/rexy/string_view.tpp @@ -22,6 +22,7 @@ #include "compat/to_address.hpp" #include "utility.hpp" #include "string_base.hpp" +#include "algorithm.hpp" //two_way_search #include "compat/string_base.hpp" @@ -49,7 +50,7 @@ namespace rexy{ } template - constexpr auto basic_string_view::search(const basic_string_view& s)const -> const_iterator{ + constexpr auto basic_string_view::search(basic_string_view s)const -> const_iterator{ return two_way_search(cbegin(), cend(), s.cbegin(), s.cend()); } template @@ -59,7 +60,7 @@ namespace rexy{ } template template - constexpr auto basic_string_view::search(const basic_string_view& s, const Searcher& searcher)const -> const_iterator{ + constexpr auto basic_string_view::search(basic_string_view s, const Searcher& searcher)const -> const_iterator{ return searcher(cbegin(), cend(), s.cbegin(), s.cend()); } template @@ -69,6 +70,70 @@ namespace rexy{ return search(tmp, searcher); } + template + constexpr bool basic_string_view::starts_with(basic_string_view sv)const{ + if(sv.length() > length()){ + return false; + } + auto it = two_way_search(begin(), begin() + sv.length(), sv.cbegin(), sv.cend()); + if(it == begin()){ + return true; + } + return false; + } + template + constexpr bool basic_string_view::starts_with(value_type v)const{ + return front() == v; + } + template + constexpr bool basic_string_view::starts_with(const_pointer s)const{ + return starts_with(basic_string_view(s)); + } + + template + constexpr bool basic_string_view::ends_with(basic_string_view sv)const{ + if(sv.length() > length()){ + return false; + } + const auto start = end() - sv.length(); + auto it = two_way_search(start, end(), sv.cbegin(), sv.cend()); + if(it == start){ + return true; + } + return false; + } + template + constexpr bool basic_string_view::ends_with(value_type v)const{ + return back() == v; + } + template + constexpr bool basic_string_view::ends_with(const_pointer s)const{ + return ends_with(basic_string_view(s)); + } + + template + constexpr bool basic_string_view::contains(basic_string_view sv)const{ + const auto it = two_way_search(cbegin(), cend(), sv.cbegin(), sv.cend()); + if(it != cend()){ + return true; + } + return false; + } + template + constexpr bool basic_string_view::contains(value_type v)const{ + for(size_type i = 0;i < length();++i){ + if(m_data[i] == v){ + return true; + } + } + return false; + } + template + constexpr bool basic_string_view::contains(const_pointer str)const{ + return contains(basic_string_view(str)); + } + + template constexpr basic_string_view basic_string_view::substr(size_type pos, size_type count)const{ const size_type real_count = rexy::min(count, length() - pos); diff --git a/src/filerd.cpp b/src/filerd.cpp index 5b25275..33e6e84 100644 --- a/src/filerd.cpp +++ b/src/filerd.cpp @@ -106,7 +106,7 @@ namespace rexy{ return fwrite(c, 1, bytes, m_fp); } size_t filerd::write(const rexy::string_base& c)noexcept{ - return write(c.get(), c.length()); + return write(c.data(), c.length()); } } diff --git a/tests/basic_string.cpp b/tests/basic_string.cpp index 50a3431..16534a4 100644 --- a/tests/basic_string.cpp +++ b/tests/basic_string.cpp @@ -19,18 +19,16 @@ void check_empty_construction(){ if(str1.length() != 0) error("length() should return 0 on default init\n"); if(test_str::uses_sso()){ - if(str1.get()[0] != 0) - error("get() should return an empty, zero length string\n"); + if(str1.data()[0] != 0) + error("data() should return an empty, zero length string\n"); }else{ - if(str1.get() != nullptr) - error("get() should return a null string\n"); + if(str1.data() != nullptr) + error("data() should return a null string\n"); } if(str1.valid()) error("valid() should return false on empty string\n"); - if(str1.get() != str1.c_str()) - error("c_str() should be a synonymn of get()\n"); - if(char* c = str1;c != str1.get()) - error("conversion to pointer type should be synonymous with get()\n"); + if(str1.data() != str1.c_str()) + error("c_str() should be a synonymn of data()\n"); test_str str2(str1); if(str2.length() != str1.length()) @@ -38,11 +36,11 @@ void check_empty_construction(){ if(str2.capacity() != str1.capacity()) error("copy construction on empty string should give equivalent capacity()\n"); if(test_str::uses_sso()){ - if(str2.get()[0] != str1.get()[0]) - error("copy construction on empty string should give equivalent get()\n"); + if(str2.data()[0] != str1.data()[0]) + error("copy construction on empty string should give equivalent data()\n"); }else{ - if(str2.get() != str1.get()) - error("copy construction on empty string should give equivalent get()\n"); + if(str2.data() != str1.data()) + error("copy construction on empty string should give equivalent data()\n"); } test_str str3(std::move(str2)); @@ -51,11 +49,11 @@ void check_empty_construction(){ if(str3.capacity() != str1.capacity()) error("move construction on empty string should give equivalent capacity()\n"); if(test_str::uses_sso()){ - if(str3.get()[0] != str1.get()[0]) - error("move construction on empty string should give equivalent get()\n"); + if(str3.data()[0] != str1.data()[0]) + error("move construction on empty string should give equivalent data()\n"); }else{ - if(str3.get() != str1.get()) - error("move construction on empty string should give equivalent get()\n"); + if(str3.data() != str1.data()) + error("move construction on empty string should give equivalent data()\n"); } } @@ -69,24 +67,24 @@ void check_short_construction(){ error("short constructed string 'a' should be length() == 1\n"); if(str1.capacity() != cap) error("short constructed string 'a' should be capacity() == short_string_size()\n"); - if(strcmp(str1.get(), "a")) - error("short constructed string 'a' should be !strcmp(get(), \"a\")\n"); + if(strcmp(str1.data(), "a")) + error("short constructed string 'a' should be !strcmp(data(), \"a\")\n"); test_str str2(str1); if(str2.length() != str1.length()) error("short copy constructed string should have equal length()\n"); if(str2.capacity() != str1.capacity()) error("short copy constructed string should have equal capacity()\n"); - if(strcmp(str2.get(), str1.get())) - error("short copy constructed string should have equivalent get()\n"); + if(strcmp(str2.data(), str1.data())) + error("short copy constructed string should have equivalent data()\n"); test_str str3(std::move(str2)); if(str3.length() != str1.length()) error("short move constructed string should have equal length()\n"); if(str3.capacity() != str1.capacity()) error("short move constructed string should have equal capacity()\n"); - if(strcmp(str3.get(), str1.get())) - error("short move constructed string should have equivalent get()\n"); + if(strcmp(str3.data(), str1.data())) + error("short move constructed string should have equivalent data()\n"); } void check_long_construction(){ const char* data = "this is a really long string that should ensure that it makes a dynamic allocation even if it has a big buffer."; @@ -96,24 +94,24 @@ void check_long_construction(){ error("long constructed string should be length() == strlen(data)\n"); if(str1.capacity() < len) error("long constructed string should be capacity() >= strlen(data)\n"); - if(strcmp(str1.get(), data)) - error("long constructed string should be !strcmp(get(), data)\n"); + if(strcmp(str1.data(), data)) + error("long constructed string should be !strcmp(data(), data)\n"); test_str str2(str1); if(str2.length() != str1.length()) error("long copy constructed string should have equal length()\n"); if(str2.capacity() != str1.capacity()) error("long copy constructed string should have equal capacity()\n"); - if(strcmp(str2.get(), str1.get())) - error("long copy constructed string should have equivalent get()\n"); + if(strcmp(str2.data(), str1.data())) + error("long copy constructed string should have equivalent data()\n"); test_str str3(std::move(str2)); if(str3.length() != str1.length()) error("long move constructed string should have equal length()\n"); if(str3.capacity() != str1.capacity()) error("long move constructed string should have equal capacity()\n"); - if(strcmp(str3.get(), str1.get())) - error("long move constructed string should have equivalent get()\n"); + if(strcmp(str3.data(), str1.data())) + error("long move constructed string should have equivalent data()\n"); } void check_short_assignment(){ if(!test_str::uses_sso()) @@ -128,8 +126,8 @@ void check_short_assignment(){ error("short assigned string 'a' should be length() == 1\n"); if(str1.capacity() != cap) error("short assigned string 'a' should be capacity() == short_string_size()\n"); - if(strcmp(str1.get(), "a")) - error("short assigned string 'a' should be !strcmp(get(), \"a\")\n"); + if(strcmp(str1.data(), "a")) + error("short assigned string 'a' should be !strcmp(data(), \"a\")\n"); test_str str2("ba"); str2 = str1; @@ -137,8 +135,8 @@ void check_short_assignment(){ error("short copy assigned string should have equal length()\n"); if(str2.capacity() != str1.capacity()) error("short copy assigned string should have equal capacity()\n"); - if(strcmp(str2.get(), str1.get())) - error("short copy assigned string should have equivalent get()\n"); + if(strcmp(str2.data(), str1.data())) + error("short copy assigned string should have equivalent data()\n"); test_str str3("cb"); str3 = std::move(str2); @@ -146,8 +144,8 @@ void check_short_assignment(){ error("short move assigned string should have equal length()\n"); if(str3.capacity() != str1.capacity()) error("short move assigned string should have equal capacity()\n"); - if(strcmp(str3.get(), str1.get())) - error("short move assigned string should have equivalent get()\n"); + if(strcmp(str3.data(), str1.data())) + error("short move assigned string should have equivalent data()\n"); test_str str4(longstartdata); str4 = str1; @@ -155,8 +153,8 @@ void check_short_assignment(){ error("long->short copy assigned string should have equal length()\n"); if(str4.capacity() < str1.capacity()) error("long->short copy assigned string should have equal or greater capacity()\n"); - if(strcmp(str4.get(), str1.get())) - error("long->short copy assigned string should have equivalent get()\n"); + if(strcmp(str4.data(), str1.data())) + error("long->short copy assigned string should have equivalent data()\n"); test_str str5(longstartdata); str5 = std::move(str4); @@ -164,8 +162,8 @@ void check_short_assignment(){ error("long->short move assigned string should have equal length()\n"); if(str5.capacity() < str1.capacity()) error("long->short move assigned string should have equal or greater capacity()\n"); - if(strcmp(str5.get(), str1.get())) - error("long->short move assigned string should have equivalent get()\n"); + if(strcmp(str5.data(), str1.data())) + error("long->short move assigned string should have equivalent data()\n"); } void check_long_assignment(){ const char* startdata1 = "this is another really long string that should ensure that it makes some sort of dyn alloc for big buf"; @@ -178,8 +176,8 @@ void check_long_assignment(){ error("long assigned string should be length() == strlen(data)\n"); if(str1.capacity() < len) error("long assigned string should be capacity() >= strlen(data)\n"); - if(strcmp(str1.get(), data)) - error("long assigned string should be !strcmp(get(), data)\n"); + if(strcmp(str1.data(), data)) + error("long assigned string should be !strcmp(data(), data)\n"); test_str str2(startdata1); str2 = str1; @@ -187,8 +185,8 @@ void check_long_assignment(){ error("long copy assigned string should have equal length()\n"); if(str2.capacity() != str1.capacity()) error("long copy assigned string should have equal capacity()\n"); - if(strcmp(str2.get(), str1.get())) - error("long copy assigned string should have equivalent get()\n"); + if(strcmp(str2.data(), str1.data())) + error("long copy assigned string should have equivalent data()\n"); test_str str3(startdata1); str3 = std::move(str2); @@ -196,8 +194,8 @@ void check_long_assignment(){ error("long move assigned string should have equal length()\n"); if(str3.capacity() != str1.capacity()) error("long move assigned string should have equal capacity()\n"); - if(strcmp(str3.get(), str1.get())) - error("long move assigned string should have equivalent get()\n"); + if(strcmp(str3.data(), str1.data())) + error("long move assigned string should have equivalent data()\n"); test_str str4(startdata2); str4 = str1; @@ -205,8 +203,8 @@ void check_long_assignment(){ error("short->long copy assigned string should have equal length()\n"); if(str4.capacity() != str1.capacity()) error("short->long copy assigned string should have equal capacity()\n"); - if(strcmp(str4.get(), str1.get())) - error("short->long copy assigned string should have equivalent get()\n"); + if(strcmp(str4.data(), str1.data())) + error("short->long copy assigned string should have equivalent data()\n"); test_str str5(startdata2); str5 = std::move(str4); @@ -214,8 +212,8 @@ void check_long_assignment(){ error("short->long move assigned string should have equal length()\n"); if(str5.capacity() != str1.capacity()) error("short->long move assigned string should have equal capacity()\n"); - if(strcmp(str5.get(), str1.get())) - error("short->long move assigned string should have equivalent get()\n"); + if(strcmp(str5.data(), str1.data())) + error("short->long move assigned string should have equivalent data()\n"); } void check_short_append(){ test_str str1; @@ -224,10 +222,10 @@ void check_short_append(){ str1.append("a"); str1.append("b"); str1.append(str2); - if(strcmp(str1.get(), "abbc")) + if(strcmp(str1.data(), "abbc")) error("short append should have resulted in abbc\n"); str1.append(str3); - if(strcmp(str1, "abbcreally long string that should trigger a short to long conversion in the string")) + if(strcmp(str1.c_str(), "abbcreally long string that should trigger a short to long conversion in the string")) error("short->long append should have resulted in abbcreally long string that should trigger a short to long conversion in the string\n"); } void check_long_append(){ @@ -235,21 +233,65 @@ void check_long_append(){ const char* appendeddata = "this is another really long string that should ensure that it makes some sort of dyn alloc for big bufstuff"; test_str str1(startdata1); str1.append("stuff"); - if(strcmp(str1.get(), appendeddata)) + if(strcmp(str1.c_str(), appendeddata)) error("long append should have resulted in this is another really long string that should ensure that it makes some sort of dyn alloc for big bufstuff\n"); } void check_substring(){ rexy::string test = "this is a test string"; rexy::string test2 = test.substring(5, 7); - if(strcmp(test2.get(), "is") || test2.length() != 2) + if(strcmp(test2.c_str(), "is") || test2.length() != 2) error("substring operation should have resulted in 'is'\n"); } void check_string_search(){ rexy::string test1 = "this is a test string"; rexy::string test2 = "string"; - auto res = test1.search(test2); - if(test1 + 15 != res){ - error("string search operation did not result in a correct result"); + auto res = test1.search(test2.create_view()); + if(test1.data() + 15 != res){ + error("string search operation did not result in a correct result\n"); + } +} + +void check_string_insert(){ + rexy::string test = "this is a string"; + auto it = test.search("string"); + if(it == test.end()){ + error("string search failed\n"); + } + test.insert(it - test.begin(), "test ", 5); + if(test != "this is a test string" || test.length() != 21){ + error("string insert operation failed\n"); + } + test.insert(0, "wow "); + if(test != "wow this is a test string" || test.length() != 25){ + error("string insert operation 2 failed\n"); + } + test.insert(test.length(), " oof"); + if(test != "wow this is a test string oof" || test.length() != 29){ + error("string insert operation 3 failed\n"); + } +} + +void check_string_erase(){ + rexy::string test = "this is a test string"; + test.erase(0, 5); + if(test != "is a test string" || test.length() != 16){ + error("string erase operation 1 did not result in a correct result\n"); + } + test.erase(5, 5); + if(test != "is a string" || test.length() != 11){ + error("string erase operation 2 did not result in a correct result\n"); + } + test.erase(9, 2); + if(test != "is a stri" || test.length() != 9){ + error("string erase operation 3 did not result in a correct result\n"); + } + test.erase(8, 2); + if(test != "is a str" || test.length() != 8){ + error("string erase operation 4 did not result in a correct result\n"); + } + test.pop_back(); + if(test != "is a st" || test.length() != 7){ + error("string erase operation 5 did not result in a correct result\n"); } } @@ -263,4 +305,6 @@ int main(){ check_long_append(); check_substring(); check_string_search(); + check_string_insert(); + check_string_erase(); }