Skip to content
Base_Packet.h 23.7 KiB
Newer Older
Valerio Pastore's avatar
Valerio Pastore committed
#pragma once
Valerio Pastore's avatar
Valerio Pastore committed

#include <vector>
Valerio Pastore's avatar
Valerio Pastore committed
#include <unordered_map>
Valerio Pastore's avatar
Valerio Pastore committed
#include <string>
#include <cstring>
#include <algorithm>
Valerio Pastore's avatar
Valerio Pastore committed
#include <numeric>
Valerio Pastore's avatar
Valerio Pastore committed
#include <iostream>
#include <cmath>
Valerio Pastore's avatar
Valerio Pastore committed
#include <functional>
Valerio Pastore's avatar
Valerio Pastore committed
#include <optional>
Valerio Pastore's avatar
Valerio Pastore committed
#include <ctime>
#include <iomanip>
Valerio Pastore's avatar
.  
Valerio Pastore committed
namespace inaf::oasbo::Packets {

/**
 * @class BasePacketStructure
 * @brief Represents the structure of a base packet.
 *
 * The BasePacketStructure class provides functionality to define and manipulate the structure of a base packet.
 * It stores information about the fields, their sizes, offsets, and mappings between field names and indices.
 * The class also provides methods to retrieve information about the structure, such as the byte size, field offsets, and field names.
 *
 * @note This is an abstract base class and should be derived to define specific packet structures. In particular, you should define
 * the sourceReadingFunc function.
 */
class BasePacketStructure {
protected:
Valerio Pastore's avatar
Valerio Pastore committed
	/**
Valerio Pastore's avatar
.  
Valerio Pastore committed
	 * @brief Type alias for the packet structure.
Valerio Pastore's avatar
Valerio Pastore committed
	 *
Valerio Pastore's avatar
.  
Valerio Pastore committed
	 * This alias represents the structure of a packet. It is defined as a vector of tuples, where each tuple contains three elements:
	 * - The first element is of type uint and represents the field index.
	 * - The second element is of type std::string and represents the field name.
	 * - The third element is of type uint and represents the field bit size.
	 */
	using Structure = std::vector<std::tuple<uint, std::string, uint>>; /**< Type alias for the packet structure. */
Valerio Pastore's avatar
.  
Valerio Pastore committed
	std::string source; /**< The source of the packet. */
	Structure structure; /**< The structure of the packet. */
	uint byteSize; /**< The size of the packet in bytes. */
	std::vector<uint> fieldSizes; /**< vector of the sizes of the fields in the packet. */
	std::unordered_map<uint, uint> indexToOffsetsMap; /**< Mapping of field index to field offset. */
	std::unordered_map<std::string, uint> fieldNameToIndexMap; /**< Mapping of field name to field index. */
	std::unordered_map<uint, std::string> indexToFieldNameMap; /**< Mapping of field index to field name. */
	std::function<Structure(std::string)> sourceReadingFunc; /**< Function to read the packet source and return the structure. */
Valerio Pastore's avatar
.  
Valerio Pastore committed
	/**
	 * @brief Updates the field sizes based on the structure.
	 * @param structure The structure of the packet.
	 */
	void updateFieldSizes(const Structure &structure) {
		std::for_each(structure.begin(), structure.end(),
				[&](const std::tuple<uint, std::string, uint> &tup) {
					this->fieldSizes.push_back(std::get<2>(tup));
				});
	}
Valerio Pastore's avatar
.  
Valerio Pastore committed
	/**
	 * @brief Updates the field offsets based on the structure.
	 * @param structure The structure of the packet.
	 */
	void updateFieldOffsets(const Structure &structure) {
		uint offset = 0;
		for (size_t i = 0; i < structure.size(); i++) {
			indexToOffsetsMap[i] = offset;
			offset += std::get<2>(structure[i]);
Valerio Pastore's avatar
Valerio Pastore committed
		}
Valerio Pastore's avatar
.  
Valerio Pastore committed
	}
Valerio Pastore's avatar
Valerio Pastore committed

Valerio Pastore's avatar
.  
Valerio Pastore committed
	/**
	 * @brief Updates the field name and index maps based on the structure.
	 * @param structure The structure of the packet.
	 */
	void updateFieldNameAndIndexMap(const Structure &structure) {
		std::for_each(structure.begin(), structure.end(),
				[&](const std::tuple<uint, std::string, uint> &tup) {
					this->fieldNameToIndexMap[std::get<1>(tup)] = std::get<0>(
							tup);
					this->indexToFieldNameMap[std::get<0>(tup)] = std::get<1>(
							tup);
				});
	}
Valerio Pastore's avatar
Valerio Pastore committed

Valerio Pastore's avatar
.  
Valerio Pastore committed
	/**
	 * @brief Updates the packet structure based on the source.
	 * @param source The source of the packet structure.
	 */
	void updateStructure(std::string source) {
		this->source = source;
		this->structure = sourceReadingFunc(source);
		updateFieldSizes(structure);
		updateFieldOffsets(structure);
		updateFieldNameAndIndexMap(structure);
		uint bitSize = std::accumulate(fieldSizes.begin(), fieldSizes.end(), 0);
		this->byteSize = bitSize % 8 == 0 ? bitSize / 8 : bitSize / 8 + 1;
	}

public:
	/**
	 * @brief Destructor.
	 */
	virtual ~BasePacketStructure() = default;
Valerio Pastore's avatar
Valerio Pastore committed

Valerio Pastore's avatar
.  
Valerio Pastore committed
	/**
	 * @brief Constructor.
	 * @param source The source of the packet structure.
	 * @param sourceReadingFunc The function to read the packet source and return the structure.
	 */
	BasePacketStructure(std::string source,
			std::function<Structure(std::string)> sourceReadingFunc) :
			source(source), sourceReadingFunc(sourceReadingFunc) {
		this->updateStructure(source);
	}
Valerio Pastore's avatar
Valerio Pastore committed

Valerio Pastore's avatar
.  
Valerio Pastore committed
	/**
	 * @brief Copy constructor.
	 * @param other The other BasePacketStructure object to copy from.
	 */
	BasePacketStructure(const BasePacketStructure &other) {
		this->source = other.source;
		this->byteSize = other.byteSize;
		this->fieldSizes = other.fieldSizes;
		this->indexToOffsetsMap = other.indexToOffsetsMap;
		this->fieldNameToIndexMap = other.fieldNameToIndexMap;
		this->indexToFieldNameMap = other.indexToFieldNameMap;
		this->sourceReadingFunc = other.sourceReadingFunc;
	}
Valerio Pastore's avatar
Valerio Pastore committed

Valerio Pastore's avatar
.  
Valerio Pastore committed
	/**
	 * @brief Changes the source of the packet and updates the structure.
	 * @param source The new source of the packet structure.
	 */
	void changeSource(std::string source) {
		updateStructure(source);
	}
Valerio Pastore's avatar
Valerio Pastore committed

Valerio Pastore's avatar
.  
Valerio Pastore committed
	/**
	 * @brief Gets the size of the packet in bytes.
	 * @return The size of the packet in bytes.
	 */
	uint getByteSize() {
		return this->byteSize;
	}
Valerio Pastore's avatar
Valerio Pastore committed

Valerio Pastore's avatar
.  
Valerio Pastore committed
	/**
	 * @brief Gets the source of the packet structure.
	 * @return The source of the packet structure.
	 */
	std::string getSource() {
		return this->source;
	}
Valerio Pastore's avatar
Valerio Pastore committed

Valerio Pastore's avatar
.  
Valerio Pastore committed
	/**
	 * @brief Gets the bit offset of a field in the packet structure.
	 * @param index The index of the field.
	 * @return The bit offset of the field, or std::nullopt if the field does not exist.
	 */
	std::optional<uint> bitOffsetOf(uint index) {
		if (index <= this->numberOfFields()) {
			return indexToOffsetsMap.at(index);
		} else {
			time_t now = time(nullptr);
			std::cerr << "["
					<< std::put_time(localtime(&now), "%Y-%m-%d %H:%M:%S")
					<< "]\t[Base Packet]\t" << "No field at " << index
					<< ", max is " << numberOfFields() << ", returning nullopt."
					<< std::endl;
			return std::nullopt;
		}
	}
Valerio Pastore's avatar
Valerio Pastore committed

Valerio Pastore's avatar
.  
Valerio Pastore committed
	/**
	 * @brief Gets the bit size of a field in the packet structure.
	 * @param index The index of the field.
	 * @return The bit size of the field, or std::nullopt if the field does not exist.
	 */
	std::optional<uint> bitSizeOf(uint index) {
		if (index <= this->numberOfFields()) {
			return this->fieldSizes[index];
		} else {
			time_t now = time(nullptr);
			std::cerr << "["
					<< std::put_time(localtime(&now), "%Y-%m-%d %H:%M:%S")
					<< "]\t[Base Packet]\t" << "No field at " << index
					<< ", max is " << numberOfFields() << ", returning nullopt."
					<< std::endl;
			return std::nullopt;
		}
	}
Valerio Pastore's avatar
Valerio Pastore committed

