/** 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_HPP #define REXY_STRING_BASE_HPP #include //is_same, integral_contant, enable_if, etc #include //forward #include //size_t,ptrdiff #include //strlen #include //CHAR_BIT #include //reverse_iterator #include //ostream #include #include "steal.hpp" #include "utility.hpp" #include "traits.hpp" #include "expression.hpp" #include "detail/string_appender.hpp" #include "detail/hasallocator.hpp" #include "allocator.hpp" #include "rexy.hpp" #include "compat/standard.hpp" namespace rexy{ template class basic_string_view; //Base of all RAII strings. Its use is allowing passing of rexy strings to functions without knowing the exact type template class string_base { public: using value_type = Char; using size_type = size_t; using difference_type = ptrdiff_t; using pointer = value_type*; using const_pointer = const value_type*; using reference = value_type&; using const_reference = const value_type&; using iterator = pointer; using const_iterator = const_pointer; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; private: static constexpr size_type EXTRA_SDATA_LEN = 0; //represent long string struct ldata{ unsigned char islong:1; //common subsequence with short string size_type capacity:(CHAR_BIT*sizeof(size_type)-1); //take away last bit from capacity for islong size_type length; //length of string excluding null terminator constexpr ldata(void)noexcept: islong(1), capacity(0), length(0){} }; static constexpr size_type MAX_SHORT_LEN = EXTRA_SDATA_LEN+sizeof(ldata)-2; //represent short string struct sdata{ unsigned char islong:1; //common subsequence with long string unsigned char length:(CHAR_BIT-1); //take away last bit from length for islong, excludes null terminator value_type data[MAX_SHORT_LEN+1]; //char array for string storage constexpr sdata(void)noexcept: islong(0), length(0), data{}{} }; //union of short and long string representations. union combine_data{ ldata l; sdata s; constexpr combine_data(void)noexcept: s(){} }m_data; //direct access to current string data regardless of representation. Increases access speed. pointer m_raw = m_data.s.data; protected: //Functions for handling long vs short string manipulation. Use this instead of directly modifying m_data. constexpr void set_islong_flag(bool b){ //although well defined to set either one at any time, constexpr functions cannot change active union member. if(b) m_data.l.islong = b; else m_data.s.islong = b; } constexpr bool islong(void)const{ //common standard layout union member subsequence, never undefined behavior return m_data.l.islong; } constexpr pointer set_short_ptr(void){ set_islong_flag(false); return m_raw = m_data.s.data; } constexpr pointer set_long_ptr(pointer ptr){ set_islong_flag(true); return m_raw = ptr; } constexpr pointer get_long_ptr(void){return m_raw;} constexpr pointer get_short_ptr(void){return m_raw;} constexpr const_pointer get_long_ptr(void)const{return m_raw;} constexpr const_pointer get_short_ptr(void)const{return m_raw;} constexpr pointer get_pointer(void){return m_raw;} constexpr const_pointer get_pointer(void)const{return m_raw;} constexpr void set_long_length(size_type len){m_data.l.length = len;} constexpr size_type get_long_length(void)const{return m_data.l.length;} constexpr void set_short_length(size_type len){m_data.s.length = static_cast(len);} constexpr size_type get_short_length(void)const{return m_data.s.length;} constexpr void set_long_capacity(size_type cap){m_data.l.capacity = cap;} 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_length(size_type s){ if(islong()) set_long_length(s); else set_short_length(s); } protected: constexpr string_base(void)noexcept = default; //Initialize without copying constexpr string_base(pointer data, size_type len, size_type cap)noexcept{ if(cap > MAX_SHORT_LEN){ set_islong_flag(true); set_long_ptr(data); set_long_length(len); set_long_capacity(cap); }else if(len){ set_islong_flag(false); pointer raw = set_short_ptr(); if(len) memcpy(raw, data, sizeof(value_type)*len); raw[len] = 0; set_short_length(len); set_short_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_data(s.m_data){} constexpr string_base(string_base&& s)noexcept: m_data(std::move(s.m_data)), m_raw(s.islong() ? s.m_raw : m_data.s.data) { s.set_islong_flag(false); } REXY_CPP20_CONSTEXPR ~string_base(void)noexcept = default; constexpr string_base& operator=(string_base&& s)noexcept{ std::swap(m_data, s.m_data); if(this->islong()) std::swap(m_raw, s.m_raw); else{ s.m_raw = m_raw; m_raw = m_data.s.data; } 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(); } //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();} //true if m_data is not empty constexpr bool valid(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 const_iterator search(const string_base& s)const; 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 compare(const string_base& s)const{return *this == s;} constexpr bool compare(const_pointer c)const{return *this == c;} constexpr iterator begin(void){return get_pointer();} constexpr const_iterator begin(void)const{return get_pointer();} constexpr iterator end(void){return get_pointer()+length();} constexpr const_iterator end(void)const{return get_pointer()+length();} constexpr const_iterator cbegin(void)const{return begin();} constexpr const_iterator cend(void)const{return end();} constexpr reverse_iterator rbegin(void){return reverse_iterator(get_pointer()+length());} constexpr const_reverse_iterator rbegin(void)const{return const_reverse_iterator(get_pointer()+length());} constexpr reverse_iterator rend(void){return reverse_iterator(get_pointer()-1);} constexpr const_reverse_iterator rend(void)const{return const_reverse_iterator(get_pointer()-1);} 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;} }; //Supplies all functions that string_base can't implement template> class basic_string : protected detail::hasallocator, public string_base { public: using value_type = typename string_base::value_type; using size_type = typename string_base::size_type; using difference_type = typename string_base::difference_type; using pointer = typename string_base::pointer; using const_pointer = typename string_base::const_pointer; using reference = typename string_base::reference; using const_reference = typename string_base::const_reference; using iterator = typename string_base::iterator; using const_iterator = typename string_base::const_iterator; using reverse_iterator = typename string_base::reverse_iterator; using const_reverse_iterator = typename string_base::const_reverse_iterator; using allocator_type = Alloc; static constexpr size_type npos = size_type{-1}; private: REXY_CPP20_CONSTEXPR void _copy_construct_string(const_pointer data, size_type len, size_type cap) noexcept(is_nothrow_allocator_v); REXY_CPP20_CONSTEXPR basic_string& _copy_string(const_pointer s, size_type len) noexcept(is_nothrow_allocator_v); public: constexpr basic_string(void)noexcept; constexpr basic_string(rexy::steal data, size_type len)noexcept; constexpr basic_string(rexy::steal data, size_type len, size_type cap)noexcept; constexpr basic_string(rexy::steal data)noexcept; REXY_CPP20_CONSTEXPR basic_string(const_pointer data, size_type len)noexcept(is_nothrow_allocator_v); REXY_CPP20_CONSTEXPR basic_string(const_pointer data, size_type len, size_type cap)noexcept(is_nothrow_allocator_v); REXY_CPP20_CONSTEXPR basic_string(const_pointer data)noexcept(is_nothrow_allocator_v); REXY_CPP20_CONSTEXPR explicit basic_string(size_type len)noexcept(is_nothrow_allocator_v); REXY_CPP20_CONSTEXPR basic_string(size_type len, size_type cap)noexcept(is_nothrow_allocator_v); template REXY_CPP20_CONSTEXPR basic_string(InputIt start, InputIt fin)noexcept(is_nothrow_allocator_v); REXY_CPP20_CONSTEXPR basic_string(const basic_string_view& sv)noexcept(is_nothrow_allocator_v); //normal copy and move ctors REXY_CPP20_CONSTEXPR basic_string(const basic_string& b)noexcept(is_nothrow_allocator_v); constexpr basic_string(basic_string&& s)noexcept; REXY_CPP20_CONSTEXPR basic_string(const string_base&)noexcept(is_nothrow_allocator_v); //dtor REXY_CPP20_CONSTEXPR ~basic_string(void)noexcept(is_nothrow_allocator_v); REXY_CPP20_CONSTEXPR basic_string& operator=(const basic_string& s) noexcept(is_nothrow_allocator_v); constexpr basic_string& operator=(basic_string&& s)noexcept; REXY_CPP20_CONSTEXPR basic_string& operator=(const string_base& s) noexcept(is_nothrow_allocator_v); REXY_CPP20_CONSTEXPR basic_string& operator=(const basic_string_view& sv) noexcept(is_nothrow_allocator_v); //Copy from c string REXY_CPP20_CONSTEXPR basic_string& operator=(const_pointer c) noexcept(is_nothrow_allocator_v); //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); //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); REXY_CPP20_CONSTEXPR basic_string& insert(size_type pos, const_pointer str, size_type insert_count)noexcept(is_nothrow_allocator_v); REXY_CPP20_CONSTEXPR basic_string& insert(size_type pos, const basic_string& other)noexcept(is_nothrow_allocator_v); 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); 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) -> std::enable_if_t> && !std::is_convertible_v>; 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); template REXY_CPP20_CONSTEXPR basic_string substring(size_type start, size_type end)const; REXY_CPP20_CONSTEXPR basic_string substr(size_type start, size_type end)const; 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; }; //Like an expression template but not really template class string_cat_expr : public rexy::binary_expression { static_assert(std::is_same::value_type,typename std::decay_t::value_type>::value); private: using left_t = std::decay_t; using right_t = std::decay_t; public: using value_type = typename left_t::value_type; using size_type = decltype(typename left_t::size_type{0} + typename right_t::size_type{0}); using difference_type = decltype(typename left_t::difference_type{0} - typename right_t::difference_type{0}); using pointer = value_type*; using const_pointer = const value_type*; using reference = value_type&; using const_reference = const value_type&; using iterator = value_type*; using const_iterator = const value_type*; public: using binary_expression::binary_expression; constexpr string_cat_expr(const string_cat_expr&) = default; constexpr string_cat_expr(string_cat_expr&&) = default; constexpr size_type length(void)const noexcept; template REXY_CPP20_CONSTEXPR operator basic_string(void) noexcept(std::is_nothrow_constructible, typename basic_string::size_type>::value && std::is_nothrow_invocable>,decltype(*this)>::value); }; 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" namespace{ template std::ostream& operator<<(std::ostream& os, const rexy::basic_string& str){ return os << str.c_str(); } } #endif