|
@ -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()); |