	/**
Valerio Pastore's avatar
.  
Valerio Pastore committed
	 * @brief Gets the index of a field in the packet structure based on its name.
	 * @param fieldName The name of the field.
	 * @return The index of the field, or std::nullopt if the field does not exist.
	 */
	std::optional<uint> indexOfField(std::string fieldName) {
		std::transform(fieldName.begin(), fieldName.end(), fieldName.begin(),
				[](unsigned char c) {
					return std::tolower(c);
				});
		try {
			return this->fieldNameToIndexMap.at(fieldName);
		} catch (const std::out_of_range &oor) {
			time_t now = time(nullptr);
			std::cerr << "["
					<< std::put_time(localtime(&now), "%Y-%m-%d %H:%M:%S")
					<< "]\t[Base Packet]\t" << "No field of name " << fieldName
					<< ", returning nullopt." << std::endl;
			return std::nullopt;
		}
	}
Valerio Pastore's avatar
Valerio Pastore committed

Valerio Pastore's avatar
.  
Valerio Pastore committed
	/**
	 * @brief Gets the name of a field in the packet based on its index.
	 * @param index The index of the field.
	 * @return The name of the field, or std::nullopt if the field does not exist.
	 */
	std::optional<std::string> fieldNameOfIndex(uint index) {
		try {
			return this->indexToFieldNameMap.at(index);
		} catch (const std::out_of_range &oor) {
			time_t now = time(nullptr);
			std::cerr << "["
					<< std::put_time(localtime(&now), "%Y-%m-%d %H:%M:%S")
					<< "]\t[Base Packet]\t" << "No field at " << index
					<< ", max is " << numberOfFields() << ", returning nullopt."
					<< std::endl;
			return std::nullopt;
		}
	}
Valerio Pastore's avatar
Valerio Pastore committed

Valerio Pastore's avatar
.  
Valerio Pastore committed
	/**
	 * @brief Gets the number of fields in the packet.
	 * @return The number of fields in the packet.
	 */
	uint numberOfFields() {
		return this->fieldSizes.size();
	}
Valerio Pastore's avatar
Valerio Pastore committed

Valerio Pastore's avatar
.  
Valerio Pastore committed
	/**
	 * @brief Gets the field name to index map.
	 * @return The field name to index map.
	 */
	std::unordered_map<std::string, uint> getFieldNameToIndexMap() const {
		return this->fieldNameToIndexMap;
	}
Valerio Pastore's avatar
Valerio Pastore committed

Valerio Pastore's avatar
.  
Valerio Pastore committed
	/**
	 * @brief Gets the index to offset map.
	 * @return The index to offset map.
	 */
	std::unordered_map<uint, uint> getIndexToOffsetsMap() const {
		return this->indexToOffsetsMap;
	}
Valerio Pastore's avatar
Valerio Pastore committed

Valerio Pastore's avatar
.  
Valerio Pastore committed
	/**
	 * @brief Gets the index to field name map.
	 * @return The index to field name map.
	 */
	std::unordered_map<uint, std::string> getIndexToFieldNameMap() const {
		return this->indexToFieldNameMap;
	}
};

/**
 * @brief Represents an iterator that iterates over individual fields in the packet.
 *
 * The `bit_iterator` class provides a random access iterator interface for iterating over individual fields in the packet.
 * It is templated on the value type of the packet fields. I.e. if the largest packet fields is 32-bit, then the value type should be at least uint32_t.
 *
 * @tparam ValueType The value type of the values returned by the iterator. The value
 */
template<typename ValueType>
class bit_iterator {

