| @ -0,0 +1,236 @@ | |||
| #include "test_scaffold.h" | |||
| #include "gp/containers/array.hpp" | |||
| #include "gp/containers/vector.hpp" | |||
| #include "gp/ipc/envelope/cbor.hpp" | |||
| #include "gp/algorithms/tmp_manip.hpp" | |||
| #include "gp/functional/monostates.hpp" | |||
| #include "gp/utils/allocators/allocator.hpp" | |||
| #include <string> | |||
| #include <unordered_map> | |||
| extern void print_logs(); | |||
| struct std_allocator : gp::allocator { | |||
| using dbg_map = std::unordered_map<void*, size_t>; | |||
| [[no_unique_address]] dbg_map debug_info; | |||
| /** | |||
| * @brief Allocates memory | |||
| * | |||
| * @param sz the amount of bytes to allocate | |||
| * | |||
| * @return the allocated memory as a pointer on success | |||
| * @return nullptr if it failed allocating | |||
| */ | |||
| virtual void* allocate(size_t sz) { | |||
| auto v = new char[sz]; | |||
| log_segment("data allocated", (std::to_string(sz) + " bytes @"+std::to_string((uintptr_t)v)).c_str()); | |||
| debug_info[v] = sz; | |||
| return v; | |||
| } | |||
| /** | |||
| * @brief Deallocates memory | |||
| * | |||
| * @param ptr the memory to deallocate | |||
| * | |||
| * @return true if the memory was successfully deallocated | |||
| * @return false if the memory was not deallocated | |||
| */ | |||
| virtual bool deallocate(void* ptr) { | |||
| log_segment("data deallocated", "Trying deallocation"); | |||
| log_segment("data deallocated", ("Target: @" + std::to_string((uintptr_t)ptr)).c_str()); | |||
| if(!debug_info.contains(ptr)) | |||
| gp_config::assertion(false, "Double free detected"); | |||
| print_logs(); | |||
| delete (char*)ptr; | |||
| log_segment("data deallocated", (std::to_string(debug_info[ptr]) + " bytes @"+std::to_string((uintptr_t)ptr)).c_str()); | |||
| debug_info.erase(ptr); | |||
| return true; | |||
| } | |||
| /** | |||
| * @brief Tries to reallocate memory | |||
| * | |||
| * @param ptr The memory to reallocate | |||
| * @param sz The new size we want to give the memory | |||
| * | |||
| * @return true if reallocation was successful | |||
| * @return false if the reallocation failed | |||
| */ | |||
| virtual bool try_reallocate(void*, size_t) { | |||
| return false; | |||
| } | |||
| virtual ~std_allocator() = default; | |||
| }; | |||
| std_allocator allocator; | |||
| enum class co_state { | |||
| waiting_receive, | |||
| waiting_send, | |||
| data_received, | |||
| data_sent | |||
| }; | |||
| typedef gp::array<char, 16> identifier_t; | |||
| using fd_t = int; | |||
| struct packet_info { | |||
| uint64_t attempts = 0; | |||
| uint64_t sequence = 0; | |||
| identifier_t co_id; | |||
| gp::vector<char> data{allocator}; | |||
| }; | |||
| gp::vector<char>& push_as_cbor(gp::vector<char>& src, identifier_t& value) { | |||
| gp::push_as_cbor(src, gp::cbor_tag_initiator{.as_integer = 37}); | |||
| gp::push_as_cbor(src, value.as_buffer()); | |||
| return src; | |||
| } | |||
| gp::vector<char>& push_as_cbor(gp::vector<char>& src, packet_info& value) { | |||
| push_as_cbor(src, gp::cbor_tag_initiator{.as_integer = 1994}); | |||
| push_as_cbor(src, gp::cbor_array_initiator{4}); | |||
| push_as_cbor(src, value.sequence); | |||
| push_as_cbor(src, value.attempts); | |||
| push_as_cbor(src, value.co_id); | |||
| push_as_cbor(src, value.data.as_buffer()); | |||
| return src; | |||
| } | |||
| namespace gp { template<> | |||
| gp::pair<gp::optional<packet_info>, parsing_state> read_cbor<packet_info>(parsing_state state, gp::allocator& alloc) { | |||
| size_t seq, attempts; | |||
| identifier_t uuid; | |||
| gp::vector<char> data{alloc}; | |||
| bool ok = true; | |||
| auto array_parser = [&](parsing_state state, allocator& alloc){ | |||
| for(size_t idx : {0,1,2,3}) | |||
| switch(idx) { | |||
| case 0:{ | |||
| auto [v ,new_state] = read_cbor<uint64_t>(state, alloc); | |||
| if(v.has_value()) { | |||
| seq = v.value(); | |||
| return new_state; | |||
| } | |||
| }break; | |||
| case 1:{ | |||
| auto [v ,new_state] = read_cbor<uint64_t>(state, alloc); | |||
| if(v.has_value()) { | |||
| attempts = v.value(); | |||
| return new_state; | |||
| } | |||
| }break; | |||
| case 2:{ | |||
| auto [tg ,nc_state] = read_cbor<cbor_tag_initiator>(state, alloc); | |||
| if(!tg.has_value()) break; | |||
| if(tg.value().as_integer != 37) break; | |||
| auto [v ,new_state] = read_cbor<gp::vector<char>>(nc_state, alloc); | |||
| if(v.has_value()) { | |||
| for(size_t idx : {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}) { | |||
| uuid[idx] = v.value()[idx]; | |||
| } | |||
| return new_state; | |||
| } | |||
| }break; | |||
| case 3:{ | |||
| auto [v ,new_state] = read_cbor<gp::vector<char>>(state, alloc); | |||
| if(v.has_value()) { | |||
| data = v.value(); | |||
| return new_state; | |||
| } | |||
| }break; | |||
| } | |||
| ok &= false; | |||
| log_segment("ok", ok ? "true" : "false"); | |||
| return state; | |||
| }; | |||
| parsing_state nc_state = state; | |||
| { | |||
| auto [value, new_state] = read_cbor<cbor_tag_initiator>(nc_state, alloc); | |||
| if(!value.has_value()) return {gp::nullopt, state}; | |||
| nc_state = new_state; | |||
| } | |||
| { | |||
| auto new_state = read_cbor_array(nc_state, alloc, array_parser, [&](size_t sz){ | |||
| ok &= sz == 4; | |||
| log_segment("ok", ok ? "true" : "false"); | |||
| return ok; | |||
| }); | |||
| if(ok && new_state.size() != nc_state.size()) { | |||
| log_segment("INFO", "Parsing terminated successfully"); | |||
| return {packet_info{.attempts = attempts, .sequence = seq, .data = gp::move(data)}, new_state}; | |||
| } | |||
| else | |||
| return {nullopt, state}; | |||
| } | |||
| }} | |||
| struct packet_info_serialization_test : public test_scaffold { | |||
| packet_info_serialization_test() { | |||
| name = __FILE__ "@packet_info_serialization_test"; | |||
| } | |||
| virtual int run() { | |||
| packet_info item; | |||
| item.sequence = 1; | |||
| item.attempts = 2; | |||
| for(auto& c : item.co_id) c = 1; | |||
| for(auto idx : {0,1,2,3,4,5,6,7,8,9}) item.data.push_back(idx); | |||
| gp::vector<char> serialized(allocator); | |||
| push_as_cbor(serialized, item); | |||
| log_segment("size", std::to_string(serialized.size()).c_str()); | |||
| gp_config::assertion(serialized.size() == 36, "serialization of the wrong size"); | |||
| return 0; | |||
| } | |||
| }; | |||
| append_test packet_info_serialization_test_impl(new packet_info_serialization_test()); | |||
| struct packet_info_deserialization_test : public test_scaffold { | |||
| packet_info_deserialization_test() { | |||
| name = __FILE__ "@packet_info_deserialization_test"; | |||
| } | |||
| virtual int run() { | |||
| packet_info item; | |||
| item.sequence = 1; | |||
| item.attempts = 2; | |||
| for(auto& c : item.co_id) c = 1; | |||
| for(auto idx : {0,1,2,3,4,5,6,7,8,9}) item.data.push_back(idx); | |||
| gp::vector<char> serialized(allocator); | |||
| push_as_cbor(serialized, item); | |||
| log_segment("size", std::to_string(serialized.size()).c_str()); | |||
| gp_config::assertion(serialized.size() == 36, "serialization of the wrong size"); | |||
| auto [value, state] = gp::read_cbor<packet_info>(serialized.as_buffer(), allocator); | |||
| gp_config::assertion(value.has_value(), "deserialization failed"); | |||
| gp_config::assertion(!state.size(), "unprocessed state remains"); | |||
| gp_config::assertion(value.value().sequence == item.sequence, "comparison failed step 1"); | |||
| gp_config::assertion(value.value().attempts == item.attempts, "comparison failed step 2"); | |||
| gp_config::assertion(value.value().co_id == item.co_id, "comparison failed step 3"); | |||
| gp_config::assertion(value.value().data == item.data, "comparison failed step 4"); | |||
| return 0; | |||
| } | |||
| }; | |||
| append_test packet_info_deserialization_test_impl(new packet_info_deserialization_test()); | |||