.. _program_listing_file_BufferFormatter.cpp: Program Listing for File BufferFormatter.cpp ============================================ |exhale_lsh| :ref:`Return to documentation for file ` (``BufferFormatter.cpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp #include "netio3/BufferFormatter.hpp" #include #include #include using namespace netio3; std::string netio3::formatter_status_string(FormatterStatus status) { switch (status) { case FormatterStatus::BUFFER_OK: return "OK"; break; case FormatterStatus::BUFFER_READY: return "READY"; case FormatterStatus::MESSAGE_TOO_BIG: return "Message too big"; default: return "unknown"; } } void BufferFormatter::fill_header(NetworkBuffer* buf, uint8_t status, uint64_t tag, std::size_t payload_size) { const bool has_status = status != 0; const bool has_tag = m_buffer_empty || tag != m_previous_tag; uint32_t msg_total_size = payload_size; if (has_status) [[unlikely]] { msg_total_size += sizeof(status); } if (has_tag) { msg_total_size += sizeof(tag); } const uint32_t header = msg_total_size | (has_status << HDR_STATUS_FLAG_POS) | (has_tag << HDR_TAG_FLAG_POS); buf->write(header); if (has_status) [[unlikely]] { buf->write(status); } if (has_tag) { buf->write(tag); m_previous_tag = tag; m_buffer_empty = false; } } bool BufferFormatter::check_size(NetworkBuffer* buf, std::size_t payload_size) { return (payload_size < MSG_MAX_SIZE) and (buf->pos() + payload_size + HDR_MAX_SIZE <= buf->size()); } FormatterStatus BufferFormatter::write(NetworkBuffer* buf, uint8_t status, uint64_t tag, const std::span data){ const auto payload_size = std::accumulate( data.begin(), data.end(), std::size_t{0}, [](std::size_t tot, iovec entry) { return tot + entry.iov_len; }); if (!check_size(buf, payload_size)) { return FormatterStatus::MESSAGE_TOO_BIG; } fill_header(buf, status, tag, payload_size); // Fill buffer with the payload for (const auto& iov : data) { buf->write(iov); } return FormatterStatus::BUFFER_OK; } FormatterStatus BufferFormatter::write(NetworkBuffer* buf, uint8_t status, uint64_t tag, std::span data){ const auto payload_size = data.size(); if (!check_size(buf, payload_size)) { return FormatterStatus::MESSAGE_TOO_BIG; } fill_header(buf, status, tag, payload_size); buf->write(data); return FormatterStatus::BUFFER_OK; } FormatterStatus BufferFormatter::write(NetworkBuffer* buf, uint64_t tag, const std::span data){ const auto payload_size = std::accumulate( data.begin(), data.end(), std::size_t{0}, [](std::size_t tot, iovec entry) { return tot + entry.iov_len; }); if (!check_size(buf, payload_size)) { return FormatterStatus::MESSAGE_TOO_BIG; } /* 0 = no status */ fill_header(buf, 0, tag, payload_size); // Fill buffer with the payload for (const auto& iov : data) { buf->write(iov); } return FormatterStatus::BUFFER_OK; } FormatterStatus BufferFormatter::write(NetworkBuffer* buf, uint64_t tag, std::span data){ const auto payload_size = data.size(); /* 0 = no status */ if (!check_size(buf, payload_size)) { return FormatterStatus::MESSAGE_TOO_BIG; } /* 0 = no status */ fill_header(buf, 0, tag, payload_size); buf->write(data); return FormatterStatus::BUFFER_OK; } void BufferFormatter::decode(std::span buf, const std::function, std::uint8_t)>& cb){ std::size_t current_pos = 0; const uint8_t* data_ptr = buf.data(); bool is_first_msg = true; uint64_t previous_tag = UINT64_MAX; while (current_pos < buf.size()) { uint32_t msg_header; if(current_pos + sizeof(msg_header) > buf.size()) break; memcpy(&msg_header, data_ptr + current_pos, sizeof(msg_header)); current_pos += sizeof(msg_header); uint8_t status = 0; if (msg_header & (1U << HDR_STATUS_FLAG_POS)) { // Check if there is a status byte if(current_pos + sizeof(status) > buf.size()) break; msg_header ^= (1U << HDR_STATUS_FLAG_POS); // Clear the most significant bit memcpy(&status, data_ptr + current_pos, sizeof(status)); current_pos += sizeof(status); msg_header -= sizeof(status); } uint64_t tag = previous_tag; if (msg_header & (1U << HDR_TAG_FLAG_POS)) { // Check if there is a tag if(current_pos + sizeof(tag) > buf.size()) break; msg_header ^= (1U << HDR_TAG_FLAG_POS); // Clear the second most significant bit memcpy(&tag, data_ptr + current_pos, sizeof(tag)); current_pos += sizeof(tag); msg_header -= sizeof(tag); previous_tag = tag; }else if(is_first_msg){ throw std::runtime_error("First message must have a tag. Something bad happened."); } if(current_pos + msg_header > buf.size()) break; std::span payload = buf.subspan(current_pos, msg_header); current_pos += msg_header; std::invoke(cb, tag, payload, status); is_first_msg = false; } } void BufferFormatter::reset_buffer(NetworkBuffer* buf){ buf->reset(); m_buffer_empty = true; }