feat: utilities namespace, as_bytes function, mqtt properties class and better trace logs

This commit is contained in:
brenodetomini
2024-02-04 16:37:16 -03:00
parent 6530245865
commit fd49879ba9
27 changed files with 553 additions and 324 deletions

View File

@@ -1,5 +1,6 @@
add_library(libmqttd "")
add_subdirectory(utilities)
add_subdirectory(types)
add_subdirectory(network)
add_subdirectory(protocol)

View File

@@ -0,0 +1,32 @@
#include "packet_interface.hpp"
#include "fixed_header.hpp"
#include <connect_ack.hpp>
#include <cstddef>
#include <vector>
ConnectACK::ConnectACK() : PacketInterface(){
PacketInterface::fixed_header.packet_type = PacketType::CONNACK;
PacketInterface::fixed_header.packet_flags = 0;
};
void set_properties(const MQTTProperties &value) {
}
std::vector<std::byte> ConnectACK::as_bytes() const {
std::vector<std::byte> byte_vector;
byte_vector = PacketInterface::as_bytes();
if(session_present)
byte_vector.push_back(std::byte(1));
else
byte_vector.push_back(std::byte(0));
byte_vector.push_back(std::byte(reason_code));
std::vector<std::byte> properties_bytes = properties.as_bytes();
byte_vector.insert(byte_vector.end(), properties_bytes.begin(), properties_bytes.end());
return byte_vector;
}

View File

