feat: utilities namespace, as_bytes function, mqtt properties class and better trace logs
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
add_library(libmqttd "")
|
||||
|
||||
add_subdirectory(utilities)
|
||||
add_subdirectory(types)
|
||||
add_subdirectory(network)
|
||||
add_subdirectory(protocol)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <ostream>
|
||||
|
||||
|
||||
enum class PacketType : uint32_t {
|
||||
enum class PacketType : uint8_t {
|
||||
RESERVED = 0,
|
||||
CONNECT = 1,
|
||||
CONNACK = 2,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
10
src/libmqttd/utilities/CMakeLists.txt
Normal file
10
src/libmqttd/utilities/CMakeLists.txt
Normal 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})
|
||||
|
||||
18
src/libmqttd/utilities/bit.hpp
Normal file
18
src/libmqttd/utilities/bit.hpp
Normal 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_
|
||||
13
src/libmqttd/utilities/bytes.cpp
Normal file
13
src/libmqttd/utilities/bytes.cpp
Normal 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
|
||||
19
src/libmqttd/utilities/bytes.hpp
Normal file
19
src/libmqttd/utilities/bytes.hpp
Normal 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_
|
||||
@@ -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_
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user