diff --git a/CMakeLists.txt b/CMakeLists.txt index c908c96..33fad03 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,6 +82,10 @@ if(SNPLIB_BUILD_EXAMPLES) set(EXAM_LOGGER logger_example) add_executable(${EXAM_LOGGER} examples/logger_exam.cpp) target_link_libraries(${EXAM_LOGGER} ${PROJECT_NAME}) + + set(EXAM_NET net_example) + add_executable(${EXAM_NET} examples/net_exam.cpp) + target_link_libraries(${EXAM_NET} ${PROJECT_NAME}) endif() include(GNUInstallDirs) diff --git a/examples/net_exam.cpp b/examples/net_exam.cpp new file mode 100644 index 0000000..7d6921b --- /dev/null +++ b/examples/net_exam.cpp @@ -0,0 +1,59 @@ +#include +#include + +#include + + +int main() +{ + snplib::snplib_netproto_stopseq_t<> proto; + + std::string bytes{"SET VAL1 10\n"}; + std::vector bytes_vec{std::from_range, bytes}; + std::list bytes_lst{std::from_range, bytes}; + + std::println("BYTES VEC: {}", bytes_vec); + + auto msg = proto.fromBytes(bytes_vec); + + if (msg) { + std::println("MSG (from std::vector): {}", msg.value()); + } else { + std::println("MSG ERR: {}", msg.error().message()); + } + + auto msg_l = proto.fromBytes(bytes_lst); + + if (msg_l) { + std::println("MSG (from std::list): {}", msg_l.value()); + } else { + std::println("MSG ERR: {}", msg_l.error().message()); + } + + auto vmsg = proto.fromBytes>(bytes_lst); + if (vmsg) { + std::println("VMSG (from std::list): {}", vmsg.value()); + } else { + std::println("VMSG ERR: {}", vmsg.error().message()); + } + + + std::println("\n\n"); + + bytes = "GET VAL2 1"; + bytes_vec = {std::from_range, bytes}; + bytes_lst = {std::from_range, bytes}; + + std::println("MSG: ", bytes_vec); + + auto bs = proto.toBytes(bytes_vec); + std::println("BYTES (from std::vector): {}", bs); + + auto bs1 = proto.toBytes(bytes_lst); + std::println("BYTES (from std::list): {}", bs1); + + bytes_vec = proto.toBytes(bytes_lst); + std::println("BYTES: {}", bytes_vec); + + return 0; +} \ No newline at end of file diff --git a/include/snipplib/network/snplib_netproto.h b/include/snipplib/network/snplib_netproto.h index ba0845c..575437b 100644 --- a/include/snipplib/network/snplib_netproto.h +++ b/include/snipplib/network/snplib_netproto.h @@ -12,12 +12,22 @@ namespace snplib { + +/* SIMPLE STOP-SEQUENCE PROTOCOL */ + +/* + A valid record consists of a byte-sequence ending with given stop-sequence of bytes + e.g.: + SET ATTR 2\n\r\n + where "\n\r\n" - the stop-sequence +*/ + static constexpr char SNPLIB_DEFAULT_NETPROTO_STOP_SEQ[] = "\n"; template + requires(snplib_char_array_size(STOP_SEQ) > 0) struct snplib_netproto_stopseq_t { - static constexpr std::string_view NETPROTO_STOP_SEQ{ - snplib_char_array_size(STOP_SEQ) ? STOP_SEQ : SNPLIB_DEFAULT_NETPROTO_STOP_SEQ}; + static constexpr std::string_view NETPROTO_STOP_SEQ{STOP_SEQ}; // returns 2-element range of the input messsage and stop-sequence template @@ -50,7 +60,8 @@ struct snplib_netproto_stopseq_t { if constexpr (std::ranges::contiguous_range) { return std::array{std::span{message}, std::span{NETPROTO_STOP_SEQ}}; } else { - return std::array{std::ranges::subrange{message}, std::ranges::subrange{NETPROTO_STOP_SEQ}}; + // return std::array{std::ranges::subrange(message), std::ranges::subrange(NETPROTO_STOP_SEQ)}; + return std::make_tuple(std::ranges::subrange(message), std::ranges::subrange(NETPROTO_STOP_SEQ)); } } @@ -59,42 +70,99 @@ struct snplib_netproto_stopseq_t { { auto found = std::ranges::search(std::forward(bytes), NETPROTO_STOP_SEQ); if (found.empty()) { - return std::unexpected(std::errc::invalid_argument); + return std::unexpected(std::make_error_code(std::errc::invalid_argument)); } - if constexpr (std::ranges::contiguous_range) { - if constexpr (std::ranges::view) { - return OR(bytes.begin(), found.begin()); // return without stop-sequence!!! - } else if constexpr (snplib_output_char_range_c) { + using inp_t = std::remove_cvref_t; + + // return a view + static constexpr bool ret_view = + std::ranges::view && std::ranges::viewable_range && + (std::constructible_from, std::ranges::iterator_t> || + std::constructible_from, std::ranges::sentinel_t>); + + + if constexpr (ret_view) { + return OR(bytes.begin(), found.begin()); // return without stop-sequence!!! + } else if constexpr (snplib_output_char_range_c) { + if constexpr (!snplib_fixed_size_range_c) { + if constexpr (std::constructible_from, + std::ranges::iterator_t>) { + return OR(bytes.begin(), found.begin()); // return without stop-sequence!!! + } else if constexpr (std::is_default_constructible_v) { + OR res; + std::ranges::copy( + std::forward(bytes) | std::views::take(std::distance(bytes.begin(), found.begin())), + std::back_inserter(res)); + + return res; + } else { + static_assert(false, "Unsupported output byte sequence type!"); + } + } else { + auto Nelem = std::distance(bytes.begin(), found.begin()); + if (Nelem > std::tuple_size_v) { + static_assert(false, "Invalid size of the output byte sequence type!"); + } + OR res; - std::ranges::copy( - std::forward(bytes) | std::views::take(std::distance(bytes.begin(), found.begin())), - std::back_inserter(res)); + auto data_it = bytes.begin(); + for (auto it = res.begin(); it != res.end(); std::advance(it, 1)) { + *it = *data_it; + std::advance(data_it, 1); + } return res; - } else { - static_assert(false, "Unsupported output byte sequence type!"); - } - } else { - } - - if constexpr (snplib_output_char_range_c) { - OR res; - std::ranges::copy(std::forward(bytes) | std::views::take(std::distance(bytes.begin(), found.begin())), - std::back_inserter(res)); // return without stop-sequence!!! - - return res; - } else if constexpr (snplib_char_view_c) { - if constexpr (std::ranges::contiguous_range) { - return OR(bytes.begin(), found.begin()); // return without stop-sequence!!! - } else { - static_assert(false, "Unsupported output byte sequence type!"); } } else { static_assert(false, "Unsupported output byte sequence type!"); } } + + template + auto fromBytes(IR&& bytes) + requires snplib_input_char_range_c> + { + using or_t = std::conditional_t, std::span, + std::ranges::subrange>>; + + return fromBytes(std::forward(bytes)); + } }; +/* BINARY BLOB PROTOCOL */ + +/* + * a sequence of bytes in format: + * + * + blob size field: a symbolic representation of a HEXIMAL UNSIGNED INTEGER (e.g. "000000FA", MUST BE IN + UPPER-CASE!!!) + */ + +template + requires(SZ_FIELD_WIDTH > 0 && + SZ_FIELD_WIDTH <= 16) // not greater than allowed for 64-bit unsigned integer representation +struct snplib_netproto_binary_blob_t { + static constexpr std::array SZ_FIELD_FORMAT{'{', ':', '0', '0' + SZ_FIELD_WIDTH, 'X', '}'}; + + template + auto toBytes(IR&& message) + { + static thread_local std::string Nbytes{}; + + Nbytes.clear(); + // std::format_to(std::back_inserter(Nbytes), "{:08X}", std::ranges::size(std::forward(message))); + std::format_to(std::back_inserter(Nbytes), std::string_view(SZ_FIELD_FORMAT), + std::ranges::size(std::forward(message))); + + if constexpr (std::ranges::contiguous_range) { + return std::array{std::span{Nbytes}, std::span{message}}; + } else { + return std::make_tuple(std::ranges::subrange(Nbytes), std::ranges::subrange(message)); + } + } +}; + } // namespace snplib \ No newline at end of file