diff --git a/Makefile b/Makefile index 6c9e79f..e095f18 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ CXX= clang++ -CXXFLAGS= --std=c++20 -O0 -g -pthread -DGP_TESTS -DFUZZ_STRENGTH=100000 -DNO_BENCH=0 -pedantic \ +CXXFLAGS= --std=c++20 -O0 -g -pthread -DGP_TESTS -DFUZZ_STRENGTH=100 -DNO_BENCH=1 -pedantic \ -fprofile-instr-generate -fcoverage-mapping -Wno-unknown-attributes -fno-omit-frame-pointer \ # -fsanitize=address -fsanitize-blacklist=blacklist.txt diff --git a/include/gp/containers/buffer.hpp b/include/gp/containers/buffer.hpp index cf13227..6f5bfd8 100644 --- a/include/gp/containers/buffer.hpp +++ b/include/gp/containers/buffer.hpp @@ -17,9 +17,22 @@ namespace gp{ private: pointer_iterator begin_elem; pointer_iterator end_elem; + + + static constexpr size_t local_strlen(const char* str) { + auto ptr = str; + do{}while(*(ptr++)); + return ptr-str; + } + public: using associated_iterator = pointer_iterator; + constexpr buffer(const char* val) requires std::is_same_v + : begin_elem{const_cast(val)} + , end_elem{const_cast(val)+local_strlen(val)} + {} + constexpr buffer(T* beg_ptr, T* end_ptr) : begin_elem{beg_ptr} , end_elem{end_ptr} diff --git a/include/gp/containers/dynarray.hpp b/include/gp/containers/dynarray.hpp new file mode 100644 index 0000000..6a3dd13 --- /dev/null +++ b/include/gp/containers/dynarray.hpp @@ -0,0 +1,185 @@ +#pragma once + +#include "gp/containers/array.hpp" + +namespace gp { + + template + class dynarray { + struct build_order{}; + + struct v_element{ + char data[sizeof(T)]; + + operator T&() { + return *(T*)data; + } + + v_element() + : data{} + {} + + template + v_element(build_order, Args&&... argv) { + new(data) T(gp::forward(argv)...); + } + + template + T& operator=(arg& argv) { + return *(T*)data = argv; + } + + template + T& operator=(arg&& argv) { + return *(T*)data = gp::forward(argv); + } + + template + void build(Args&&... argv) { + new(data) T(gp::forward(argv)...); + } + + T& value() { + return *(T*)data; + } + + T& value() const { + return *(T*)data; + } + + void clear(){ + ((T*)data)->~T(); + } + }; + + using associated_iterator = pointer_iterator; + using associated_const_iterator = const_pointer_iterator; + using associated_riterator = pointer_iterator; + using associated_const_riterator = const_pointer_iterator; + + v_element data[cap]; + size_t sz = 0; + + public: + dynarray() = default; + + dynarray(dynarray& oth) + { + for(auto& ref : oth) { + push_back(ref); + } + } + + /*dynarray(dynarray&& oth) + { + for(auto& ref : oth) { + emplace_back(gp::forward(ref)); + } + }*/ + + constexpr associated_iterator begin() + { + return associated_iterator((T*)&data[0]); + } + + constexpr associated_iterator end() + { + return associated_iterator((T*)&data[sz]); + } + + constexpr associated_const_iterator cbegin() const + { + return associated_const_iterator((T*)&data[0]); + } + + constexpr associated_const_iterator cend() const + { + return associated_const_iterator((T*)&data[sz]); + } + + constexpr associated_riterator rbegin() + { + return associated_riterator((T*)&data[sz-1]); + } + + constexpr associated_riterator rend() + { + return associated_riterator((T*)data-1); + } + + constexpr associated_const_riterator crbegin() const + { + return associated_const_riterator((T*)&data[sz-1]); + } + + constexpr associated_const_riterator crend() const + { + return associated_const_riterator((T*)data-1); + } + + constexpr bool operator==(const dynarray& oth) const + { + if(size() != oth.size()) return false; + for(size_t idx = 0; idx + void emplace_back(Args&&... argv) { + data[sz].build(gp::forward(argv)...); + ++sz; + } + + void remove_back() { + --sz; + data[sz].clear(); + } + + ~dynarray() { + auto it = data; + auto end = data + size(); + while(it != end) { + (*it).clear(); + ++it; + } + } + }; + +} \ No newline at end of file diff --git a/include/gp/functional/optional.hpp b/include/gp/functional/optional.hpp index 67a3ab7..999bc21 100644 --- a/include/gp/functional/optional.hpp +++ b/include/gp/functional/optional.hpp @@ -17,7 +17,7 @@ namespace gp{ template class optional final{ bool ready = false; - char buffer[sizeof(T)]; + alignas(T) char buffer[sizeof(T)]; public: constexpr optional() : ready{false} @@ -36,7 +36,7 @@ namespace gp{ constexpr optional(T&& value) : ready{true} { - new(buffer) T(gp::move(value)); + new(buffer) T(gp::forward(value)); } ~optional() { @@ -65,16 +65,16 @@ namespace gp{ optional& operator=(T&& value) { if(ready) { - *(T*)buffer = gp::move(value); + *(T*)buffer = gp::forward(value); } else { ready = true; - new(buffer) T(gp::move(value)); + new(buffer) T(gp::forward(value)); } return *this; } - constexpr bool has_value() + constexpr bool has_value() const { return ready; } @@ -92,5 +92,19 @@ namespace gp{ } return *reinterpret_cast(buffer); } + + constexpr const T& value() const + { + if constexpr (gp_config::has_exceptions) + { + if(!ready) + { + throw bad_optional{}; + } + } else { + gp_config::assertion(ready, "bad optional access"); + } + return *reinterpret_cast(buffer); + } }; } \ No newline at end of file diff --git a/include/gp/functional/variant.hpp b/include/gp/functional/variant.hpp index db7108d..8df87ff 100644 --- a/include/gp/functional/variant.hpp +++ b/include/gp/functional/variant.hpp @@ -23,7 +23,7 @@ namespace gp{ template class fixed_variant final { std::size_t index = std::numeric_limits::max(); - char buffer[max_size()]; + alignas(gp::max(alignof(T)...)) char data[max_size()]; gp::function cpytor = {[](void*, void*){}, nullopt}; gp::function mvtor = {[](void*, void*){}, nullopt}; gp::function dtor = {[](void*){}, nullopt}; @@ -54,7 +54,7 @@ namespace gp{ mvtor = gp::function([](void* src, void* dest){ new(dest) actual(gp::move(*(actual*)src)); }, nullopt); - cpytor((char*)&value, buffer); + cpytor((char*)&value, data); } static_assert(list_contains_class::value, "list_contains_class doesn't work properly"); @@ -83,7 +83,7 @@ namespace gp{ mvtor = gp::function([](void* src, void* dest){ new(dest) actual(gp::move(*(actual*)src)); }, nullopt); - mvtor((char*)&value, buffer); + mvtor((char*)&value, data); } fixed_variant(const fixed_variant& oth) @@ -92,7 +92,7 @@ namespace gp{ , cpytor{oth.cpytor} , mvtor{oth.mvtor} { - cpytor((char*)oth.buffer, (char*)buffer); + cpytor((char*)oth.data, (char*)data); } fixed_variant(fixed_variant& oth) @@ -101,7 +101,7 @@ namespace gp{ , cpytor{oth.cpytor} , mvtor{oth.mvtor} { - cpytor(oth.buffer, buffer); + cpytor(oth.data, data); } fixed_variant(fixed_variant&& oth) @@ -111,7 +111,7 @@ namespace gp{ , mvtor{oth.mvtor} { oth.index = std::numeric_limits::max(); - mvtor(oth.buffer, buffer); + mvtor(oth.data, data); } /** @@ -140,27 +140,27 @@ namespace gp{ { if(index != std::numeric_limits::max()) { - dtor((void*)buffer); + dtor((void*)data); } index = value.index; cpytor = value.cpytor; dtor = value.dtor; mvtor = value.mvtor; - cpytor(value.buffer, buffer); + cpytor(value.data, data); } void operator=(fixed_variant&& value) { if(index != std::numeric_limits::max()) { - dtor((void*)buffer); + dtor((void*)data); } dtor = value.dtor; cpytor = value.cpytor; mvtor = value.mvtor; index = value.index; value.index = std::numeric_limits::max(); - mvtor(value.buffer, buffer); + mvtor(value.data, data); } template::value,int>::type> @@ -168,10 +168,10 @@ namespace gp{ { if(index != std::numeric_limits::max()) { - dtor((void*)buffer); + dtor((void*)data); } index = r_index_of, T...>::value; - new(buffer) U(value); + new(data) U(value); dtor = gp::function([](void* thing){((U*)thing)->~U();}, nullopt); cpytor = gp::function([](void* src, void* dest){new(dest) U(*(U*)src);}, nullopt); mvtor = gp::function([](void* src, void* dest){new(dest) U(gp::move(*(U*)src));}, nullopt); @@ -182,9 +182,9 @@ namespace gp{ { if(index != std::numeric_limits::max()) { - dtor((void*)buffer); + dtor((void*)data); } - new(buffer) U(gp::move(value)); + new(data) U(gp::move(value)); index = r_index_of, T...>::value; dtor = gp::function([](void* thing){((U*)thing)->~U();}, nullopt); cpytor = gp::function([](void* src, void* dest){new(dest) U(*(U*)src);}, nullopt); @@ -195,7 +195,7 @@ namespace gp{ { if(index != std::numeric_limits::max() && dtor.ready()) { - dtor((void*)buffer); + dtor((void*)data); index = std::numeric_limits::max(); } } @@ -217,7 +217,7 @@ namespace gp{ throw bad_variant_access{}; } } - return *reinterpret_cast(buffer); + return *reinterpret_cast(data); } /** diff --git a/include/gp/system/logging_segment.hpp b/include/gp/system/logging_segment.hpp new file mode 100644 index 0000000..87e97e5 --- /dev/null +++ b/include/gp/system/logging_segment.hpp @@ -0,0 +1,126 @@ +#pragma once + +#include "gp/algorithms/min_of.hpp" +#include "gp/containers/dynarray.hpp" +#include "gp/containers/vector.hpp" +#include "gp/functional/optional.hpp" + +#include + +struct segment{ + int16_t priority = 0; + gp::dynarray name; + gp::dynarray text; +}; + +struct virtual_logging_segment{ + virtual size_t push_segment(gp::buffer name, gp::buffer text, int16_t prio = 0) = 0; + virtual void set_segment(gp::buffer name, gp::buffer text, int16_t prio = 0) = 0; + virtual gp::optional> get_segment(gp::buffer name) = 0; + virtual size_t size() = 0; + virtual segment& get_segment_by_idx(size_t) = 0; + virtual void clear() = 0; +}; + + +template +struct static_logging_segment : virtual_logging_segment{ + gp::dynarray data; + gp::optional wrap_around; + gp::function on_destruct = +[](virtual_logging_segment&){}; + + ~static_logging_segment() { + on_destruct(*this); + } + + segment prepare_segment(gp::buffer name, gp::buffer text, int prio = 0) { + segment prepared; + prepared.priority = prio; + { + auto it = name.begin(); + auto end = name.begin()+gp::min(name.size(), prepared.name.capacity()); + while(it != end) { + prepared.name.push_back(*it); + ++it; + } + } + { + auto it = text.begin(); + auto end = text.begin()+gp::min(text.size(), prepared.text.capacity()); + while(it != end) { + prepared.text.push_back(*it); + ++it; + } + if(text.size()>prepared.text.capacity()) { + auto it = prepared.text.rbegin(); + *it = '.'; ++it; + *it = '.'; ++it; + *it = '.'; + } + } + + return prepared; + } + + virtual size_t push_segment(gp::buffer name, gp::buffer text, int16_t prio = 0) { + auto prepared = prepare_segment(name, text, prio); + + if(wrap_around.has_value()) { + size_t idx = wrap_around.value(); + size_t override_prio = gp::min_of(data.begin(), data.end(), [](auto v){ return v.priority; }); + while(true) { + if(data[idx].priority == override_prio) { + data[idx] = prepared; + break; + } + idx = (++idx) % data.size(); + } + idx = (++idx) % data.size(); + wrap_around = idx; + return idx-1; + } else { + data.push_back(prepared); + return data.size()-1; + } + } + virtual void set_segment(gp::buffer name, gp::buffer text, int16_t prio = 0) { + auto prepared = prepare_segment(name, text, prio); + + if(auto seg = get_segment(name); seg.has_value()) { + seg.value() = prepared; + } else { + data.push_back(prepared); + } + } + virtual gp::optional> get_segment(gp::buffer name) { + decltype(segment{}.name) tname; + auto it = name.begin(); + auto end = name.begin()+gp::min(name.size(), tname.capacity()); + while(it != end) { + tname.push_back(*it); + ++it; + } + + for(auto& elem : data) { + if(elem.name == tname) { + return gp::reference_wrapper(elem); + } + } + return gp::nullopt; + } + virtual size_t size() { + return data.size(); + } + virtual segment& get_segment_by_idx(size_t idx) { + return data[idx]; + } + + virtual void clear() { + data.~dynarray(); + new(&data) decltype(data){}; + } +}; + +/*struct dynamic_logging_segment{ + +};*/ \ No newline at end of file diff --git a/include/gp/utils/pointers.hpp b/include/gp/utils/pointers.hpp index d1aee04..c283d5f 100644 --- a/include/gp/utils/pointers.hpp +++ b/include/gp/utils/pointers.hpp @@ -6,6 +6,7 @@ #include "gp/functional/function.hpp" #include +#include namespace gp { @@ -165,4 +166,101 @@ namespace gp { } }; + template + struct resource_traits { + using identifier = uint64_t; + static constexpr bool create_if_not_exist = false; + + static T* create(const identifier&, gp::allocator&); + + static T* retrieve(const identifier&, gp::allocator&); + + static bool imprint(const identifier&, const T& value); + + static bool annihilate(const identifier&); + }; + + template + requires std::copyable::identifier> + class resource_ptr + { + typename resource_traits::identifier _identifier; + gp::allocator& _alloc; + T* value = nullptr; + + operator T&() { + if(!value) { + value = resource_traits::retrieve(_identifier, _alloc); + } + if(value) return *value; + else if constexpr (resource_traits::create_if_not_exist) { + value = resource_traits::create(_identifier, _alloc); + if(value) return *value; + } + gp_config::assertion(false, "unavailable resource lead to crash"); + } + + gp::optional read() { + if(!value) { + value = resource_traits::retrieve(_identifier, _alloc); + } + if(value) return *value; + else if constexpr (resource_traits::create_if_not_exist) { + value = resource_traits::create(_identifier, _alloc); + if(value) return *value; + } + return gp::nullopt; + } + + gp::optional force_read() { + value = resource_traits::retrieve(_identifier, _alloc); + if(value) return *value; + return gp::nullopt; + } + + template + requires std::is_base_of_v + gp::optional operator=(U& new_value) { + if(resource_traits::imprint(_identifier, new_value)) { + if(value) { + value->~T(); + gp_config::assertion(_alloc.deallocate((void*)value), "deallocation failure during exchange"); + } + value = _alloc.allocate(sizeof(U)); + gp_config::assertion(value != nullptr, "could not allocate during resource transcription"); + new(value) U(new_value); + return *value; + } + return gp::nullopt; + } + + template + requires std::is_base_of_v + gp::optional operator=(const U& new_value) { + if(resource_traits::imprint(_identifier, new_value)) { + if(value) { + value->~T(); + gp_config::assertion(_alloc.deallocate((void*)value), "deallocation failure during exchange"); + } + value = _alloc.allocate(sizeof(U)); + gp_config::assertion(value != nullptr, "could not allocate during resource transcription"); + new(value) U(new_value); + return *value; + } + return gp::nullopt; + } + + + template<> + gp::optional operator=(const gp::nullopt_t) { + if(resource_traits::annihilate(_identifier)) { + if(value) { + value->~T(); + gp_config::assertion(_alloc.deallocate((void*)value), "deallocation failure during exchange"); + } + value = nullptr; + } + return gp::nullopt; + } + }; } \ No newline at end of file diff --git a/include/gp_config.hpp b/include/gp_config.hpp index e04f64f..9fd5c07 100644 --- a/include/gp_config.hpp +++ b/include/gp_config.hpp @@ -15,6 +15,9 @@ namespace gp { } #endif +extern void log_failure(const char* failure); +extern void log_segment(const char* name, const char* text, int16_t prio = 0); + /** * @brief This namespace contains the configuration. * @@ -90,6 +93,8 @@ namespace gp_config{ constexpr size_t hardware_constructive_interference_size = 128; constexpr size_t hardware_destructive_interference_size = 128; #endif + + constexpr size_t loggers_segment_size = 128; } namespace memory_module{ @@ -140,8 +145,36 @@ namespace gp_config{ * @brief an assertion function */ constexpr auto assertion = [](bool pred, const char* reason) -> void{ - if constexpr (has_exceptions) - if(!pred) throw assert_failure(reason); + if(!pred) { + log_failure(reason); + if constexpr (has_exceptions) + throw assert_failure(reason); + } + }; + + enum class cbor_tag { + datetime = 0, + unix_time = 1, + ubignum = 2, + nbignum = 3, + decimal = 4, + bigfloat = 5, + cose_encrypt0 = 16, + cose_mac0 = 17, + cose_sign1 = 18, + expected_base64url = 21, + expected_base64 = 22, + expected_base16 = 23, + encoded_cbor = 24, + url = 32, + base64url = 33, + base64 = 34, + regexp = 35, + mime = 36, + cose_encrypt = 96, + cose_mac = 97, + cose_sign = 98, + signature = 55799 }; } diff --git a/tests.cpp b/tests.cpp index 0359fbd..b668725 100644 --- a/tests.cpp +++ b/tests.cpp @@ -1,5 +1,7 @@ #include "gp_config.hpp" +#include "gp/system/logging_segment.hpp" + #include "allocator.hpp" #include "test_scaffold.h" @@ -8,6 +10,32 @@ alignas(2048) gp::array static_mapper::store; gp::buddy<> static_mapper::impl = gp::buddy<>{store.begin().data, store.size()}; +static_logging_segment<32> logger; + +void log_failure(const char* failure) { + log_segment("FAILURE", failure, std::numeric_limits::max()); +} + +void log_segment(const char* name, const char* text, int16_t prio) { + logger.push_segment(name, text, prio); +} + +void print_logs() { + auto end = logger.size(); + for(size_t idx = 0; idx < end; idx++) { + auto seg = logger.get_segment_by_idx(idx); + std::cout << "\t"; + for(char c : seg.name) { + std::cout << c; + } + std::cout << ":\t"; + for(char c : seg.text) { + std::cout << c; + } + std::cout << "\n"; + } +} + std::vector> tests; int main() @@ -26,14 +54,18 @@ int main() } } catch (gp::runtime_error err) { std::cout << test->name << " failed with an exception: " << err.what() << std::endl; + print_logs(); value = -1; } catch (gp_config::assert_failure err) { std::cout << test->name << " failed with an assertion failure: " << err.what() << std::endl; + print_logs(); value = -1; } catch (...) { std::cout << test->name << " failed with an exception" << std::endl; + print_logs(); value = -1; } + logger.clear(); failed += (value != 0); } std::cout << std::dec << "Runned "< +#include +#include +#include +#include + + +struct logging_test : public test_scaffold { + logging_test() { + name = __FILE__ ":1"; + } + + + virtual int run() { + int res = 0; + + const gp::buffer name{"FIRST TEST"}; + const gp::buffer text{"FIRST TEST TEXT"}; + + static_logging_segment<4> logger; + + return res; + } +}; + +append_test dummy_rduhk786f(new logging_test{});