diff --git a/src/net/connection_client.hpp b/src/net/connection_client.hpp index 61fcbb98801ec9f5599d0886ba730d347c36a196..a192249f3d68a01e599bf8bf35338c4bbf36b9fa 100644 --- a/src/net/connection_client.hpp +++ b/src/net/connection_client.hpp @@ -13,7 +13,8 @@ namespace rmrf::net { -class connection_client : public std::enable_shared_from_this<connection_client> { +template<class client> +class connection_client : public std::enable_shared_from_this<client> { public: /** * This function type accepts a reference to the incomming data string which it may not alter diff --git a/src/net/connection_line_buffer.cpp b/src/net/connection_line_buffer.cpp index 00ae1b8e4f0af093685ea08751c4156c43fb08d2..7650bda0a8c0fade862bc6996426bed132c486b3 100644 --- a/src/net/connection_line_buffer.cpp +++ b/src/net/connection_line_buffer.cpp @@ -38,61 +38,4 @@ std::string::size_type default_eol_search( return std::string::npos; } -connection_line_buffer::connection_line_buffer( - std::shared_ptr<connection_client> c, - found_next_line_cb_t found_next_line_cb_, - std::string::size_type max_line_size, - eol_search_t search_lb -) : - search(search_lb), - client(c), - found_next_line_cb(found_next_line_cb_), - max(max_line_size), - data("") -{ - this->client->set_incomming_data_callback( - [this](const std::string& data_in) { - conn_data_in_cb(data_in); - }); -} - -connection_line_buffer::connection_line_buffer( - std::shared_ptr<connection_client> c, - found_next_line_cb_t found_next_line_cb_, - std::string::size_type max_line_size -) : - connection_line_buffer{c, found_next_line_cb_, max_line_size, &default_eol_search} -{} - -void connection_line_buffer::conn_data_in_cb(const std::string &data_in) { - - // Iterate throug the incomming data as long as we find line breaks - for (std::string::size_type strpos = 0, nextpos = -1; strpos != std::string::npos; strpos = nextpos++) { - // Search for the next line break - nextpos = this->search(data_in, strpos); - - // Advance, if the line would be empty - if (nextpos == strpos) { - nextpos = this->search(data_in, ++strpos); - } - - if (nextpos == std::string::npos) { - // If we didn't find one we store the remaining data in the buffer - this->data += data_in.substr(strpos, data_in.length() - strpos); - break; - } else { - // If we find one we send the buffer plus the incomming data up to the line break to the callback - const std::string data_to_send = this->data + data_in.substr(strpos, nextpos - strpos); - this->found_next_line_cb(data_to_send, true); - // and clear the buffer - this->data = std::string(""); - } - } - - if (this->data.length() > this->max) { - this->found_next_line_cb(this->data, false); - this->data = std::string(""); - } -} - } diff --git a/src/net/connection_line_buffer.hpp b/src/net/connection_line_buffer.hpp index 0c1a140f25957553ba2f58f3e037efff43d56073..c25e1b5684b3b4572edefbd6128c33a35b729524 100644 --- a/src/net/connection_line_buffer.hpp +++ b/src/net/connection_line_buffer.hpp @@ -10,6 +10,7 @@ #include <functional> #include <memory> #include <string> +#include <sstream> #include "net/connection_client.hpp" @@ -58,6 +59,7 @@ std::string::size_type default_eol_search(const std::string& data, std::string:: * @see rmrf::net::default_eol_search * @see rmrf::net::eol_search_t */ +template<class client_type> class connection_line_buffer { public: /** @@ -72,10 +74,11 @@ public: private: const eol_search_t search; - std::shared_ptr<connection_client> client; + std::shared_ptr<connection_client<client_type>> client; found_next_line_cb_t found_next_line_cb; std::string::size_type max; - std::string data; + std::ostringstream data_buffer; + size_t buffer_length; public: /** @@ -86,7 +89,7 @@ public: * @param max_line_size The maximum line length to aquire prior to collection abort and transmission of the incomplete line * @param search_lb The callback to use for line break detection */ - connection_line_buffer(std::shared_ptr<connection_client> c, found_next_line_cb_t found_next_line_cb_, std::string::size_type max_line_size, eol_search_t search_lb); + connection_line_buffer(std::shared_ptr<connection_client<client_type>> c, found_next_line_cb_t found_next_line_cb_, std::string::size_type max_line_size, eol_search_t search_lb); /** * This constructor creates a new buffer based on a given client, an input callback and the maximum line size. @@ -96,10 +99,81 @@ public: * @param found_next_line_cb_ The callback to be called when a complete new line arrived * @param max_line_size The maximum line length to aquire prior to collection abort and transmission of the incomplete line */ - connection_line_buffer(std::shared_ptr<connection_client> c, found_next_line_cb_t found_next_line_cb_, std::string::size_type max_line_size); + connection_line_buffer(std::shared_ptr<connection_client<client_type>> c, found_next_line_cb_t found_next_line_cb_, std::string::size_type max_line_size); private: void conn_data_in_cb(const std::string& data_in); + void clear(); }; +template <class client_type> +connection_line_buffer<client_type>::connection_line_buffer( + std::shared_ptr<connection_client<client_type>> c, + found_next_line_cb_t found_next_line_cb_, + std::string::size_type max_line_size, + eol_search_t search_lb +) : + search(search_lb), + client(c), + found_next_line_cb(found_next_line_cb_), + max(max_line_size), + data_buffer(std::ostringstream::ate), + buffer_length{0} +{ + this->client->set_incomming_data_callback( + [this](const std::string& data_in) { + conn_data_in_cb(data_in); + }); +} + +template <class client_type> +connection_line_buffer<client_type>::connection_line_buffer( + std::shared_ptr<connection_client<client_type>> c, + found_next_line_cb_t found_next_line_cb_, + std::string::size_type max_line_size +) : + connection_line_buffer{c, found_next_line_cb_, max_line_size, &default_eol_search} +{} + +template <class client_type> +void connection_line_buffer<client_type>::clear() { + this->data_buffer.str(std::string()); + this->data_buffer.clear(); + this->buffer_length = 0; +} + +template <class client_type> +void connection_line_buffer<client_type>::conn_data_in_cb(const std::string& data_in) { + + // Iterate throug the incomming data as long as we find line breaks + for (std::string::size_type strpos = 0, nextpos = -1; strpos != std::string::npos; strpos = nextpos++) { + // Search for the next line break + nextpos = this->search(data_in, strpos); + + // Advance, if the line would be empty + if (nextpos == strpos) { + nextpos = this->search(data_in, ++strpos); + } + + if (nextpos == std::string::npos) { + // If we didn't find one we store the remaining data in the buffer + const auto length = data_in.length() - strpos; + this->data_buffer << data_in.substr(strpos, length); + this->buffer_length += length; + break; + } else { + // If we find one we send the buffer plus the incomming data up to the line break to the callback + const std::string data_to_send = this->data_buffer.str() + data_in.substr(strpos, nextpos - strpos); + this->found_next_line_cb(data_to_send, true); + // and clear the buffer + this->clear(); + } + } + + if (this->buffer_length > this->max) { + this->found_next_line_cb(this->data_buffer.str(), false); + this->clear(); + } +} + } diff --git a/src/net/tcp_client.hpp b/src/net/tcp_client.hpp index 180055b26515a4ffeb7c980620467fa0d707631c..366c859717199fb3fbbc596795131913a551a94c 100644 --- a/src/net/tcp_client.hpp +++ b/src/net/tcp_client.hpp @@ -32,7 +32,7 @@ enum class exit_status_t : uint16_t { * @author doralitze BenBe * @brief A raw TCP client. */ -class tcp_client : public connection_client, std::enable_shared_from_this<tcp_client> { +class tcp_client : public connection_client<tcp_client> { public: /** * A callback for the destructor must match this definition. diff --git a/test/connection_line_buffer_test.cpp b/test/connection_line_buffer_test.cpp index cb21581720079773f61112489cee1f6c1d89557f..c94fa33d689fb0507d75aabae227450e29169031 100644 --- a/test/connection_line_buffer_test.cpp +++ b/test/connection_line_buffer_test.cpp @@ -67,7 +67,7 @@ BOOST_AUTO_TEST_CASE(Default_EoL_Search_Test) { BOOST_AUTO_TEST_CASE(Connection_Line_Buffer_Test) { mut_send_stage = 0; auto ll_client = std::make_shared<loopback_connection_client>(mut_send_data_cb, false); - connection_line_buffer clb(ll_client, next_line_cb, 150); + connection_line_buffer<loopback_connection_client> clb(ll_client, next_line_cb, 150); if constexpr (display_dbg_msg) std::cout << "Testing legit lines" << std::endl; diff --git a/test/loopback_connection_client.hpp b/test/loopback_connection_client.hpp index f9b1828ee8876fc071b3c09cf68b1a05c2189fd2..115b720e884cf05184bbffc12411dce4e9a8a278 100644 --- a/test/loopback_connection_client.hpp +++ b/test/loopback_connection_client.hpp @@ -10,9 +10,9 @@ namespace rmrf::test { /** * Use this class to mock a connection client. */ -class loopback_connection_client : public rmrf::net::connection_client { +class loopback_connection_client : public rmrf::net::connection_client<loopback_connection_client> { private: - const rmrf::net::connection_client::incomming_data_cb send_data_cb; + const incomming_data_cb send_data_cb; std::vector<std::string> data_archive; const bool echo_data_transfer; @@ -22,10 +22,10 @@ public: * send data. */ loopback_connection_client( - rmrf::net::connection_client::incomming_data_cb send_data_cb_, + const incomming_data_cb& send_data_cb_, bool echo_data ) : - rmrf::net::connection_client{}, + rmrf::net::connection_client<loopback_connection_client>{}, send_data_cb(send_data_cb_), data_archive{}, echo_data_transfer(echo_data)