/** This file is a part of 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; constexpr string_base(size_t len): m_length(len){} //Initialize without copying constexpr string_base(char* data, size_t len): m_length(len), m_data(data){} //Copy ctor (do nothing) string_base(const string_base&){} ~string_base(void) = default; public: //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;} constexpr operator char*(void){return m_data;} constexpr operator const char*(void)const{return m_data;} //true if m_data is not null constexpr operator bool(void)const{return m_data;} char& operator[](size_t i); const char& operator[](size_t i)const; }; //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, size_t len): string_base(reinterpret_cast(len ? Allocator::copy(data, len+1) : nullptr), len){} string_intermediary(const char* data): string_base(data ? strlen(data) : 0) { if(m_length) m_data = reinterpret_cast(Allocator::copy(data, m_length+1)); } string_intermediary(size_t len): string_base(reinterpret_cast(len ? Allocator::allocate(len+1) : nullptr), len){} //normal copy and move ctors string_intermediary(const string_intermediary& b): string_base(reinterpret_cast(b.m_length ? Allocator::copy(b.m_data, b.m_length+1) : nullptr), 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(b.length() ? Allocator::copy(b.get(), b.length()+1) : nullptr), 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(); if(!len){ return; } 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& s){ string_intermediary tmp(s); std::swap(m_data, tmp.m_data); m_length = tmp.m_length; return *this; } string_intermediary& operator=(string_intermediary&& s){ std::swap(m_data, s.m_data); m_length = s.m_length; return *this; } //Copy from c string string_intermediary& operator=(const char* c){ return _copy_string(c, strlen(c)); } //Copy from other string_base string_intermediary& operator=(const string_base& s){ return _copy_string(s.get(), s.length()); } //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 = reinterpret_cast(Allocator::allocate(len+1)); _assign(tmp, t.get(), 0); Allocator::free(m_data); }else{ tmp = m_data; _assign(tmp, t.get(), 0); } m_data = tmp; m_data[len] = 0; m_length = len; return *this; } 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; } //Replace managed pointer. Frees existing value void reset(char* val = nullptr){ Allocator::free(m_data); m_data = val; m_length = val ? strlen(val) : 0; } void reset(char* val, size_t len){ Allocator::free(m_data); m_data = val; m_length = len; } bool resize(size_t newsize){ if(newsize < m_length) return false; string_intermediary tmp(newsize); memcpy(tmp.get(), m_data, m_length); tmp[m_length] = 0; *this = std::move(tmp); return true; } void append(const char* data, size_t len){ string_intermediary tmp(m_length + len); memcpy(tmp.m_data, m_data, m_length); memcpy(tmp.m_data+m_length, data, len); tmp[m_length+len] = 0; *this = std::move(tmp); } void append(const char* data){ *this += data; } void append(const string_expr& s){ *this += s; } private: string_intermediary& _copy_string(const char* s, size_t len){ if(!len){ Allocator::free(m_data); m_length = 0; return *this; } if(len <= m_length){ strcpy(m_data, s); }else{ Allocator::free(m_data); m_data = reinterpret_cast(Allocator::copy(s, len+1)); if(!m_data){ m_length = 0; return *this; } } m_length = len; return *this; } 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)); } } }; //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 constexpr string_cat_expr(T&& l, U&& r): m_l(std::forward(l)), m_r(std::forward(r)){} constexpr string_cat_expr(const string_cat_expr& s): m_l(s.m_l), m_r(s.m_r){} constexpr string_cat_expr(string_cat_expr&& s): m_l(s.m_l), m_r(s.m_r){} constexpr size_t length(void)const{ return _llen() + _rlen(); } constexpr auto get(void){ return std::tuple_cat(_lget(), _rget()); } private: constexpr 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)); } } constexpr 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)); } } constexpr size_t _llen(void)const{ if constexpr(detail::has_len::type>::value){ return m_l.length(); }else{ return strlen(m_l); } } constexpr 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> constexpr auto operator+(const char* left, Right&& right){ return raii::string_cat_expr(right))>(left, std::forward(right)); } template::value,void>::type* = nullptr> constexpr 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> constexpr 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); } #ifdef RAII_BINARY_HPP #include "raii/binary_string_conv.hpp" #endif #endif