From 3737da708d1a7b8af83f3532b0e469cce8b8354e Mon Sep 17 00:00:00 2001 From: heck Date: Tue, 22 Feb 2022 22:39:14 +0100 Subject: [PATCH] Inital prototyping --- test/test_nr1.cc | 413 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 413 insertions(+) create mode 100644 test/test_nr1.cc diff --git a/test/test_nr1.cc b/test/test_nr1.cc new file mode 100644 index 0000000..0385391 --- /dev/null +++ b/test/test_nr1.cc @@ -0,0 +1,413 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//using namespace pEp; + + +namespace pEp { + + template + std::string type_add_val(const T c, size_t val_len = 30) + { + static_assert(std::is_pointer::value, "only pointer types are valid"); + std::stringstream ss_type; + //type + ss_type << typeid(T).name(); + + //addr + std::stringstream ss_addr{}; + ss_addr << static_cast(c); + + //value + std::stringstream ss_val{}; + if (c != nullptr) { + ss_val << "\"" << c << "\""; + } else { + ss_val << ""; + } + + std::stringstream ret{}; + ret << "{ " << ss_type.str() << " | " + ss_addr.str() << " | " + << pEp::Utils::clip(ss_val.str(), val_len) << " }"; + return ret.str(); + } + + char* alloc(const std::string& str) + { + char* ret = strdup(str.c_str()); + pEpLog(type_add_val(ret)); + return ret; + } + + template + void free(T ptr_type) + { + pEpLog(type_add_val(ptr_type)); + ::pEp_free(ptr_type); + } + + template<> + void free(char* ptr_type) + { + pEpLog(type_add_val(ptr_type)); + ::pEp_free(ptr_type); + } + + template<> + void free(::pEp_identity* ptr_type) + { + pEpLog(type_add_val(ptr_type)); + ::pEp_free(ptr_type); + } + + +//--------------------------------------------------------------------------------------------- +#define EXSTR(msg) std::string(__FUNCTION__) + " - " + msg + + + // Manages a char* to appear like a std::string + // char* c_str + // c_str MUST point to either: + // * NULL - means there is no value/mem alloc yet + // * dyn allocated memory + // in case the mem is already allocated, there must be 2 modes + // * take ownership (and free it) + // * dont take ownership, only provide a C++ interface + // There must be the option to construct the object before the wrapped c_str (char*) + // is known. This leads to a pEp::String object in invalid state, and it must be initialized using + // set(). Otherwise all(most) functions will throw. + // set() can only be called once, will throw otherwise. + class String { + public: + String() = default; + + // Best to use this constructor, as the object is in a valid state when the wrapped + // c_str (char*) is known + explicit String(char** c_str_pp) + { + initialize(c_str_pp); + } + + ~String() + { + _free(); + } + + void initialize(char** c_str_pp) + { + if (c_str_pp == nullptr) { + throw Exception{ EXSTR("cant initialize on a nullptr") }; + } + + if (_is_initialized) { + throw Exception{ EXSTR("double initialization") }; + } + + // init + _c_str_pp = c_str_pp; + // _c_str_p.reset(*_c_str_pp, [&](char* ptr) { this->_free(ptr); }); + _c_str_p = *_c_str_pp; + + _is_initialized = true; + pEpLogClass(to_string() + " - taking ownership"); + } + + // make a copy + String& operator=(const std::string& str) + { + pEpLogClass("Before: " + to_string() + " - new val: '" + pEp::Utils::clip(str, 30) + "'"); + if (_c_str_pp == nullptr) { + throw Exception{ EXSTR("invalid state") }; + } + + // DEALLOCATION + _free(); + // // if we point to an initialized string + // if (*_c_str_pp != nullptr) { + // // if the string there is not the one we created + //// if (*_c_str_pp != _c_str_p.get()) { + // if (*_c_str_pp != _c_str_p) { + // // we need to free it before we lose the pointer to it + // pEpLog("freeing foreign allocated string"); + // pEp::free(*_c_str_pp); + // // } // else it will be free automatically upon .reset() + // } + // //and we need to anyways free the strings we create ourselfes + // if + // pEp::free(_c_str_p;) + // } + + + // ALLOCATION + if (str.empty()) { + // if the new value is empty str, lets point to nothing + *_c_str_pp = nullptr; + // _c_str_p.reset(*_c_str_pp); + _c_str_p = *_c_str_pp; + } else { + *_c_str_pp = pEp::alloc(str); + // _c_str_p.reset(*_c_str_pp, [&](char* ptr) { this->_free(ptr); }); + _c_str_p = *_c_str_pp; + } + pEpLogClass("After: " + to_string()); + return *this; + } + + // return a copy of whatever c_str currently is (maybe created by us, maybe changed meanwhile) + operator std::string() const + { + if (_c_str_pp == nullptr) { + throw Exception{ EXSTR("invalid state") }; + } + + if (*_c_str_pp != nullptr) { + return { *_c_str_pp }; + } + return {}; + } + + char** data() + { + return _c_str_pp; + } + + const char* c_data() const + { + if (_c_str_pp == nullptr) { + throw Exception{ EXSTR("invalid state") }; + } + return *_c_str_pp; + } + + std::string to_string() const + { + if (_c_str_pp == nullptr) { + throw Exception{ EXSTR("invalid state") }; + } + + std::string ret{ "[" + type_add_val(_c_str_pp) + " / " + type_add_val(*_c_str_pp) + "]" }; + return ret; + } + + static bool log_enabled; + + + private: + void _free() + { + // if the c_str points to a different address now, than the one we created + if (*_c_str_pp != _c_str_p) { + // a new string has been created, and we need to free it as well + pEpLog("raw access string change detected"); + pEp::free(*_c_str_pp); + } else { + // we anyways need to free the one we created + // NO: WE ASSUME THAT: + // if the char* has been replaced, it has been freed, as well + if (_c_str_p != nullptr) { + pEp::free(_c_str_p); + } + } + } + + bool _is_initialized{ false }; + char** _c_str_pp{ nullptr }; + // std::shared_ptr _c_str_p{}; + char* _c_str_p{ nullptr }; + + Adapter::pEpLog::pEpLogger logger{ "pEp::String", log_enabled }; + Adapter::pEpLog::pEpLogger& m4gic_logger_n4me = logger; + + class Exception : public std::runtime_error { + public: + explicit Exception(const std::string& msg) : std::runtime_error(msg) {} + }; + }; + + bool pEp::String::log_enabled{ true }; + + std::ostream& operator<<(std::ostream& o, const pEp::String& pEpStr) + { + return o << std::string(pEpStr); + } + + //--------------------------------------------------------------------------------------------- + + // TOOD: + // ctor not exception safe + class Identity { + public: + Identity( + const std::string& address = "", + const std::string& username = "", + const std::string& user_id = "", + const std::string& fpr = "") + { + pEpLogClass("called"); + _wrappee = ::new_identity(nullptr, nullptr, nullptr, nullptr); + + // set the pEp::String wrapper underlying c_str + this->address.initialize(&_wrappee->address); + this->username.initialize(&_wrappee->username); + this->user_id.initialize(&_wrappee->user_id); + this->fpr.initialize(&_wrappee->fpr); + + // set the values + this->address = address; + this->username = username; + this->user_id = user_id; + this->fpr = fpr; + } + + ~Identity() + { + _free(); + } + + + pEp::String address{}; + pEp::String username{}; + pEp::String user_id{}; + pEp::String fpr{}; + + operator ::pEp_identity*() + { + return _wrappee; + } + + static bool log_enabled; + + private: + void _free() + { + // pEp::free(_wrappee); + } + + ::pEp_identity* _wrappee{ nullptr }; + Adapter::pEpLog::pEpLogger logger{ "IdentWrappySP", log_enabled }; + Adapter::pEpLog::pEpLogger& m4gic_logger_n4me = logger; + }; + + bool Identity::log_enabled{ true }; + +} // namespace pEp + + +::PEP_SESSION session; + +void test_getters(char const* const* const c_str_p, pEp::String& pstr) +{ + pEpLog("to_string(): " + pstr.to_string()); + assert(pstr.c_data() == *c_str_p); + assert(pstr.data() == c_str_p); + + if (*c_str_p != nullptr) { + std::string tmp{ pstr }; + pEpLog("operator std::string(): " + tmp); + assert(tmp == std::string(*c_str_p)); + + // will segfault with nullptr, and this is correct + std::string tmp2{ *pstr.data() }; + pEpLog("data(): " + tmp2); + assert(tmp2 == std::string(*c_str_p)); + + std::string tmp3{ pstr.c_data() }; + pEpLog("c_data(): " + tmp2); + assert(tmp3 == std::string(*c_str_p)); + } else { + std::string tmp{ pstr }; + pEpLog("operator std::string(): " + tmp); + assert(tmp == ""); + } +} + +void test_assign_and_getters(char** c_str_p, pEp::String& pstr) +{ + test_getters(c_str_p, pstr); + pEpLogH2("assign operator"); + pstr = "assign operator"; + test_getters(c_str_p, pstr); + + pEpLogH2("raw c_str assign"); + *c_str_p = strdup("raw c_str assign"); + test_getters(c_str_p, pstr); +} + +int main() +{ + // pEp::Utils::readKey(); + pEp::Adapter::pEpLog::set_enabled(true); + + // pEp::String::log_enabled = false; + if (1) { + // INVALID USAGE + // char* c_str_u; // uninitialized == INVALID + // undefined behaviour, most likely "pointer being freed was not allocated" + // { + // char* c_str; + // pEp::String pstr(c_str); + // } + // char* c_str_s = "fdsfs"; // statically initialized == INVALID + // { + // char ca[4] = { 'p', 'E', 'p'}; + // char* c_str = ca; + // pEp::String pstr(c_str); + // } + + // VALID USAGE + // new pEp::String on char* pointing to NULL + { + pEpLogH1("new pEp::String on char* pointing to NULL"); + char* c_str = NULL; // nullptr + + pEp::String pstr(&c_str); + test_assign_and_getters(&c_str, pstr); + } + + // new pEp::String on already initalized char* + { + pEpLogH1("new pEp::String on already initalized char*"); + char* c_str = strdup("initialized c string"); + + pEp::String pstr(&c_str); + test_assign_and_getters(&c_str, pstr); + } + + // initialize() + { + pEpLogH1("initialize()"); + pEp::String pstr{}; + + char* c_str = strdup("initialized c string"); + //TODO: PITYASSERT_THROWS + pstr.initialize(&c_str); + + test_assign_and_getters(&c_str, pstr); + } + } + + setenv("HOME", ".", 1); + ::init(&session, nullptr, nullptr, nullptr); + + // create identity + pEp::Identity id1{ "wrong@entry.lol", "wrong", "23", "INVA_FPR" }; + + pEpLog(*id1); + id1.username = "alice"; + id1.address = "alice@peptest.org"; + pEpLog(id1.address); + pEpLog(id1.username); + ::myself(session, id1); + pEpLog(*id1); + + pEp::Identity id2{ "bob" }; + ::update_identity(session, id2); + pEpLog(*id2); +}