	using iterator_category = std::random_access_iterator_tag;
	using value_type = ValueType;
	using difference_type = std::ptrdiff_t;
	using pointer = ValueType *;
	using reference = ValueType &;

private:
Valerio Pastore's avatar
Valerio Pastore committed

Valerio Pastore's avatar
.  
Valerio Pastore committed
	/**
	 * @brief Pointer to the packet data.
	 */
	const uint8_t *m_data;
	/**
	 * @brief Current offset within the packet.
	 */
	int m_offset;
	/**
	 * @brief Pointer to the BasePacketStructure.
	 */
	BasePacketStructure *m_structure;
Valerio Pastore's avatar
Valerio Pastore committed

Valerio Pastore's avatar
.  
Valerio Pastore committed
	/**
	 * @brief Function to retrieve the value from data based on offset and size.
	 */
	std::function<ValueType(const uint8_t*, uint, uint)> m_func;

public:
	bit_iterator(const uint8_t *data, int offset,
			BasePacketStructure *structure,
			std::function<ValueType(const uint8_t*, uint, uint)> func) :
			m_data(data), m_offset(offset), m_structure(structure), m_func(func) {
	}

	bit_iterator(const bit_iterator &other) :
			m_data(other.m_data), m_offset(other.m_offset), m_structure(
					other.m_structure), m_func(other.m_func) {
	}

