From b927b7a05dfa87886badcb413743bc06111edac1 Mon Sep 17 00:00:00 2001 From: Doralitze <doralitze@chaotikum.org> Date: Tue, 5 Jan 2021 21:58:48 +0100 Subject: [PATCH] add: connection_line_buffer class TODO: Write unit test for connection_line_buffer using loopback client. Warning: Do not rely on connection_line_buffer::conn_data_in_cb until the unit test case proves its correctness (Nice looking solution but kinda smells like an off-by-one) --- src/net/connection_line_buffer.cpp | 74 ++++++++++++++++++++++++++++++ src/net/connection_line_buffer.hpp | 38 +++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 src/net/connection_line_buffer.cpp create mode 100644 src/net/connection_line_buffer.hpp diff --git a/src/net/connection_line_buffer.cpp b/src/net/connection_line_buffer.cpp new file mode 100644 index 0000000..7d1c562 --- /dev/null +++ b/src/net/connection_line_buffer.cpp @@ -0,0 +1,74 @@ +/* + * connection_line_buffer.cpp + * + * Created on: 05.01.2021 + * Author: doralitze + */ + +#include "net/connection_line_buffer.hpp" + +namespace rmrf::net { + +static std::string::size_type default_eol_search(const std::string& data, std::string::size_type start_position) { + const std::string::size_type s = data.size(); + for (std::string::size_type i = start_position; i < s; i++) { + switch (data[i]) { + case '\r': + if(i < s - 1) { + if (data[i + 1] == '\n') { + i++; + } + } + [[fallthrough]]; + case '\n': + return i; + break; + default: + break; + } + } + 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(std::bind(&connection_line_buffer::conn_data_in_cb, this, std::placeholders::_1)); +} + +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) { + + /** + * Known limitation: If the last received data ends with '\r' and the next incoming data would start with '\n' there is no way + * to detect this as the received message might indeed be a complete one ending with '\r' and one cannot wait for a potential + * continuation of said message to arrive. + */ + + std::string::size_type strpos = 0; + + while(strpos != std::string::npos) { + std::string::size_type nextpos = this->search(data_in, strpos); + if (nextpos == std::string::npos) { + this->data += data_in.substr(strpos, data_in.length() - strpos); + break; + } else { + this->found_next_line_cb(this->data + data_in.substr(strpos, nextpos - strpos), true); + this->data = std::string(""); + } + strpos = nextpos; + } + + 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 new file mode 100644 index 0000000..6eaf17c --- /dev/null +++ b/src/net/connection_line_buffer.hpp @@ -0,0 +1,38 @@ +/* + * connection_line_buffer.hpp + * + * Created on: 05.01.2021 + * Author: doralitze + */ + +#pragma once + +#include <functional> +#include <memory> +#include <string> + +#include "net/connection_client.hpp" + +namespace rmrf::net { + +typedef std::function<std::string::size_type(const std::string&, std::string::size_type)> eol_search_t; + +static std::string::size_type default_eol_search(const std::string& data, std::string::size_type start_position); + +class connection_line_buffer { +public: + typedef std::function<void(const std::string&, bool)> found_next_line_cb_t; +private: + const eol_search_t search; + std::shared_ptr<connection_client> client; + found_next_line_cb_t found_next_line_cb; + std::string::size_type max; + std::string data; +public: + 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 = default_eol_search); + 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); +private: + void conn_data_in_cb(const std::string& data_in); +}; + +} -- GitLab