/** This file is a part of r0nk, atlas_moon, and rexy's matrix client Copyright (C) 2019 rexy712 This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ #ifndef RAII_STRING_BASE_HPP #define RAII_STRING_BASE_HPP #include //size_t #include //strlen, strcpy #include //memcpy #include //is_same, integral_contant, enable_if, etc #include //forward #include namespace raii{ class string_expr{}; class string_base; namespace detail{ std::true_type is_string_helper(string_expr); std::false_type is_string_helper(...); template struct is_string{ static constexpr bool value = std::is_same()))>::value; }; std::true_type is_string_base(string_base*); std::false_type is_string_base(...); template struct is_concrete_string{ static constexpr bool value = std::is_same::type*>()))>::value; }; template std::true_type is_tuple_helper(std::tuple); std::false_type is_tuple_helper(...); template struct is_tuple{ static constexpr bool value = std::is_same()))>::value; }; } //Base of all RAII strings. Its use is allowing passing of raii strings to functions without knowing the exact type class string_base : public string_expr { protected: size_t m_length = 0; char* m_data = nullptr; protected: constexpr string_base(void) = default; //Initialize without copying constexpr string_base(char* data, size_t len): m_length(len), m_data(data){} //Allocate without assigning string_base(size_t len); //Copy ctor (do nothing) string_base(const string_base&){} public: virtual ~string_base(void) = default; public: //Copy from c string string_base& operator=(const char* c); //Copy from other string_base string_base& operator=(const string_base& s); //Move from other string base template::value && !detail::is_concrete_string::value,void>::type* = nullptr> string_base& operator=(T&& t){ size_t len = t.length(); char* tmp; if(len > m_length){ tmp = _allocate(len+1); _assign(tmp, t.get(), 0); _free(m_data); }else{ tmp = m_data; _assign(tmp, t.get(), 0); } m_data = tmp; m_data[len] = 0; m_length = len; return *this; } //Replace managed pointer. Frees existing value void reset(char* val = nullptr); //Stop managing stored pointer. Does not free. char* release(void); //Length of string not including null terminator constexpr size_t length(void)const{return m_length;} //direct access to managed pointer constexpr char* get(void){return m_data;} constexpr const char* get(void)const{return m_data;} operator char*(void); operator const char*(void)const; //true if m_data is not null operator bool(void)const; char& operator[](size_t i); const char& operator[](size_t i)const; protected: string_base& _copy_string(const char* s, size_t len); template static void _assign(char* dest, Tup&& t, size_t offset){ memcpy(dest+offset, std::get(t), std::get(t)); if constexpr(I+2 < std::tuple_size::value){ _assign(dest, std::forward(t), offset+std::get(t)); } } private: virtual char* _allocate(size_t)const = 0; virtual void _free(char*)const = 0; virtual char* _copy(const char*, size_t)const = 0; }; //Supplies all functions that string_base can't implement template class string_intermediary : public string_base { public: using allocator_type = Allocator; public: string_intermediary(void) = default; string_intermediary(char* data, size_t len): string_base(data, len){} string_intermediary(const char* data): string_base(strlen(data)) { m_data = reinterpret_cast(Allocator::copy(data, m_length+1)); } string_intermediary(size_t len): string_base(reinterpret_cast(Allocator::allocate(len+1)), len){} //normal copy and move ctors string_intermediary(const string_intermediary& b): string_base(reinterpret_cast(Allocator::copy(b.m_data, b.m_length+1)), b.m_length){} string_intermediary(string_intermediary&& s): string_base(std::exchange(s.m_data, nullptr), s.m_length){} string_intermediary(const string_base& b): string_base(reinterpret_cast(Allocator::copy(b.get(), b.length()+1)), b.length()){} //copy from string expression template::value && !detail::is_concrete_string::value,void>::type* = nullptr> string_intermediary(T&& t){ size_t len = t.length(); char* tmp = reinterpret_cast(Allocator::allocate(len+1)); _assign(tmp, t.get(), 0); m_data = tmp; m_data[len] = 0; m_length = len; } //dtor ~string_intermediary(void){ Allocator::free(m_data); } string_intermediary& operator=(const string_intermediary&) = default; string_intermediary& operator=(string_intermediary&& s){ std::swap(m_data, s.m_data); m_length = s.m_length; return *this; } using string_base::operator=; string_intermediary operator+(const string_base& s)const{ string_intermediary tmp(reinterpret_cast(Allocator::allocate(m_length + s.length() + 1)), m_length+s.length()); memcpy(tmp.get(), m_data, m_length); strcpy(tmp.get()+m_length, s.get()); return tmp; } string_intermediary operator+(const char* c)const{ size_t len = strlen(c); string_intermediary tmp(reinterpret_cast(Allocator::allocate(m_length + len + 1)), m_length+len); memcpy(tmp.get(), m_data, m_length); strcpy(tmp.get()+m_length, c); return tmp; } private: char* _allocate(size_t len)const override final{ return reinterpret_cast(Allocator::allocate(len)); } void _free(char* ptr)const override final{ Allocator::free(ptr); } char* _copy(const char* ptr, size_t len)const override final{ return reinterpret_cast(Allocator::copy(ptr, len)); } }; //check for member function 'length' namespace detail{ template struct has_len{ template struct check; template static std::true_type test(check*); template static std::false_type test(...); static constexpr bool value = std::is_same(0))>::value; }; } //Like an expression template but not really template class string_cat_expr : public string_expr { private: Left m_l; Right m_r; public: template string_cat_expr(T&& l, U&& r): m_l(std::forward(l)), m_r(std::forward(r)){} string_cat_expr(const string_cat_expr& s): m_l(s.m_l), m_r(s.m_r){} string_cat_expr(string_cat_expr&& s): m_l(s.m_l), m_r(s.m_r){} size_t length(void)const{ return _llen() + _rlen(); } auto get(void){ return std::tuple_cat(_lget(), _rget()); } private: auto _lget(void){ if constexpr(detail::is_string::value){ if constexpr(detail::is_tuple::value){ //string_cat_expr return m_l.get(); }else{ //string_base return std::make_tuple(m_l.get(), m_l.length()); } }else{ //c string return std::make_tuple(m_l, strlen(m_l)); } } auto _rget(void){ if constexpr(detail::is_string::value){ if constexpr(detail::is_tuple::value){ return m_r.get(); }else{ return std::make_tuple(m_r.get(), m_r.length()); } }else{ return std::make_tuple(m_r, strlen(m_r)); } } size_t _llen(void)const{ if constexpr(detail::has_len::type>::value){ return m_l.length(); }else{ return strlen(m_l); } } size_t _rlen(void)const{ if constexpr(detail::has_len::type>::value){ return m_r.length(); }else{ return strlen(m_r); } } }; } template::value&&raii::detail::is_concrete_string::value,void>::type* = nullptr> bool operator==(Str1&& left, Str2&& right){ return left && right && left.length() == right.length() && !strcmp(left.get(), right.get()); } template::value&&raii::detail::is_concrete_string::value,void>::type* = nullptr> bool operator!=(Str1&& left, Str2&& right){ return !(left == right); } template::value,void>::type* = nullptr> auto operator+(const char* left, Right&& right){ return raii::string_cat_expr(right))>(left, std::forward(right)); } template::value,void>::type* = nullptr> auto operator+(Left&& left, const char* right){ return raii::string_cat_expr(left)),const char*>(std::forward(left), right); } template::value&&raii::detail::is_string::value,void>::type* = nullptr> auto operator+(Left&& l, Right&& r){ return raii::string_cat_expr(l)),decltype(std::forward(r))>(std::forward(l), std::forward(r)); } template::value&&raii::detail::is_string::value,void>::type* = nullptr> decltype(auto) operator+=(Left& l, Right&& r){ return l = (l + std::forward(r)); } template::value,void>::type* = nullptr> decltype(auto) operator+=(Left& l, const char* r){ return l = (l + r); } #endif