@@ -1,29 +1,49 @@
#ifndef INCLUDE_CONNECTION_CONNECT_ACK_HPP_
#define INCLUDE_CONNECTION_CONNECT_ACK_HPP_
#include <control_packet.hpp>
#include "utf8_string.hpp"
#include <cstddef>
#include <last_will.hpp>
#include <utilities.hpp>
#include <vector>
#include <packet_interface.hpp>
#include <property.hpp>
class ConnectACK : public ControlPacket {
enum ConnectReasonCode : uint8_t {
SUCCESS = 0,
UNSPECIFIED_ERROR = 128,
MALFORMED_PACKET = 129,
PROTOCOL_ERROR = 130,
IMPLEMENTATION_SPECIFIC_ERROR = 131,
UNSUPPORTED_PROTOCOL_VERSION = 132,
CLIENT_IDENTIFIER_NOT_VALID = 133,
BAD_USERNAME_OR_PASSWORD = 134,
NOT_AUTHORIZED = 135,
SERVER_UNAVAILABLE = 136,
SERVER_BUSY = 137,
BANNED = 138,
BAD_AUTHENTICATION_METHOD = 140,
TOPIC_NAME_INVALID = 144,
PACKET_TOO_LARGE = 149,
QUOTA_EXCEEDED = 151,
PAYLOAD_FORMAT_INVALID = 153,
RETAIN_NOT_SUPPORTED = 154,
QOS_NOT_SUPPORTED = 155,
USER_ANOTHER_SERVER = 156,
SERVER_MOVED = 157,
CONNECTION_RATE_EXCEEDED = 159
};
class ConnectACK : public PacketInterface {
public:
ConnectACK(ControlPacket &);
ConnectACK(std::vector<std::byte>);
~ConnectACK();
std::string to_string() final;
ConnectACK();
~ConnectACK() = default;
void set_session_present(const bool &value) { this->session_present = value; };
void set_reason_code(const ConnectReasonCode &value) { this->reason_code = value; };
void set_properties(const MQTTProperties &value);
std::vector<std::byte> as_bytes() const;
private:
utf8_str protocol_name;
std::uint8_t protocol_version;
std::byte connection_flags;
std::uint16_t keepalive;
std::vector<std::byte>::const_iterator payload_start_byte;
void parse_variable_header() final;
void parse_payload() final;
//Variable Header
bool session_present;
ConnectReasonCode reason_code;
MQTTProperties properties;
};
#endif // INCLUDE_CONNECTION_CONNECT_ACK_HPP_

View File

@@ -1,8 +1,8 @@
#include "connect_packet.hpp"
#include "binary_data.hpp"
#include "property.hpp"
#include "spdlog/spdlog.h"
#include "utf8_string.hpp"
#include "utilities.hpp"
#include <iostream>
#include <iterator>
#include <sstream>
@@ -18,12 +18,12 @@ ConnectPacket::~ConnectPacket() {}
ConnectPacket::ConnectPacket(std::vector<std::byte> data) : PacketInterface(data) { parse_variable_header(); }
void ConnectPacket::parse_variable_header() {
auto protocol_name_start_byte = this->get_variable_header_start_byte();
this->protocol_name = utf8_str(protocol_name_start_byte);
spdlog::trace("Parsing Connect variable header");
this->protocol_name = utf8_str(this->variable_header_start_byte);
if (this->protocol_name.as_string() != "MQTT")
throw std::invalid_argument("Wrong protocol name. Expected MQTT and received " + this->protocol_name.as_string());
auto protocol_version_start_byte = std::next(protocol_name_start_byte, 2 + protocol_name.size());
auto protocol_version_start_byte = std::next(this->variable_header_start_byte, protocol_name.as_bytes().size());
this->protocol_version = utilities::types::get_fixed_size_integer<uint8_t>(protocol_version_start_byte);
auto connection_flags_start_byte = std::next(protocol_version_start_byte);
@@ -33,10 +33,11 @@ void ConnectPacket::parse_variable_header() {
this->keepalive = utilities::types::get_fixed_size_integer<uint16_t>(keepalive_start_byte);
auto header_properties_start_byte = std::next(keepalive_start_byte, sizeof(this->keepalive));
std::tie(this->properties, payload_start_byte) = parse_mqtt_properties(header_properties_start_byte);
this->properties = MQTTProperties(header_properties_start_byte);
this->payload_start_byte = std::next(header_properties_start_byte, this->properties.as_bytes().size() + 1);
}
ConnectionFlagValue ConnectPacket::get_connection_flag(ConnectFlags flag) {
ConnectionFlagValue ConnectPacket::get_connection_flag(ConnectFlags flag) const {
uint8_t connection_flags_value = static_cast<uint8_t>(this->connection_flags);
uint8_t flag_pos = static_cast<uint8_t>(flag);
// WILL_QOS is a 2 byte integer value
@@ -50,10 +51,10 @@ ConnectionFlagValue ConnectPacket::get_connection_flag(ConnectFlags flag) {
return static_cast<bool>(utilities::bit::get(connection_flags_value, flag_pos));
}
std::string ConnectPacket::to_string() {
std::string ConnectPacket::as_string() const {
std::ostringstream packet_str;
packet_str << PacketInterface::to_string();
packet_str << PacketInterface::as_string();
packet_str << "\t"
<< "\u21B3"
<< "Variable Hedaer" << std::endl;
@@ -72,7 +73,7 @@ std::string ConnectPacket::to_string() {
packet_str << "\t\t"
<< "\u21B3"
<< "Packet identifier = " << packet_identifier << std::endl;
if (properties.length > 0) {
if (properties.size() > 0) {
packet_str << properties;
}
@@ -105,24 +106,29 @@ std::string ConnectPacket::to_string() {
}
void ConnectPacket::parse_payload() {
spdlog::trace("Parsing Connect payload");
this->client_id = utf8_str(payload_start_byte);
auto next_field_start_byte = std::next(payload_start_byte, 2 + this->client_id.size());
auto next_field_start_byte = std::next(payload_start_byte, this->client_id.as_bytes().size());
if (std::get<bool>(get_connection_flag(ConnectFlags::WILL_FLAG))) {
std::tie(this->lastwill.properties, next_field_start_byte) = parse_mqtt_properties(next_field_start_byte);
this->lastwill.properties = MQTTProperties(next_field_start_byte);
//TODO: Why here we don't have to sum 1, while at variable_header_start_byte and payload_start_byte we do?
next_field_start_byte = std::next(next_field_start_byte, this->lastwill.properties.as_bytes().size());
this->lastwill.will_topic = utf8_str(next_field_start_byte);
next_field_start_byte = std::next(next_field_start_byte, 2 + this->lastwill.will_topic.size());
next_field_start_byte = std::next(next_field_start_byte, this->lastwill.will_topic.as_bytes().size());
this->lastwill.will_payload = binary_data(next_field_start_byte);
next_field_start_byte = std::next(next_field_start_byte, 2 + this->lastwill.will_payload.size());
next_field_start_byte = std::next(next_field_start_byte, this->lastwill.will_payload.as_bytes().size());
}
if (std::get<bool>(get_connection_flag(ConnectFlags::USERNAME_FLAG))) {
this->username = utf8_str(next_field_start_byte);
next_field_start_byte = std::next(next_field_start_byte, 2 + this->username.size());
next_field_start_byte = std::next(next_field_start_byte, this->username.as_bytes().size());
}
if (std::get<bool>(get_connection_flag(ConnectFlags::PASSWORD_FLAG))) {
this->password = binary_data(next_field_start_byte);
next_field_start_byte = std::next(next_field_start_byte, 2 + this->password.size());
next_field_start_byte = std::next(next_field_start_byte, this->password.as_bytes().size());
}
}

View File

@@ -1,11 +1,12 @@
#ifndef INCLUDE_CONNECTION_CONNECT_PACKET_HPP_
#define INCLUDE_CONNECTION_CONNECT_PACKET_HPP_
#include "exceptions.hpp"
#include "utf8_string.hpp"
#include <packet_interface.hpp>
#include <cstddef>
#include <last_will.hpp>
#include <utilities.hpp>
#include <bit.hpp>
#include <vector>
enum ConnectFlags : uint16_t {
@@ -25,9 +26,11 @@ public:
ConnectPacket(PacketInterface &);
ConnectPacket(std::vector<std::byte>);
~ConnectPacket();
ConnectionFlagValue get_connection_flag(ConnectFlags flag);
std::string to_string() final;
ConnectionFlagValue get_connection_flag(ConnectFlags flag) const;
std::string as_string() const final;
std::vector<std::byte> as_bytes() const final { throw NotImplemented(); };
private:
void parse_variable_header() final;

View File

@@ -6,7 +6,7 @@
#include <ostream>
enum class PacketType : uint32_t {
enum class PacketType : uint8_t {
RESERVED = 0,
CONNECT = 1,
CONNACK = 2,

View File

@@ -1,4 +1,5 @@
#include "packet_interface.hpp"
#include "bytes.hpp"
#include <cstddef>
#include <vector>
#include <sstream>
@@ -13,17 +14,19 @@ PacketInterface::PacketInterface(const std::vector<std::byte> &data) {
auto it = data.begin() + 1;
this->fixed_header.remaining_length = int_vb(it);
this->variable_header_start_byte = std::next(raw_data.begin(), fixed_header.remaining_length.size() + 1);
}
PacketInterface::PacketInterface(PacketInterface &packet) {
this->fixed_header.packet_type = packet.fixed_header.packet_type;
this->fixed_header.packet_flags = packet.fixed_header.packet_flags;
this->fixed_header.remaining_length = packet.fixed_header.remaining_length;
this->variable_header_start_byte = packet.variable_header_start_byte;
this->raw_data = packet.raw_data;
}
std::string PacketInterface::to_string() {
std::string PacketInterface::as_string() const {
std::ostringstream packet_str;
packet_str << std::endl;
packet_str << "Packet Type = " << this->get_packet_type() << std::endl;
@@ -37,3 +40,19 @@ std::string PacketInterface::to_string() {
return packet_str.str();
}
std::vector<std::byte> PacketInterface::as_bytes() const {
std::vector<std::byte> packet_bytes;
uint8_t flags_value = static_cast<uint8_t>(fixed_header.packet_flags.to_ulong());
uint8_t first_byte = 0;
first_byte = static_cast<uint16_t>(fixed_header.packet_type) << 4; // Shift de 4 bits para deixar espaço para os flags
first_byte |= flags_value;
packet_bytes.push_back(static_cast<std::byte>(first_byte));
std::vector<std::byte> remaining_length_bytes = fixed_header.remaining_length.as_bytes();
packet_bytes.insert(packet_bytes.end(), remaining_length_bytes.begin(), remaining_length_bytes.end());
return packet_bytes;
}

View File

@@ -3,7 +3,8 @@
#include "fixed_header.hpp"
#include <exceptions.hpp>
#include <utilities.hpp>
#include <bit.hpp>
#include <types.hpp>
#include <variable_byte_int.hpp>
#include <vector>
@@ -11,7 +12,7 @@ class PacketInterface {
public:
PacketInterface(const std::vector<std::byte> &);
PacketInterface(PacketInterface &);
PacketInterface();
PacketInterface(){};
~PacketInterface() = default;
inline uint length() const { return sizeof(this->fixed_header) + this->fixed_header.remaining_length; }
@@ -19,8 +20,8 @@ public:
inline PacketType get_packet_type() const { return this->fixed_header.packet_type; };
inline std::bitset<4> get_packet_flags() const { return this->fixed_header.packet_flags; };
virtual std::string to_string();
virtual std::vector<std::byte> to_bytes() const { return this->raw_data; };
virtual std::string as_string() const;
virtual std::vector<std::byte> as_bytes() const;
protected:
std::vector<std::byte> raw_data;
@@ -29,7 +30,7 @@ protected:
virtual void parse_variable_header() { throw NotImplemented(); };
virtual void parse_payload() { throw NotImplemented(); };
inline auto get_variable_header_start_byte() const { return std::next(raw_data.begin(), 1 + fixed_header.remaining_length.size()); };
std::vector<std::byte>::const_iterator variable_header_start_byte;
};
#endif // INCLUDE_NETWORK_CONTROL_PACKET_HPP_

View File

@@ -1,9 +1,244 @@
#include "bytes.hpp"
#include "spdlog/spdlog.h"
#include "type_interface.hpp"
#include "utf8_string_pair.hpp"
#include <cstddef>
#include <iostream>
#include <iterator>
#include <ostream>
#include <property.hpp>
#include <tuple>
#include <queue>
#include <sstream>
#include <string>
#include <type_traits>
#include <vector>
MQTTProperties::MQTTProperties() { this->properties[PropertyIdentifier::USER_PROPERTY] = UserProperty(); }
MQTTProperties::MQTTProperties(const std::vector<std::byte>::const_iterator &property_start) {
this->properties[PropertyIdentifier::USER_PROPERTY] = UserProperty();
this->length = int_vb(property_start);
spdlog::trace("Parsing Properties with length " + std::to_string(length));
auto current_byte = std::next(property_start, this->length.size());
while (std::distance(property_start, current_byte) < this->length) {
uint next_property_offset = 0;
PropertyIdentifier current_property = PropertyIdentifier(std::to_integer<uint8_t>(*current_byte));
std::ostringstream log_msg;
log_msg << "Parsing property " << current_property << " (ID " << std::to_string(int(current_property)) << ")";
spdlog::trace(log_msg.str());
switch (current_property) {
case PropertyIdentifier::PAYLOAD_FORMAT_INDICATOR: {
next_property_offset = 2;
std::byte property_value = *(std::next(current_byte, 1));
this->properties[PropertyIdentifier::PAYLOAD_FORMAT_INDICATOR] = PropertyValue{property_value};
break;
}
case PropertyIdentifier::MESSAGE_EXPIRY_INTERVAL: {
uint32_t property_value = utilities::types::get_fixed_size_integer<uint32_t>(std::next(current_byte));
;
next_property_offset = 1 + sizeof(property_value);
this->properties[PropertyIdentifier::MESSAGE_EXPIRY_INTERVAL] = PropertyValue{property_value};
break;
}
case PropertyIdentifier::CONTENT_TYPE: {
utf8_str property_value(std::next(current_byte));
this->properties[PropertyIdentifier::CONTENT_TYPE] = PropertyValue{property_value};
next_property_offset = 1 + property_value.as_bytes().size();
break;
}
case PropertyIdentifier::RESPONSE_TOPIC: {
utf8_str property_value(std::next(current_byte));
this->properties[PropertyIdentifier::RESPONSE_TOPIC] = PropertyValue{property_value};
next_property_offset = 1 + property_value.as_bytes().size();
break;
}
case PropertyIdentifier::CORRELATION_DATA: {
binary_data property_value(std::next(current_byte));
this->properties[PropertyIdentifier::CORRELATION_DATA] = PropertyValue{property_value};
next_property_offset = 1 + property_value.as_bytes().size();
}
case PropertyIdentifier::SUBSCRIPTION_IDENTIFIER: {
int_vb property_value(current_byte);
this->properties[PropertyIdentifier::CORRELATION_DATA] = PropertyValue{property_value};
next_property_offset = 1 + property_value.as_bytes().size();
}
case PropertyIdentifier::SESSION_EXPIRY_INTERVAL: {
uint32_t property_value = utilities::types::get_fixed_size_integer<uint32_t>(std::next(current_byte));
next_property_offset = 1 + sizeof(property_value);
this->properties[PropertyIdentifier::SESSION_EXPIRY_INTERVAL] = PropertyValue{property_value};
break;
}
case PropertyIdentifier::ASSIGNED_CLIENT_IDENTIFIER: {
utf8_str property_value(std::next(current_byte));
this->properties[PropertyIdentifier::ASSIGNED_CLIENT_IDENTIFIER] = PropertyValue{property_value};
next_property_offset = 1 + property_value.as_bytes().size();
break;
}
case PropertyIdentifier::SERVER_KEEP_ALIVE: {
uint16_t property_value = utilities::types::get_fixed_size_integer<uint16_t>(std::next(current_byte));
next_property_offset = 1 + sizeof(property_value);
this->properties[PropertyIdentifier::SERVER_KEEP_ALIVE] = PropertyValue{property_value};
break;
}
case PropertyIdentifier::AUTHENTICATION_METHOD: {
utf8_str property_value(std::next(current_byte));
this->properties[PropertyIdentifier::AUTHENTICATION_METHOD] = PropertyValue{property_value};
next_property_offset = 1 + property_value.as_bytes().size();
break;
}
case PropertyIdentifier::AUTHENTICATION_DATA: {
binary_data property_value(std::next(current_byte));
this->properties[PropertyIdentifier::AUTHENTICATION_DATA] = PropertyValue{property_value};
next_property_offset = 1 + property_value.as_bytes().size();
}
case PropertyIdentifier::REQUEST_PROBLEM_INFORMATION: {
next_property_offset = 2;
std::byte property_value = *(std::next(current_byte, 1));
this->properties[PropertyIdentifier::REQUEST_PROBLEM_INFORMATION] = PropertyValue{property_value};
break;
}
case PropertyIdentifier::WILL_DELAY_INTERVAL: {
uint32_t property_value = utilities::types::get_fixed_size_integer<uint32_t>(std::next(current_byte));
next_property_offset = 1 + sizeof(property_value);
this->properties[PropertyIdentifier::WILL_DELAY_INTERVAL] = PropertyValue{property_value};
break;
}
case PropertyIdentifier::REQUEST_RESPONSE_INFORMATION: {
next_property_offset = 2;
std::byte property_value = *(std::next(current_byte, 1));
this->properties[PropertyIdentifier::REQUEST_RESPONSE_INFORMATION] = PropertyValue{property_value};
break;
}
case PropertyIdentifier::RESPONSE_INFORMATION: {
utf8_str property_value(std::next(current_byte));
this->properties[PropertyIdentifier::RESPONSE_INFORMATION] = PropertyValue{property_value};
next_property_offset = 1 + property_value.as_bytes().size();
break;
}
case PropertyIdentifier::SERVER_REFERENCE: {
utf8_str property_value(std::next(current_byte));
this->properties[PropertyIdentifier::SERVER_REFERENCE] = PropertyValue{property_value};
next_property_offset = 1 + property_value.as_bytes().size();
break;
}
case PropertyIdentifier::REASON_STRING: {
utf8_str property_value(std::next(current_byte));
this->properties[PropertyIdentifier::REASON_STRING] = PropertyValue{property_value};
next_property_offset = 1 + property_value.as_bytes().size();
break;
}
case PropertyIdentifier::RECEIVE_MAXIMUM: {
uint16_t property_value = utilities::types::get_fixed_size_integer<uint16_t>(std::next(current_byte));
next_property_offset = 1 + sizeof(property_value);
this->properties[PropertyIdentifier::RECEIVE_MAXIMUM] = PropertyValue{property_value};
break;
}
case PropertyIdentifier::TOPIC_ALIAS_MAXIMUM: {
uint16_t property_value = utilities::types::get_fixed_size_integer<uint16_t>(std::next(current_byte));
next_property_offset = 1 + sizeof(property_value);
this->properties[PropertyIdentifier::RECEIVE_MAXIMUM] = PropertyValue{property_value};
break;
}
case PropertyIdentifier::TOPIC_ALIAS: {
uint16_t property_value = utilities::types::get_fixed_size_integer<uint16_t>(std::next(current_byte));
next_property_offset = 1 + sizeof(property_value);
this->properties[PropertyIdentifier::RECEIVE_MAXIMUM] = PropertyValue{property_value};
break;
}
case PropertyIdentifier::MAXIMUM_QOS: {
std::byte property_value = *(std::next(current_byte, 1));
next_property_offset = 1 + sizeof(property_value);
this->properties[PropertyIdentifier::MAXIMUM_QOS] = PropertyValue{property_value};
break;
}
case PropertyIdentifier::RETAIN_AVAILABLE: {
std::byte property_value = *(std::next(current_byte, 1));
next_property_offset = 1 + sizeof(property_value);
this->properties[PropertyIdentifier::RETAIN_AVAILABLE] = PropertyValue{property_value};
break;
}
case PropertyIdentifier::USER_PROPERTY: {
UTF8StringPair property_value(std::next(current_byte));
auto &user_property_list = std::get<UserProperty>(this->properties[PropertyIdentifier::USER_PROPERTY]);
user_property_list.push(property_value);
next_property_offset = 1 + property_value.as_bytes().size();
break;
}
case PropertyIdentifier::MAXIMUM_PACKET_SIZE: {
uint32_t property_value = utilities::types::get_fixed_size_integer<uint32_t>(std::next(current_byte));
next_property_offset = 1 + sizeof(property_value);
this->properties[PropertyIdentifier::RECEIVE_MAXIMUM] = PropertyValue{property_value};
break;
}
case PropertyIdentifier::WILDCARD_SUBSCRIPTION_AVAILABLE: {
std::byte property_value = *(std::next(current_byte, 1));
next_property_offset = 1 + sizeof(property_value);
this->properties[PropertyIdentifier::WILDCARD_SUBSCRIPTION_AVAILABLE] = PropertyValue{property_value};
break;
}
case PropertyIdentifier::SUBSCRIPTION_IDENTIFIER_AVAILABLE: {
std::byte property_value = *(std::next(current_byte, 1));
next_property_offset = 1 + sizeof(property_value);
this->properties[PropertyIdentifier::SUBSCRIPTION_IDENTIFIER_AVAILABLE] = PropertyValue{property_value};
break;
}
case PropertyIdentifier::SHARED_SUBSCRIPTION_AVAILABLE: {
std::byte property_value = *(std::next(current_byte, 1));
next_property_offset = 1 + sizeof(property_value);
this->properties[PropertyIdentifier::SHARED_SUBSCRIPTION_AVAILABLE] = PropertyValue{property_value};
break;
}
default: {
uint8_t byte_value = std::to_integer<uint8_t>(*current_byte);
throw std::runtime_error("Invalid property received (" + std::to_string(byte_value) + ")");
}
}
current_byte = std::next(current_byte, next_property_offset);
}
}
std::vector<std::byte> MQTTProperties::as_bytes() const {
std::vector<std::byte> properties_bytes;
std::vector<std::byte> length_bytes = this->length.as_bytes();
properties_bytes.insert(properties_bytes.end(), length_bytes.begin(), length_bytes.end());
for (const auto &[prop_identifier, prop_value] : this->properties) {
std::vector<std::byte> property_bytes;
std::visit(
[&property_bytes](const auto &value) {
using T = std::decay_t<decltype(value)>;
if constexpr (std::is_base_of_v<TypeInterface, T>) {
property_bytes = value.as_bytes();
} else if constexpr (std::is_base_of_v<std::queue<UTF8StringPair>, T>) {
std::queue<UTF8StringPair> temp_queue = value;
// UserProperty is initalized as an empty queue, if we count that we will add an extra byte in payload resulting in inconsistencies
if (temp_queue.empty()) {
property_bytes.clear();
return;
}
while (!temp_queue.empty()) {
auto element_bytes = temp_queue.front().as_bytes();
temp_queue.pop();
property_bytes.insert(property_bytes.end(), element_bytes.begin(), element_bytes.end());
}
} else {
property_bytes = utilities::bytes::to_bytes(value);
}
},
prop_value);
if (!property_bytes.empty()) {
properties_bytes.push_back(static_cast<std::byte>(prop_identifier));
properties_bytes.insert(properties_bytes.end(), property_bytes.begin(), property_bytes.end());
}
}
return properties_bytes;
}
struct VariantPrinter {
std::ostream &os;
template <typename T> void operator()(const T &value) const { os << value; }
@@ -112,7 +347,7 @@ std::ostream &operator<<(std::ostream &os, const PropertyValue &value) {
std::ostream &operator<<(std::ostream &os, const UserProperty &value) {
UserProperty queue = value;
os << std::endl;
while(!queue.empty()) {
while (!queue.empty()) {
UTF8StringPair queue_value = queue.front();
os << "\t\t\t\t\u21B3(" << queue_value.get_key() << ", " << queue_value.get_value() << ")" << std::endl;
queue.pop();
@@ -132,194 +367,6 @@ std::ostream &operator<<(std::ostream &os, const MQTTProperties &value) {
<< "Property Length = " << value.length << std::endl;
os << "\t\t\u21B3"
<< "Property Values" << std::endl;
os << value.properties;
os << value.properties;
return os;
}
std::tuple<MQTTProperties, std::vector<std::byte>::const_iterator> parse_mqtt_properties(const std::vector<std::byte>::const_iterator &property_start) {
MQTTProperties parsed_properties;
parsed_properties.properties[PropertyIdentifier::USER_PROPERTY] = UserProperty();
parsed_properties.length = int_vb(property_start);
spdlog::trace("Parsing Properties with length " + std::to_string(parsed_properties.length));
auto current_byte = std::next(property_start, parsed_properties.length.size());
while (std::distance(property_start, current_byte) < parsed_properties.length) {
uint next_property_offset = 0;
PropertyIdentifier current_property = PropertyIdentifier(std::to_integer<uint8_t>(*current_byte));
spdlog::trace("Parsing Variable Header Property with identifier " + std::to_string(int(current_property)));
switch (current_property) {
case PropertyIdentifier::PAYLOAD_FORMAT_INDICATOR: {
next_property_offset = 2;
std::byte property_value = *(std::next(current_byte, 1));
parsed_properties.properties[PropertyIdentifier::PAYLOAD_FORMAT_INDICATOR] = PropertyValue{property_value};
break;
}
case PropertyIdentifier::MESSAGE_EXPIRY_INTERVAL: {
uint32_t property_value = utilities::types::get_fixed_size_integer<uint32_t>(std::next(current_byte));
;
next_property_offset = 1 + sizeof(property_value);
parsed_properties.properties[PropertyIdentifier::MESSAGE_EXPIRY_INTERVAL] = PropertyValue{property_value};
break;
}
case PropertyIdentifier::CONTENT_TYPE: {
utf8_str property_value(std::next(current_byte));
parsed_properties.properties[PropertyIdentifier::CONTENT_TYPE] = PropertyValue{property_value};
next_property_offset = 1 + 2 + property_value.size();
break;
}
case PropertyIdentifier::RESPONSE_TOPIC: {
utf8_str property_value(std::next(current_byte));
parsed_properties.properties[PropertyIdentifier::RESPONSE_TOPIC] = PropertyValue{property_value};
next_property_offset = 1 + 2 + property_value.size();
break;
}
case PropertyIdentifier::CORRELATION_DATA: {
binary_data property_value(std::next(current_byte));
parsed_properties.properties[PropertyIdentifier::CORRELATION_DATA] = PropertyValue{property_value};
next_property_offset = 1 + 2 + property_value.size();
}
case PropertyIdentifier::SUBSCRIPTION_IDENTIFIER: {
int_vb property_value(current_byte);
parsed_properties.properties[PropertyIdentifier::CORRELATION_DATA] = PropertyValue{property_value};
next_property_offset = 1 + 2 + property_value.size();
}
case PropertyIdentifier::SESSION_EXPIRY_INTERVAL: {
uint32_t property_value = utilities::types::get_fixed_size_integer<uint32_t>(std::next(current_byte));
next_property_offset = 1 + sizeof(property_value);
parsed_properties.properties[PropertyIdentifier::SESSION_EXPIRY_INTERVAL] = PropertyValue{property_value};
break;
}
case PropertyIdentifier::ASSIGNED_CLIENT_IDENTIFIER: {
utf8_str property_value(std::next(current_byte));
parsed_properties.properties[PropertyIdentifier::ASSIGNED_CLIENT_IDENTIFIER] = PropertyValue{property_value};
next_property_offset = 1 + 2 + property_value.size();
break;
}
case PropertyIdentifier::SERVER_KEEP_ALIVE: {
uint16_t property_value = utilities::types::get_fixed_size_integer<uint16_t>(std::next(current_byte));
next_property_offset = 1 + sizeof(property_value);
parsed_properties.properties[PropertyIdentifier::SERVER_KEEP_ALIVE] = PropertyValue{property_value};
break;
}
case PropertyIdentifier::AUTHENTICATION_METHOD: {
utf8_str property_value(std::next(current_byte));
parsed_properties.properties[PropertyIdentifier::AUTHENTICATION_METHOD] = PropertyValue{property_value};
next_property_offset = 1 + 2 + property_value.size();
break;
}
case PropertyIdentifier::AUTHENTICATION_DATA: {
binary_data property_value(std::next(current_byte));
parsed_properties.properties[PropertyIdentifier::AUTHENTICATION_DATA] = PropertyValue{property_value};
next_property_offset = 1 + 2 + property_value.size();
}
case PropertyIdentifier::REQUEST_PROBLEM_INFORMATION: {
next_property_offset = 2;
std::byte property_value = *(std::next(current_byte, 1));
parsed_properties.properties[PropertyIdentifier::REQUEST_PROBLEM_INFORMATION] = PropertyValue{property_value};
break;
}
case PropertyIdentifier::WILL_DELAY_INTERVAL: {
uint32_t property_value = utilities::types::get_fixed_size_integer<uint32_t>(std::next(current_byte));
next_property_offset = 1 + sizeof(property_value);
parsed_properties.properties[PropertyIdentifier::WILL_DELAY_INTERVAL] = PropertyValue{property_value};
break;
}
case PropertyIdentifier::REQUEST_RESPONSE_INFORMATION: {
next_property_offset = 2;
std::byte property_value = *(std::next(current_byte, 1));
parsed_properties.properties[PropertyIdentifier::REQUEST_RESPONSE_INFORMATION] = PropertyValue{property_value};
break;
}
case PropertyIdentifier::RESPONSE_INFORMATION: {
utf8_str property_value(std::next(current_byte));
parsed_properties.properties[PropertyIdentifier::RESPONSE_INFORMATION] = PropertyValue{property_value};
next_property_offset = 1 + 2 + property_value.size();
break;
}
case PropertyIdentifier::SERVER_REFERENCE: {
utf8_str property_value(std::next(current_byte));
parsed_properties.properties[PropertyIdentifier::SERVER_REFERENCE] = PropertyValue{property_value};
next_property_offset = 1 + 2 + property_value.size();
break;
}
case PropertyIdentifier::REASON_STRING: {
utf8_str property_value(std::next(current_byte));
parsed_properties.properties[PropertyIdentifier::REASON_STRING] = PropertyValue{property_value};
next_property_offset = 1 + 2 + property_value.size();
break;
}
case PropertyIdentifier::RECEIVE_MAXIMUM: {
uint16_t property_value = utilities::types::get_fixed_size_integer<uint16_t>(std::next(current_byte));
next_property_offset = 1 + sizeof(property_value);
parsed_properties.properties[PropertyIdentifier::RECEIVE_MAXIMUM] = PropertyValue{property_value};
break;
}
case PropertyIdentifier::TOPIC_ALIAS_MAXIMUM: {
uint16_t property_value = utilities::types::get_fixed_size_integer<uint16_t>(std::next(current_byte));
next_property_offset = 1 + sizeof(property_value);
parsed_properties.properties[PropertyIdentifier::RECEIVE_MAXIMUM] = PropertyValue{property_value};
break;
}
case PropertyIdentifier::TOPIC_ALIAS: {
uint16_t property_value = utilities::types::get_fixed_size_integer<uint16_t>(std::next(current_byte));
next_property_offset = 1 + sizeof(property_value);
parsed_properties.properties[PropertyIdentifier::RECEIVE_MAXIMUM] = PropertyValue{property_value};
break;
}
case PropertyIdentifier::MAXIMUM_QOS: {
std::byte property_value = *(std::next(current_byte, 1));
next_property_offset = 1 + sizeof(property_value);
parsed_properties.properties[PropertyIdentifier::MAXIMUM_QOS] = PropertyValue{property_value};
break;
}
case PropertyIdentifier::RETAIN_AVAILABLE: {
std::byte property_value = *(std::next(current_byte, 1));
next_property_offset = 1 + sizeof(property_value);
parsed_properties.properties[PropertyIdentifier::RETAIN_AVAILABLE] = PropertyValue{property_value};
break;
}
case PropertyIdentifier::USER_PROPERTY: {
UTF8StringPair property_value(std::next(current_byte));
auto& user_property_list = std::get<UserProperty>(parsed_properties.properties[PropertyIdentifier::USER_PROPERTY]);
user_property_list.push(property_value);
next_property_offset = 1 + 4 + property_value.size();
break;
}
case PropertyIdentifier::MAXIMUM_PACKET_SIZE: {
uint32_t property_value = utilities::types::get_fixed_size_integer<uint32_t>(std::next(current_byte));
next_property_offset = 1 + sizeof(property_value);
parsed_properties.properties[PropertyIdentifier::RECEIVE_MAXIMUM] = PropertyValue{property_value};
break;
}
case PropertyIdentifier::WILDCARD_SUBSCRIPTION_AVAILABLE: {
std::byte property_value = *(std::next(current_byte, 1));
next_property_offset = 1 + sizeof(property_value);
parsed_properties.properties[PropertyIdentifier::WILDCARD_SUBSCRIPTION_AVAILABLE] = PropertyValue{property_value};
break;
}
case PropertyIdentifier::SUBSCRIPTION_IDENTIFIER_AVAILABLE: {
std::byte property_value = *(std::next(current_byte, 1));
next_property_offset = 1 + sizeof(property_value);
parsed_properties.properties[PropertyIdentifier::SUBSCRIPTION_IDENTIFIER_AVAILABLE] = PropertyValue{property_value};
break;
}
case PropertyIdentifier::SHARED_SUBSCRIPTION_AVAILABLE: {
std::byte property_value = *(std::next(current_byte, 1));
next_property_offset = 1 + sizeof(property_value);
parsed_properties.properties[PropertyIdentifier::SHARED_SUBSCRIPTION_AVAILABLE] = PropertyValue{property_value};
break;
}
default: {
uint8_t byte_value = std::to_integer<uint8_t>(*current_byte);
throw std::runtime_error("Invalid property received (" + std::to_string(byte_value) + ")");
}
}
current_byte = std::next(current_byte, next_property_offset);
}
return std::make_tuple(parsed_properties, current_byte);
}
std::size_t get_mqtt_properties_byte_size(const MQTTProperties &value);

View File

@@ -2,13 +2,15 @@
#define INCLUDE_CONTROL_PACKET_PROPERTY_HPP_
#include <binary_data.hpp>
#include <cstddef>
#include <cstdint>
#include <map>
#include <queue>
#include <spdlog/spdlog.h>
#include <types.hpp>
#include <utf8_string.hpp>
#include <utf8_string_pair.hpp>
#include <variable_byte_int.hpp>
#include <spdlog/spdlog.h>
#include <cstdint>
#include <map>
#include <variant>
enum class PropertyIdentifier : uint32_t {
@@ -45,20 +47,28 @@ using UserProperty = std::queue<UTF8StringPair>;
using PropertyValue = std::variant<std::byte, uint32_t, uint16_t, int_vb, binary_data, utf8_str, UserProperty>;
using PropertyMap = std::map<PropertyIdentifier, PropertyValue>;
struct MQTTProperties {
int_vb length;
PropertyMap properties;
};
std::tuple<MQTTProperties, std::vector<std::byte>::const_iterator> parse_mqtt_properties(const std::vector<std::byte>::const_iterator &property_start);
std::size_t get_mqtt_properties_byte_size(const MQTTProperties &value);
std::ostream &operator<<(std::ostream &os, const std::byte &value);
std::ostream &operator<<(std::ostream &os, const UserProperty &value);
std::ostream &operator<<(std::ostream &os, const PropertyIdentifier &value);
std::ostream &operator<<(std::ostream &os, const PropertyValue &value);
std::ostream &operator<<(std::ostream &os, const PropertyMap &value);
std::ostream &operator<<(std::ostream &os, const MQTTProperties &value);
class MQTTProperties {
public:
MQTTProperties();
MQTTProperties(const std::vector<std::byte>::const_iterator &property_start);
~MQTTProperties() = default;
std::vector<std::byte> as_bytes() const;
uint16_t size() const { return this->length; };
PropertyValue get_property(const PropertyIdentifier &prop) { return this->properties[prop]; };
friend std::ostream &operator<<(std::ostream &os, const std::byte &value);
friend std::ostream &operator<<(std::ostream &os, const MQTTProperties &value);
private:
int_vb length;
PropertyMap properties;
};
#endif // INCLUDE_CONTROL_PACKET_PROPERTY_HPP_

View File

@@ -1,5 +1,10 @@
#include "connect_ack.hpp"
#include "connect_packet.hpp"
#include <cstddef>
#include <session.hpp>
#include <sys/socket.h>
#include <unistd.h>
#include <vector>
Session::Session(const int &socket_fd) {
this->socket = socket_fd;
@@ -48,7 +53,12 @@ void Session::listen() {
switch (packet.get_packet_type()) {
case PacketType::CONNECT: {
ConnectPacket conn_packet(packet);
spdlog::trace(conn_packet.to_string());
spdlog::trace(conn_packet.as_string());
// ConnectACK ack;
// ack.set_reason_code(ConnectReasonCode::SUCCESS);
// std::vector<std::byte> ack_bytes = ack.as_bytes();
// send(this->socket, ack_bytes.data(), ack_bytes.size(), 0);
break;
}

View File

@@ -1,4 +1,5 @@
#include "binary_data.hpp"
#include "bytes.hpp"
#include "spdlog/spdlog.h"
#include <iomanip>
@@ -9,6 +10,10 @@ BinaryData::BinaryData() {
BinaryData::BinaryData(const std::vector<std::byte>::const_iterator &data_begin) {
this->data_size = std::to_integer<uint16_t>((*data_begin << 8) | *(std::next(data_begin, 1)));
auto data_end = std::next(data_begin, data_size);
if (std::distance(data_begin, data_end) < data_size)
throw std::runtime_error("Not enough bytes in the vector to construct BinaryData");
this->data.resize(this->data_size);
auto binary_data_begin = std::next(data_begin, 2);
@@ -16,7 +21,9 @@ BinaryData::BinaryData(const std::vector<std::byte>::const_iterator &data_begin)
std::copy(binary_data_begin, binary_data_end, this->data.begin());
spdlog::trace("New BinaryData with length " + std::to_string(this->size()));
std::ostringstream log_msg;
log_msg << "New BinaryData with length " << this->size() << ": " << *this;
spdlog::trace(log_msg.str());
}
std::ostream &operator<<(std::ostream &os, const BinaryData &value) { return os << value.as_string(); }
@@ -30,3 +37,9 @@ std::string BinaryData::as_string() const {
}
return binary_str.str();
}
std::vector<std::byte> BinaryData::as_bytes() const {
std::vector<std::byte> byte_vector = utilities::bytes::to_bytes(this->data_size);
byte_vector.insert(byte_vector.end(), data.begin(), data.end());
return byte_vector;
}

View File

@@ -6,7 +6,7 @@
#include <ostream>
#include <vector>
class BinaryData : TypeInterface {
class BinaryData : public TypeInterface {
public:
// Create a BinaryData from the start of byte vector
BinaryData();
@@ -15,9 +15,9 @@ public:
friend std::ostream &operator<<(std::ostream &, const BinaryData&);
size_t size() const final { return data_size; };
uint16_t size() const final { return data_size; };
std::string as_string() const final;
std::vector<std::byte> as_bytes() const final { return data; };
std::vector<std::byte> as_bytes() const final;
protected:
std::vector<std::byte> data;

View File

@@ -1,15 +1,15 @@
#ifndef INCLUDE_TYPES_TYPE_INTERFACE_HPP_
#define INCLUDE_TYPES_TYPE_INTERFACE_HPP_
#include <cstdint>
#include <string>
#include <vector>
class TypeInterface {
public:
TypeInterface() {};
~TypeInterface() = default;
virtual ~TypeInterface() = default;
virtual size_t size() const = 0;
virtual uint16_t size() const = 0;
virtual std::string as_string() const = 0;
virtual std::vector<std::byte> as_bytes() const = 0;
};

View File

@@ -1,20 +1,30 @@
// TODO: Implement UTF8 string verifications such as Page 17, line 274
#include "bytes.hpp"
#include "spdlog/spdlog.h"
#include <cstddef>
#include <iterator>
#include <utf8_string.hpp>
#include <vector>
#include <sstream>
UTF8String::UTF8String() { this->utf8_string = ""; }
UTF8String::UTF8String(const std::vector<std::byte>::const_iterator &data_begin) {
uint16_t data_size = std::to_integer<uint16_t>((*data_begin << 8) | *(std::next(data_begin, 1)));
auto data_end = std::next(data_begin, data_size);
if (std::distance(data_begin, data_end) < data_size)
throw std::runtime_error("Not enough bytes in the vector to construct UTF8String");
auto utf8_string_begin = reinterpret_cast<const char *>(&(*std::next(data_begin, 2)));
auto utf8_string_end = reinterpret_cast<const char *>(&(*std::next(data_begin, 2 + data_size)));
this->utf8_string.resize(data_size);
this->utf8_string.assign(utf8_string_begin, utf8_string_end);
spdlog::trace("New UTF8 String with length " + std::to_string(this->size()));
std::ostringstream log_msg;
log_msg << "New UTF8 String with length " << this->size() << ": " << *this;
spdlog::trace(log_msg.str());
}
std::ostream &operator<<(std::ostream &os, const UTF8String &value) {
@@ -23,6 +33,8 @@ std::ostream &operator<<(std::ostream &os, const UTF8String &value) {
}
std::vector<std::byte> UTF8String::as_bytes() const {
const std::byte* byte_ptr = reinterpret_cast<const std::byte*>(utf8_string.c_str());
return std::vector<std::byte>(byte_ptr, byte_ptr + utf8_string.size());
std::vector<std::byte> byte_vector = utilities::bytes::to_bytes(this->size());
std::vector<std::byte> byte_str = utilities::bytes::to_bytes(this->utf8_string);
byte_vector.insert(byte_vector.end(), byte_str.begin(), byte_str.end());
return byte_vector;
}

View File

@@ -7,13 +7,13 @@
#include <string>
#include <vector>
class UTF8String : TypeInterface {
class UTF8String : public TypeInterface {
public:
UTF8String();
UTF8String(const std::vector<std::byte>::const_iterator &data_begin);
~UTF8String() = default;
size_t size() const final { return utf8_string.size(); };
uint16_t size() const final { return static_cast<uint16_t>(utf8_string.size()); };
std::string as_string() const final { return this->utf8_string; };
std::vector<std::byte> as_bytes() const final;

View File

@@ -3,11 +3,15 @@
#include <cstddef>
#include <utf8_string_pair.hpp>
#include <vector>
#include <sstream>
UTF8StringPair::UTF8StringPair(const std::vector<std::byte>::const_iterator &data_begin) {
this->key = UTF8String(data_begin);
auto value_start_byte = std::next(data_begin, 2 + this->key.size());
auto value_start_byte = std::next(data_begin, this->key.as_bytes().size());
this->value = UTF8String(value_start_byte);
std::ostringstream log_msg;
log_msg << "New UTF8 String Pair with length " << this->as_bytes().size() << ": " << *this;
spdlog::trace(log_msg.str());
}
std::ostream &operator<<(std::ostream &os, const UTF8StringPair &value) {
@@ -17,7 +21,7 @@ std::ostream &operator<<(std::ostream &os, const UTF8StringPair &value) {
std::vector<std::byte> UTF8StringPair::as_bytes() const {
std::vector<std::byte> key_bytes = key.as_bytes();
std::vector<std::byte> value_bytes = key.as_bytes();
std::vector<std::byte> value_bytes = value.as_bytes();
key_bytes.insert(key_bytes.end(), value_bytes.begin(), value_bytes.end());
return key_bytes;
}

View File

@@ -4,7 +4,7 @@
#include <type_interface.hpp>
#include <utf8_string.hpp>
class UTF8StringPair : TypeInterface {
class UTF8StringPair : public TypeInterface {
public:
UTF8StringPair();
UTF8StringPair(const std::vector<std::byte>::const_iterator &data_begin);
@@ -12,7 +12,7 @@ public:
friend std::ostream &operator<<(std::ostream &os, const UTF8StringPair &value);
size_t size() const final { return key.size() + value.size(); };
uint16_t size() const final { return key.size() + value.size(); };
std::string as_string() const final { return key.as_string() + "=" + value.as_string(); };
std::vector<std::byte> as_bytes() const final;

View File

@@ -1,9 +1,10 @@
//TODO: This implemetation assumes 4 bytes from variable int always, according to specification it should occupy as many bytes as length()
#include "variable_byte_int.hpp"
#include "spdlog/spdlog.h"
#include <vector>
#include <sstream>
VariableByteInteger::VariableByteInteger() {
this->encoded_value.fill(std::byte(0));
this->decoded_value = 0;
}
@@ -14,25 +15,29 @@ VariableByteInteger::VariableByteInteger(const uint32_t &value) {
this->encoded_value = this->encode(value);
this->decoded_value = value;
spdlog::trace("New VariableByteInteger with length " + std::to_string(this->size()));
std::ostringstream log_msg;
log_msg << "New VariableByteInteger with length " << this->size() << ": " << *this;
spdlog::trace(log_msg.str());
}
VariableByteInteger::VariableByteInteger(const std::array<std::byte, 4> &value) {
this->encoded_value = value;
this->decoded_value = this->decode(value);
spdlog::trace("New VariableByteInteger with length " + std::to_string(this->size()));
VariableByteInteger::VariableByteInteger(const std::vector<std::byte> &value) {
new (this) VariableByteInteger(value.begin());
}
VariableByteInteger::VariableByteInteger(const std::vector<std::byte>::const_iterator &it) {
if (std::distance(it, it + 4) < 4)
throw std::runtime_error("Not enough bytes in the vector to construct VariableByteInteger");
std::array<std::byte, 4> value;
std::vector<std::byte> value;
value.resize(4);
std::copy(it, it+4, value.begin());
this->encoded_value = value;
this->decoded_value = this->decode(value);
spdlog::trace("New VariableByteInteger with length " + std::to_string(this->size()));
this->encoded_value = std::vector<std::byte>(value.begin(), value.begin() + this->bytes_count);
std::ostringstream log_msg;
log_msg << "New VariableByteInteger with length " << this->size() << ": " << *this;
spdlog::trace(log_msg.str());
}
VariableByteInteger::~VariableByteInteger() {}
@@ -50,11 +55,11 @@ std::ostream &operator<<(std::ostream &os, const VariableByteInteger &value) {
return os;
}
std::array<std::byte, 4> VariableByteInteger::encode(const uint32_t &value) {
std::vector<std::byte> VariableByteInteger::encode(const uint32_t &value) {
uint8_t encoded_byte = 0;
uint32_t value_to_encode = value;
std::array<std::byte, 4> encoded_value = {std::byte(0)};
std::vector<std::byte> encoded_value;
uint8_t current_byte = 0;
do {
@@ -64,7 +69,7 @@ std::array<std::byte, 4> VariableByteInteger::encode(const uint32_t &value) {
if (value_to_encode > 0)
encoded_byte |= 0x80;
encoded_value[current_byte] = std::byte(encoded_byte);
encoded_value.insert(encoded_value.end(), std::byte(encoded_byte));
current_byte++;
} while (value_to_encode > 0);
@@ -73,9 +78,9 @@ std::array<std::byte, 4> VariableByteInteger::encode(const uint32_t &value) {
return encoded_value;
}
uint32_t VariableByteInteger::decode(const std::array<std::byte, 4> &value) {
uint32_t VariableByteInteger::decode(const std::vector<std::byte> &value) {
uint8_t encoded_byte = 0;
std::array<std::byte, 4> value_to_decode = value;
std::vector<std::byte> value_to_decode = value;
uint32_t decoded_value = 0;
uint8_t current_byte = 0;

View File

@@ -6,7 +6,7 @@
#include <ostream>
#include <type_interface.hpp>
#include <type_traits>
#include <utilities.hpp>
#include <bit.hpp>
#include <vector>
class VariableByteInteger : public TypeInterface {
@@ -18,7 +18,7 @@ public:
// Create a VariableByte Integer from a byte array that representes a
// VariableByteInteger
VariableByteInteger(const std::array<std::byte, 4> &encoded_value);
VariableByteInteger(const std::vector<std::byte> &encoded_value);
// Create a VariableByte Integer from the start of byte vector that representes a
// VariableByteInteger
@@ -26,9 +26,9 @@ public:
~VariableByteInteger();
size_t size() const final { return this->bytes_count; };
uint16_t size() const final { return this->bytes_count; };
std::string as_string() const final { return std::to_string(this->decoded_value); };
std::vector<std::byte> as_bytes() const final { return std::vector<std::byte>(encoded_value.begin(), encoded_value.end()); };
std::vector<std::byte> as_bytes() const final { return this->encoded_value; };
template <typename T> typename std::enable_if<std::is_integral<T>::value, VariableByteInteger &>::type operator=(T &);
@@ -36,16 +36,14 @@ public:
inline operator uint32_t() const { return this->decoded_value; };
std::array<std::byte, 4> bytes() const { return this->encoded_value; };
// Maximum allowed value for a VariableByte Integer
inline uint32_t max() const { return 268435455; };
private:
std::array<std::byte, 4> encoded_value;
std::array<std::byte, 4> encode(const unsigned int &);
std::vector<std::byte> encoded_value;
std::vector<std::byte> encode(const unsigned int &);
uint32_t decoded_value;
uint32_t decode(const std::array<std::byte, 4> &);
uint32_t decode(const std::vector<std::byte> &);
uint8_t bytes_count;
};

View File

@@ -0,0 +1,10 @@
FILE(GLOB CPP_FILES_LOCAL CONFIGURE_DEPENDS *.cpp)
FILE(GLOB HPP_FILES_LOCAL CONFIGURE_DEPENDS *.hpp)
target_sources(libmqttd
PRIVATE ${CPP_FILES_LOCAL}
PUBLIC ${HPP_FILES_LOCAL}
)
target_include_directories(libmqttd PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

View File

@@ -0,0 +1,18 @@
#ifndef INCLUDE_UTILITIES_BIT_HPP_
#define INCLUDE_UTILITIES_BIT_HPP_
namespace utilities {
namespace bit {
template <typename T> T set(T num, int position) {
T mask = static_cast<T>(1) << position;
return num | mask;
}
template <typename T> bool get(T num, int position) {
T mask = static_cast<T>(1) << position;
return (num & mask) != 0;
}
} // namespace bit
} // namespace utilities
#endif // INCLUDE_UTILITIES_BIT_HPP_

View File

@@ -0,0 +1,13 @@
#include <bytes.hpp>
#include <algorithm>
namespace utilities {
namespace bytes {
std::vector<std::byte> to_bytes(const std::string &value) {
std::vector<std::byte> bytes;
bytes.reserve(value.size());
std::transform(std::begin(value), std::end(value), std::back_inserter(bytes), [](char c) { return std::byte(c); });
return bytes;
}
} // namespace bytes
} // namespace utilities

View File

@@ -0,0 +1,19 @@
#ifndef INCLUDE_UTILITIES_BYTES_HPP_
#define INCLUDE_UTILITIES_BYTES_HPP_
#include <string>
#include <vector>
namespace utilities {
namespace bytes {
std::vector<std::byte> to_bytes(const std::string &value);
template <typename T> std::vector<std::byte> to_bytes(const T &value) {
const std::byte *byte_ptr = reinterpret_cast<const std::byte *>(&value);
return std::vector<std::byte>(byte_ptr, byte_ptr + sizeof(T));
}
} // namespace bytes
} // namespace utilities
#endif // INCLUDE_UTILITIES_BYTES_HPP_

View File

@@ -1,23 +1,11 @@
#ifndef INCLUDE_LIBMQTTD_UTILITIES_HPP_
#define INCLUDE_LIBMQTTD_UTILITIES_HPP_
#ifndef INCLUDE_UTILITIES_TYPES_HPP_
#define INCLUDE_UTILITIES_TYPES_HPP_
#include <cstdint>
#include <memory>
#include <type_traits>
#include <vector>
namespace utilities {
namespace bit {
template <typename T> T set(T num, int position) {
T mask = static_cast<T>(1) << position;
return num | mask;
}
template <typename T> bool get(T num, int position) {
T mask = static_cast<T>(1) << position;
return (num & mask) != 0;
}
} // namespace bit
namespace types {
template <typename T> typename std::enable_if<std::is_integral<T>::value, T>::type get_fixed_size_integer(const std::vector<std::byte>::const_iterator &it) {
T value = 0;
@@ -30,4 +18,4 @@ template <typename T> typename std::enable_if<std::is_integral<T>::value, T>::ty
} // namespace types
} // namespace utilities
#endif // INCLUDE_LIBMQTTD_UTILITIES_HPP_
#endif // INCLUDE_UTILITIES_TYPES_HPP_

View File

@@ -1,5 +1,5 @@
#define MQTTD_VERSION_MAJOR 0
#define MQTTD_VERSION_MINOR 0
#define MQTTD_VERSION_PATCH 1
#define MQTTD_COMMIT_HASH c34d78d4972974acab6a152c60f8373ade45798e
#define MQTTD_BUILD_TIMESTAMP 1707002675
#define MQTTD_COMMIT_HASH 65302458650650a7d293cf3fbe8bbf65501f242e
#define MQTTD_BUILD_TIMESTAMP 1707058988

View File

@@ -1,74 +1,74 @@
#include <array>
#include <vector>
#include <catch2/catch_test_macros.hpp>
#include <cstddef>
#include <variable_byte_int.hpp>
TEST_CASE("Variable Byte Integer Encoding", "[Variable Byte Integer]") {
int_vb num1 = 42;
std::array<std::byte, 4> expected_bytes1 = {std::byte(42), std::byte(0),
std::vector<std::byte> expected_bytes1 = {std::byte(42), std::byte(0),
std::byte(0), std::byte(0)};
REQUIRE(num1.bytes() == expected_bytes1);
REQUIRE(num1.as_bytes() == expected_bytes1);
int_vb num2 = 2048;
std::array<std::byte, 4> expected_bytes2 = {std::byte(128), std::byte(16),
std::vector<std::byte> expected_bytes2 = {std::byte(128), std::byte(16),
std::byte(0), std::byte(0)};
REQUIRE(num2.bytes() == expected_bytes2);
REQUIRE(num2.as_bytes() == expected_bytes2);
REQUIRE_THROWS_AS([&](){ int_vb num3 = 4294967167;}(), std::runtime_error);
int_vb num4 = 0;
std::array<std::byte, 4> expected_bytes4 = {std::byte(0), std::byte(0),
std::vector<std::byte> expected_bytes4 = {std::byte(0), std::byte(0),
std::byte(0), std::byte(0)};
REQUIRE(num4.bytes() == expected_bytes4);
REQUIRE(num4.as_bytes() == expected_bytes4);
int_vb num5 = 16383;
std::array<std::byte, 4> expected_bytes5 = {std::byte(255), std::byte(127),
std::vector<std::byte> expected_bytes5 = {std::byte(255), std::byte(127),
std::byte(0), std::byte(0)};
REQUIRE(num5.bytes() == expected_bytes5);
REQUIRE(num5.as_bytes() == expected_bytes5);
int_vb num6 = 2097151;
std::array<std::byte, 4> expected_bytes6 = {std::byte(255), std::byte(255),
std::vector<std::byte> expected_bytes6 = {std::byte(255), std::byte(255),
std::byte(127), std::byte(0)};
REQUIRE(num6.bytes() == expected_bytes6);
REQUIRE(num6.as_bytes() == expected_bytes6);
}
TEST_CASE("VariableByte Integer Decoding", "[Variable Byte Integer]") {
// Caso de teste 1
uint32_t expected_num1 = 42;
std::array<std::byte, 4> bytes1 = {std::byte(42), std::byte(0),
std::vector<std::byte> bytes1 = {std::byte(42), std::byte(0),
std::byte(0), std::byte(0)};
int_vb num1(bytes1);
REQUIRE(uint32_t(num1) == expected_num1);
// Caso de teste 2
uint32_t expected_num2 = 2048;
std::array<std::byte, 4> bytes2 = {std::byte(128), std::byte(16),
std::vector<std::byte> bytes2 = {std::byte(128), std::byte(16),
std::byte(0), std::byte(0)};
int_vb num2(bytes2);
REQUIRE(uint32_t(num2) == expected_num2);
// Caso de teste 3
std::array<std::byte, 4> bytes3 = {std::byte(255), std::byte(255),
std::vector<std::byte> bytes3 = {std::byte(255), std::byte(255),
std::byte(255), std::byte(255)};
REQUIRE_THROWS_AS([&](){int_vb num3(bytes3);}(), std::runtime_error);
// Caso de teste 4
uint32_t expected_num4 = 0;
std::array<std::byte, 4> bytes4 = {std::byte(0), std::byte(0),
std::vector<std::byte> bytes4 = {std::byte(0), std::byte(0),
std::byte(0), std::byte(0)};
int_vb num4(bytes4);
REQUIRE(uint32_t(num4) == expected_num4);
// Caso de teste 5
uint32_t expected_num5 = 16383;
std::array<std::byte, 4> bytes5 = {std::byte(255), std::byte(127),
std::vector<std::byte> bytes5 = {std::byte(255), std::byte(127),
std::byte(0), std::byte(0)};
int_vb num5(bytes5);
REQUIRE(uint32_t(num5) == expected_num5);
// Caso de teste 6
uint32_t expected_num6 = 2097151;
std::array<std::byte, 4> bytes6 = {std::byte(255), std::byte(255),
std::vector<std::byte> bytes6 = {std::byte(255), std::byte(255),
std::byte(127), std::byte(0)};
int_vb num6(bytes6);
REQUIRE(uint32_t(num6) == expected_num6);