	bit_iterator& operator++() {
		++m_offset;
		return *this;
	}

	bit_iterator operator++(int) {
		bit_iterator temp(*this);
		++m_offset;
		return temp;
	}

	bit_iterator& operator--() {
		--m_offset;
		return *this;
	}

	bit_iterator operator--(int) {
		bit_iterator temp(*this);
		--m_offset;
		return temp;
	}

	bit_iterator operator+(int n) const {
		return bit_iterator(m_data, m_offset + n, m_structure, m_func);
	}

	bit_iterator operator-(int n) const {
		return bit_iterator(m_data, m_offset - n, m_structure, m_func);
	}

	int operator-(const bit_iterator &other) const {
		return m_offset - other.m_offset;
	}

	bool operator==(const bit_iterator &other) const {
		return m_data == other.m_data && m_offset == other.m_offset;
	}

	bool operator!=(const bit_iterator &other) const {
		return !(*this == other);
	}

	ValueType operator*() const {
		auto offset = m_structure->bitOffsetOf(m_offset); // offset from the beginning of the byte
		auto num_bits = m_structure->bitSizeOf(m_offset);
		return m_func(m_data, offset.value(), num_bits.value());
	}
};

/**
 * @brief Represents a template class for handling packets with a specific structure.
 *
 * The `BasePacketTempl` class provides a generic template for handling packets with a specified structure.
 * It includes methods for reading and writing values from/to memory, as well as other utility functions.
 *
 * @tparam ValueType The value type of the largest packet fields. i.e The value type should be at least uint32_t if the largest packet field is 32-bit.
 */
template<typename ValueType>
class BasePacketTempl {
protected:
	BasePacketStructure *packetStructure; /**< Pointer to the structure defining the packet. */
	uint8_t *memoryPointer; /**< Pointer to the memory containing packet data. */
Valerio Pastore's avatar
Valerio Pastore committed

Valerio Pastore's avatar
.  
Valerio Pastore committed
	/**
	 * @brief reads a binary value from a memory location pointed to by binaryPointer.
	 * The binary value is represented by num_bits bits starting from the offset-th bit in the memory.
	 *
	 * @param binaryPointer Pointer to the memory containing the binary value.
	 * @param offset The offset of the first bit of the binary value.
	 * @param num_bits The number of bits to read.
	 * @return ValueType The value read from memory.
	 */
	static ValueType _readValueFromMemoryAt_(const uint8_t *binaryPointer,
			uint offset, uint num_bits) {
		// Calculate the bit offset from the byte offset:
		uint bit_offset = offset % 8;
		// Calculate the byte offset from the bit offset:
		uint byte_offset = offset / 8;
		ValueType value = 0;
		ValueType bit = 1;

		for (uint i = 0; i < num_bits; i++) {
			// Calculate the byte and bit index of the current bit:
			uint byte_index = byte_offset + (bit_offset + i) / 8;
			uint bit_index = (bit_offset + i) % 8;
			uint8_t byte = binaryPointer[byte_index];
			// Create a bit mask to isolate the desired bit:
			uint8_t bit_mask = 1 << (7 - bit_index);
			// Set the corresponding bit in the return value if the retrieved bit is 1:
			value |= (byte & bit_mask) ? (bit << (num_bits - i - 1)) : 0;
		}
		return value;
	}
Valerio Pastore's avatar
Valerio Pastore committed

Valerio Pastore's avatar
.  
Valerio Pastore committed
	/**
	 * @brief Calculates the minimum number of bits required to represent a given value.
	 *
	 * @param value The value to calculate the minimum bits for.
	 * @return size_t The minimum number of bits required.
	 */
	size_t minBitsRequired(size_t value) const {
		// Handle special cases
		size_t bitsNeeded = 1;
Valerio Pastore's avatar
Valerio Pastore committed

Valerio Pastore's avatar
.  
Valerio Pastore committed
		while (value != 0) {
			bitsNeeded++;
			value >>= 1;
Valerio Pastore's avatar
.  
Valerio Pastore committed
		return bitsNeeded;
	}
Valerio Pastore's avatar
Valerio Pastore committed

Valerio Pastore's avatar
.  
Valerio Pastore committed
public:
Valerio Pastore's avatar
Valerio Pastore committed
	/**
Valerio Pastore's avatar
.  
Valerio Pastore committed
	 * @brief Constructor for the BasePacketTempl class.
Valerio Pastore's avatar
Valerio Pastore committed
	 *
Valerio Pastore's avatar
.  
Valerio Pastore committed
	 * @param structure The structure defining the packet.
	 */
	BasePacketTempl(BasePacketStructure &structure) {
		this->packetStructure = new BasePacketStructure(structure);
		this->memoryPointer = new uint8_t[structure.getByteSize()];
	}
Valerio Pastore's avatar
Valerio Pastore committed

Valerio Pastore's avatar
.  
Valerio Pastore committed
	/**
	 * @brief Copy constructor for the BasePacketTempl class.
	 *
	 * @param other The other BasePacketTempl object to copy.
	 */
	BasePacketTempl(const BasePacketTempl &other) {
		this->packetStructure = new BasePacketStructure(
				other.getPacketStructure());
		this->memoryPointer = new uint8_t[other.packetStructure->getByteSize()];
		std::memcpy(this->memoryPointer, other.memoryPointer,
				other.packetStructure->getByteSize());
	}
Valerio Pastore's avatar
Valerio Pastore committed

Valerio Pastore's avatar
.  
Valerio Pastore committed
	/**
	 * @brief  Destructor  for the BasePacketTempl class.
	 */
	virtual ~BasePacketTempl() = default;
Valerio Pastore's avatar
Valerio Pastore committed

Valerio Pastore's avatar
.  
Valerio Pastore committed
	/**
	 * @brief Updates the packet structure.
	 * @param structure The new packet structure.
	 */
	void updatePacketStructure(BasePacketStructure &structure) {
		size_t newSize = structure.getByteSize();
		size_t oldSize = this->packetStructure->getByteSize();
Valerio Pastore's avatar
Valerio Pastore committed

Valerio Pastore's avatar
.  
Valerio Pastore committed
		uint8_t *buff = new uint8_t[newSize];
		std::memset(buff, 0, newSize);
		std::memcpy(buff, memoryPointer, std::min(newSize, oldSize));
		delete this->memoryPointer;
Valerio Pastore's avatar
Valerio Pastore committed

Valerio Pastore's avatar
.  
Valerio Pastore committed
		this->memoryPointer = new uint8_t[newSize];
		std::memcpy(memoryPointer, buff, newSize);
		delete[] buff;
		this->packetStructure = &structure;
	}
Valerio Pastore's avatar
Valerio Pastore committed

Valerio Pastore's avatar
.  
Valerio Pastore committed
	/**
	 * @brief Reads a value from the memory at a given index.
	 * @param index The index of the field to read.
	 * @return std::optional<ValueType> The value read from memory, or std::nullopt if the field does not exist.
	 */
	std::optional<ValueType> readValueFromMemoryAt(uint index) const {
		auto offset = packetStructure->bitOffsetOf(index); // offset from the beginning of the byte
		auto num_bits = packetStructure->bitSizeOf(index); // remaining bits to read
		if (offset.has_value() && num_bits.has_value())
			return _readValueFromMemoryAt_(memoryPointer, offset.value(),
					num_bits.value());
		else {
			time_t now = time(nullptr);
			std::cerr << "["
					<< std::put_time(localtime(&now), "%Y-%m-%d %H:%M:%S")
					<< "]\t[Base Packet]\t" << "Error: No field at " << index
					<< ", max is " << packetStructure->numberOfFields()
					<< ", returning nullopt" << std::endl;
			return std::nullopt;
		}
	}
Valerio Pastore's avatar
Valerio Pastore committed

Valerio Pastore's avatar
.  
Valerio Pastore committed
	/**
	 * @brief Returns a pointer to the memory in which stores the packet.
	 *
	 * This function returns a constant pointer to the memory.
	 *
	 * @return A constant pointer to the memory.
	 */
	uint8_t const* getPointerToMemory() const {
		return memoryPointer;
	}
Valerio Pastore's avatar
Valerio Pastore committed

Valerio Pastore's avatar
.  
Valerio Pastore committed
	/**
	 * @brief Copies data from a source memory location to the packet's memory.
	 *
	 * @param from Pointer to the source memory location.
	 * @param size Number of bytes to copy.
	 * @return int The number of bytes copied.
	 */
	int copyToMemory(const uint8_t *from, uint size) {
		uint max_writable = this->packetStructure->getByteSize();
		if (size > max_writable) {
			time_t now = time(nullptr);
			std::cerr << "["
					<< std::put_time(localtime(&now), "%Y-%m-%d %H:%M:%S")
					<< "]\t[Base Packet]\t" << "Error: you are trying to copy "
					<< size << " byte where the max size is: " << max_writable
					<< std::endl;
			std::cerr << "["
					<< std::put_time(localtime(&now), "%Y-%m-%d %H:%M:%S")
					<< "]\t[Base Packet]\t" << "\tI copy only until "
					<< max_writable << " byte" << std::endl;
			std::memcpy(memoryPointer, from, max_writable);
			return max_writable;
		} else {
			std::memcpy(memoryPointer, from, size);
			return size;
		}
	}
Valerio Pastore's avatar
Valerio Pastore committed

Valerio Pastore's avatar
.  
Valerio Pastore committed
	/**
	 * Copies data from the given memory location to the packet's memory buffer.
	 *
	 * @param from Pointer to the source memory location.
	 * @param size Number of bytes to copy.
	 * @param offset Offset in the packet's memory buffer to start copying to.
	 * @return The number of bytes copied.
	 */
	int copyToMemory(const uint8_t *from, uint size, uint offset) {
		uint max_writable = this->packetStructure->getByteSize();
		if (offset > max_writable) {
			time_t now = time(nullptr);
			std::cerr << "["
					<< std::put_time(localtime(&now), "%Y-%m-%d %H:%M:%S")
					<< "]\t[Base Packet]\t"
					<< "Error: you are trying to copy starting from " << offset
					<< " byte where the max size is: " << max_writable
					<< std::endl;
			return -1;
		}
		if (size + offset > max_writable) {
			time_t now = time(nullptr);
			std::cerr << "["
					<< std::put_time(localtime(&now), "%Y-%m-%d %H:%M:%S")
					<< "]\t[Base Packet]\t" << "Error: you are trying to copy "
					<< size + offset << " byte where the max size is: "
					<< max_writable << std::endl;
			std::cerr << "["
					<< std::put_time(localtime(&now), "%Y-%m-%d %H:%M:%S")
					<< "]\t[Base Packet]\t" << "\tI copy only until "
					<< max_writable << " byte" << std::endl;
			int to_write = max_writable - offset;
			std::memcpy(&memoryPointer[offset], from, to_write);
			return to_write;
		} else {
			std::memcpy(&memoryPointer[offset], from, size);
			return size;
		}
	}
Valerio Pastore's avatar
Valerio Pastore committed

Valerio Pastore's avatar
.  
Valerio Pastore committed
	/**
	 * @brief Accesses the value at the specified index in the packet.
	 *
	 * @param index The index of the value to access.
	 * @return An optional containing the value at the specified index, if it exists.
	 */
	std::optional<ValueType> operator[](uint index) const {
		return readValueFromMemoryAt(index);
	}
Valerio Pastore's avatar
Valerio Pastore committed

Valerio Pastore's avatar
.  
Valerio Pastore committed
	/**
	 * @brief Accesses the value of a field in the packet by its field name.
	 *
	 * @param fieldName The name of the field to access.
	 * @return An optional value of the specified field type. If the field does not exist, the optional will be empty.
	 */
	std::optional<ValueType> operator[](std::string fieldName) const {
		std::transform(fieldName.begin(), fieldName.end(), fieldName.begin(),
				[](unsigned char c) {
					return std::tolower(c);
				});
		auto index = packetStructure->indexOfField(fieldName);
		if (index.has_value())
			return readValueFromMemoryAt(index.value());
		else
			return std::nullopt;
	}
Valerio Pastore's avatar
Valerio Pastore committed

Valerio Pastore's avatar
.  
Valerio Pastore committed
	/**
	 * @brief Returns a constant bit iterator pointing to the beginning of the packet data.
	 *
	 * @return A constant bit iterator pointing to the beginning of the packet data.
	 */
	bit_iterator<ValueType> begin() const {
		return bit_iterator<ValueType>(memoryPointer, 0, packetStructure,
				&_readValueFromMemoryAt_);
	}
Valerio Pastore's avatar
Valerio Pastore committed

Valerio Pastore's avatar
.  
Valerio Pastore committed
	/**
	 * @brief Returns a bit_iterator pointing to the end of the container.
	 *
	 * @return A bit_iterator pointing to the end of the container.
	 */
	bit_iterator<ValueType> end() const {
		return bit_iterator<ValueType>(memoryPointer,
				packetStructure->numberOfFields(), packetStructure,
				&_readValueFromMemoryAt_);
	}
Valerio Pastore's avatar
Valerio Pastore committed

Valerio Pastore's avatar
.  
Valerio Pastore committed
	/**
	 * Writes a value into memory at the specified index.
	 *
	 * @param index The index at which to write the value.
	 * @param value The value to be written.
	 * @return An optional containing number of fits written, if any.
	 */
	std::optional<uint> writeValueIntoMemoryAtIndex(uint index,
			ValueType value) {
		auto offset = this->packetStructure->bitOffsetOf(index);
		auto numbits = this->packetStructure->bitSizeOf(index);
		size_t min_req = minBitsRequired(value);
		if (!offset.has_value() || !numbits.has_value())
			return std::nullopt;
		if (numbits < min_req) {
			time_t now = time(nullptr);
			std::cerr << "["
					<< std::put_time(localtime(&now), "%Y-%m-%d %H:%M:%S")
					<< "]\t[Base Packet]\t" << "Error: you are trying to write "
					<< value << " which requires at least " << min_req
					<< " bits in a field of size " << numbits << std::endl;
			return std::nullopt;
		}

		// Calculate the bit offset from the byte offset:
		uint bitoffset = offset.value() % 8;
		// Calculate the byte offset from the bit offset:
		uint byteoffset = offset.value() / 8;
		uint numbits_written = 0;

		for (size_t i = 0; i < numbits; i++) {
			// Calculate the byte and bit index of the current bit:
			uint byte_index = byteoffset + (bitoffset + i) / 8;
			uint bit_index = (bitoffset + i) % 8;
			// Create a bit mask to isolate the desired bit:
			uint8_t bit_mask = 1 << (7 - bit_index);
			// Set the corresponding bit in the binary array if the value is 1:
			if ((value >> (numbits.value() - i - 1)) & 1) {
				memoryPointer[byte_index] |= bit_mask;
				numbits_written++;
			} else {
				memoryPointer[byte_index] &= ~bit_mask;
Valerio Pastore's avatar
Valerio Pastore committed
			}
		}
Valerio Pastore's avatar
.  
Valerio Pastore committed
		return numbits_written;
	}
Valerio Pastore's avatar
Valerio Pastore committed

Valerio Pastore's avatar
.  
Valerio Pastore committed
	/**
	 * @brief Get the byte size of the packet structure.
Valerio Pastore's avatar
Valerio Pastore committed
	 *
Valerio Pastore's avatar
.  
Valerio Pastore committed
	 * @return The byte size of the packet structure.
Valerio Pastore's avatar
Valerio Pastore committed
	 */
Valerio Pastore's avatar
.  
Valerio Pastore committed
	uint getPacketStructureByteSize() const {
		return packetStructure->getByteSize();
	}
	/**
	 * @brief Returns the packet structure of the BasePacket.
	 *
	 * @return A reference to the packet structure.
	 */
	BasePacketStructure& getPacketStructure() const {
		return *(this->packetStructure);
	}
Valerio Pastore's avatar
Valerio Pastore committed

Valerio Pastore's avatar
.  
Valerio Pastore committed
	/**
	 * @brief Checks if the packet has a recognized header.
	 * @note This is a pure virtual function and must be implemented by derived classes.
	 * @return true if the packet has a recognized header, false otherwise.
	 */
	virtual bool hasRecognizedHeader() const = 0;
	/**
	 * @brief Checks if the passed data has a recognized header.
	 * @param buff The data to check.
	 * @note This is a pure virtual function and must be implemented by derived classes.
	 * @return true if buff has a recognized header, false otherwise.
	 */
	virtual bool isRecognizedHeader(std::vector<uint8_t> buff) const = 0;

	/**
	 * @brief Get the size of the header of the packet.
	 * @note This is a pure virtual function and must be implemented by derived classes.
	 * @return The size of the header as an unsigned integer.
	 */
	virtual uint getHeaderSize() const = 0;
	/**
	 * @brief Get the size of the payload of the packet.
	 * @note This is a pure virtual function and must be implemented by derived classes.
	 * @return The size of the payload as an unsigned integer.
	 */
	virtual uint getPayloadSize() const = 0;
	/**
	 * @brief Get the size of the tail of the packet.
	 * @note This is a pure virtual function and must be implemented by derived classes.
	 * @return The size of the tail as an unsigned integer.
	 */
	virtual uint getTailSize() const = 0;
};

using valueType = size_t;
/**
 * @brief Represents a concrete class derived from BasePacketTempl with a specified value type.
 *
 * The `BasePacket` class is a concrete class derived from `BasePacketTempl` with a value type of `size_t`.
 */
class BasePacket: public BasePacketTempl<valueType> {

protected:
public:
	BasePacket(BasePacketStructure &structure) :
			BasePacketTempl<valueType>(structure) {
	}
	virtual ~BasePacket() = default;
};
Valerio Pastore's avatar
Valerio Pastore committed
}