Skip to content
Base_Packet.h 13.1 KiB
Newer Older
Valerio Pastore's avatar
Valerio Pastore committed
/*
 * PacketLib.h
 *
 *  Created on: Nov 24, 2022
 *      Author: valerio
 */
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
namespace inaf::oasbo::PacketLib {

Valerio Pastore's avatar
Valerio Pastore committed
class BasePacketStructure {
protected:
	using Structure = std::vector<std::tuple<uint, std::string, uint>>;

Valerio Pastore's avatar
Valerio Pastore committed
	std::string source;
	Structure structure;
Valerio Pastore's avatar
Valerio Pastore committed
	uint byteSize;
	std::vector<uint> fieldSizes;
	std::unordered_map<uint, uint> indexToOffsetsMap;
	std::unordered_map<std::string, uint> fieldNameToIndexMap;
	std::unordered_map<uint, std::string> indexToFieldNameMap;
	std::function<Structure(std::string)> sourceReadingFunc;
Valerio Pastore's avatar
Valerio Pastore committed

	void updateFieldSizes(
			const Structure &paramsTuple) {
Valerio Pastore's avatar
Valerio Pastore committed
		std::for_each(paramsTuple.begin(), paramsTuple.end(),
Valerio Pastore's avatar
Valerio Pastore committed
				[&](const std::tuple<uint, std::string, uint> &tup) {
Valerio Pastore's avatar
Valerio Pastore committed
					this->fieldSizes.push_back(std::get<2>(tup));
				});
	}
	void updateFieldOffsets(
			const Structure &paramsTuple) {
Valerio Pastore's avatar
Valerio Pastore committed
		uint offset = 0;
Valerio Pastore's avatar
Valerio Pastore committed
		for (size_t i = 0; i < paramsTuple.size(); i++) {
Valerio Pastore's avatar
Valerio Pastore committed
			indexToOffsetsMap[i] = offset;
Valerio Pastore's avatar
Valerio Pastore committed
			offset += std::get<2>(paramsTuple[i]);
		}
	}
	void updateFieldNameAndIndexMap(
			const Structure &paramsTuple) {
Valerio Pastore's avatar
Valerio Pastore committed
		std::for_each(paramsTuple.begin(), paramsTuple.end(),
Valerio Pastore's avatar
Valerio Pastore committed
				[&](const std::tuple<uint, std::string, uint> &tup) {
Valerio Pastore's avatar
Valerio Pastore committed
					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
	void updateStructure(std::string source) {
Valerio Pastore's avatar
Valerio Pastore committed
		this->source = source;
		this->structure = sourceReadingFunc(source);
Valerio Pastore's avatar
Valerio Pastore committed
		updateFieldSizes(structure);
		updateFieldOffsets(structure);
		updateFieldNameAndIndexMap(structure);
Valerio Pastore's avatar
Valerio Pastore committed
		uint bitSize = std::accumulate(fieldSizes.begin(), fieldSizes.end(),0);
Valerio Pastore's avatar
Valerio Pastore committed
		this->byteSize = bitSize%8 == 0 ? bitSize/8 : bitSize/8 +1;
Valerio Pastore's avatar
Valerio Pastore committed
	}

public:
	virtual ~BasePacketStructure() = default;
	BasePacketStructure(std::string source, std::function<Structure(std::string)> sourceReadingFunc) : source(source), sourceReadingFunc(sourceReadingFunc){
		this->updateStructure(source);
	};
Valerio Pastore's avatar
Valerio Pastore committed
	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
	void changeSource(std::string source) {
Valerio Pastore's avatar
Valerio Pastore committed
		updateStructure(source);
	}
Valerio Pastore's avatar
Valerio Pastore committed
	uint getByteSize() {
Valerio Pastore's avatar
Valerio Pastore committed
		return this->byteSize;
	}
Valerio Pastore's avatar
Valerio Pastore committed
	std::string getSource() {
Valerio Pastore's avatar
Valerio Pastore committed
		return this->source;
	}
Valerio Pastore's avatar
Valerio Pastore committed
	std::optional<uint> bitOffsetOf(uint index) {
Valerio Pastore's avatar
Valerio Pastore committed
		if (index <= this->numberOfFields()) {
Valerio Pastore's avatar
Valerio Pastore committed
			return indexToOffsetsMap.at(index);
Valerio Pastore's avatar
Valerio Pastore committed
		} else {
			std::cerr << "No field at " << index << ", max is "
					<< numberOfFields() << ", returning nullopt." << std::endl;
			return std::nullopt;
		}
	}
Valerio Pastore's avatar
Valerio Pastore committed
	std::optional<uint> bitSizeOf(uint index) {
Valerio Pastore's avatar
Valerio Pastore committed
		if (index <= this->numberOfFields()) {
			return this->fieldSizes[index];
		} else {
			std::cerr << "No field at " << index << ", max is "
					<< numberOfFields() << ", returning nullopt." << std::endl;
			return std::nullopt;
		}
	}
Valerio Pastore's avatar
Valerio Pastore committed
	std::optional<uint> indexOfField(std::string fieldName) {
Valerio Pastore's avatar
Valerio Pastore committed
		std::transform(fieldName.begin(), fieldName.end(), fieldName.begin(),
				[](unsigned char c) {
					return std::tolower(c);
				});
Valerio Pastore's avatar
Valerio Pastore committed
		try {
			return this->fieldNameToIndexMap.at(fieldName);
		} catch (const std::out_of_range &oor) {
			std::cerr << "No field of name " << fieldName
					<< ", returning nullopt." << std::endl;
			return std::nullopt;
		}
	}

Valerio Pastore's avatar
Valerio Pastore committed
	std::optional<std::string> fieldNameOfIndex(uint index) {
Valerio Pastore's avatar
Valerio Pastore committed
		try {
			return this->indexToFieldNameMap.at(index);
		} catch (const std::out_of_range &oor) {
			std::cerr << "No field at " << index << ", max is "
					<< numberOfFields() << ", returning nullopt." << std::endl;
			return std::nullopt;
		}
	}
Valerio Pastore's avatar
Valerio Pastore committed
	uint numberOfFields() {
Valerio Pastore's avatar
Valerio Pastore committed
		return this->fieldSizes.size();
	}

Valerio Pastore's avatar
Valerio Pastore committed
	std::unordered_map<std::string, uint> getFieldNameToIndexMap() const {
Valerio Pastore's avatar
Valerio Pastore committed
		return this->fieldNameToIndexMap;
	}
};
Valerio Pastore's avatar
Valerio Pastore committed
template<typename ValueType>
Valerio Pastore's avatar
Valerio Pastore committed
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&;

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

	bit_iterator(const bit_iterator &other) :
Valerio Pastore's avatar
Valerio Pastore committed
			m_data(other.m_data), m_offset(other.m_offset), m_structure(
					other.m_structure), m_func(other.m_func) {
Valerio Pastore's avatar
Valerio Pastore committed
	}

	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 {
Valerio Pastore's avatar
Valerio Pastore committed
		return bit_iterator(m_data, m_offset + n, m_structure, m_func);
Valerio Pastore's avatar
Valerio Pastore committed
	}

	bit_iterator operator-(int n) const {
Valerio Pastore's avatar
Valerio Pastore committed
		return bit_iterator(m_data, m_offset - n, m_structure, m_func);
Valerio Pastore's avatar
Valerio Pastore committed
	}

	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 {
Valerio Pastore's avatar
Valerio Pastore committed
		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());
Valerio Pastore's avatar
Valerio Pastore committed
	}

private:
	const uint8_t *m_data;
	int m_offset;
Valerio Pastore's avatar
Valerio Pastore committed
	BasePacketStructure *m_structure;
Valerio Pastore's avatar
Valerio Pastore committed
	std::function<ValueType(const uint8_t*, uint, uint)> m_func;
Valerio Pastore's avatar
Valerio Pastore committed
};

template<typename ValueType>
class BasePacketTempl {
Valerio Pastore's avatar
Valerio Pastore committed
protected:
Valerio Pastore's avatar
Valerio Pastore committed
	BasePacketStructure *structure;
Valerio Pastore's avatar
Valerio Pastore committed
	uint8_t *binaryPointer;
Valerio Pastore's avatar
Valerio Pastore committed
	// This C++ function reads a binary value from a memory location pointed to by binaryPointer.
	// The binary value is represented by num_bits number of bits starting from the offset-th bit in the memory.
	// The function returns the value of the binary as a ValueType.
Valerio Pastore's avatar
Valerio Pastore committed
	static ValueType _readValueFromBinaryAt_(const uint8_t *binaryPointer,
Valerio Pastore's avatar
Valerio Pastore committed
			uint offset, uint num_bits) {
Valerio Pastore's avatar
Valerio Pastore committed
		// Calculate the bit offset from the byte offset:
Valerio Pastore's avatar
Valerio Pastore committed
		auto bit_offset = offset % 8;
Valerio Pastore's avatar
Valerio Pastore committed
		// Calculate the byte offset from the bit offset:
Valerio Pastore's avatar
Valerio Pastore committed
		auto byte_offset = offset / 8;
Valerio Pastore's avatar
Valerio Pastore committed
		ValueType value = 0;

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

		// Calculate the number of bits needed
Valerio Pastore's avatar
Valerio Pastore committed
		auto bitsNeeded = 0;
Valerio Pastore's avatar
Valerio Pastore committed
		bool isNegative = value < 0;
		uint32_t absValue = isNegative ? -value : value;

		while (absValue != 0) {
			bitsNeeded++;
			absValue >>= 1;
		}

		if (isNegative) {
			bitsNeeded++;
		}

		return bitsNeeded;
	}
Valerio Pastore's avatar
Valerio Pastore committed
public:

Valerio Pastore's avatar
Valerio Pastore committed
	BasePacketTempl(BasePacketStructure &structure) {
		this->structure = new BasePacketStructure(structure);
Valerio Pastore's avatar
Valerio Pastore committed
		this->binaryPointer = new uint8_t[structure.getByteSize()];
Valerio Pastore's avatar
Valerio Pastore committed
    BasePacketTempl(const BasePacketTempl& other) {
        this->structure = new BasePacketStructure(other.getPacketStructure());
        this->binaryPointer = new uint8_t[other.structure->getByteSize()];
        std::memcpy(this->binaryPointer, other.binaryPointer, other.structure->getByteSize());
    }


Valerio Pastore's avatar
Valerio Pastore committed
	virtual ~BasePacketTempl() = default;

Valerio Pastore's avatar
Valerio Pastore committed
	void updateStructure(BasePacketStructure &structure) {
Valerio Pastore's avatar
Valerio Pastore committed
		size_t newSize = structure.getByteSize();
		size_t oldSize = this->structure->getByteSize();
Valerio Pastore's avatar
Valerio Pastore committed
		uint8_t* buff = new uint8_t[newSize];
Valerio Pastore's avatar
Valerio Pastore committed
		std::memset(buff, 0, newSize);
		std::memcpy(buff, binaryPointer, std::min(newSize,oldSize));
Valerio Pastore's avatar
Valerio Pastore committed
		delete this->binaryPointer;
Valerio Pastore's avatar
Valerio Pastore committed
		this->binaryPointer = new uint8_t[newSize];
		std::memcpy(binaryPointer, buff, newSize);
Valerio Pastore's avatar
Valerio Pastore committed
		delete[] buff;
Valerio Pastore's avatar
Valerio Pastore committed
		this->structure = &structure;
Valerio Pastore's avatar
Valerio Pastore committed
	}
Valerio Pastore's avatar
Valerio Pastore committed

Valerio Pastore's avatar
Valerio Pastore committed
	std::optional<ValueType> readValueFromBinaryAt(uint index) const {
Valerio Pastore's avatar
Valerio Pastore committed
		auto offset = structure->bitOffsetOf(index); // offset from the beginning of the byte
		auto num_bits = structure->bitSizeOf(index); //remaining bits to read
		if (offset.has_value() && num_bits.has_value())
			return _readValueFromBinaryAt_(binaryPointer, offset.value(),
					num_bits.value());
		else {
			std::cerr << "Error: No field at " << index << ", max is "
					<< structure->numberOfFields() << ", returning nullopt"
					<< std::endl;
			return std::nullopt;
		}
Valerio Pastore's avatar
Valerio Pastore committed
	uint8_t const* getBinaryPointer() const {
		return binaryPointer;
	}
Valerio Pastore's avatar
Valerio Pastore committed

Valerio Pastore's avatar
Valerio Pastore committed
	int copyToBinaryPointer(const uint8_t *from, uint size) {
Valerio Pastore's avatar
Valerio Pastore committed
		uint max_writable = this->structure->getByteSize();
Valerio Pastore's avatar
Valerio Pastore committed
		if (size > max_writable) {
Valerio Pastore's avatar
Valerio Pastore committed
			std::cerr << "Error: you are trying to copy " << size
					<< " byte where the max size is: " << max_writable
Valerio Pastore's avatar
Valerio Pastore committed
					<< std::endl;
			std::cerr << "\tI copy only until " << max_writable << " byte"
					<< std::endl;
Valerio Pastore's avatar
Valerio Pastore committed
			std::memcpy(binaryPointer, from, max_writable);
			return max_writable;
Valerio Pastore's avatar
Valerio Pastore committed
		} else {
Valerio Pastore's avatar
Valerio Pastore committed
			std::memcpy(binaryPointer, from, size);
Valerio Pastore's avatar
Valerio Pastore committed
			return size;
		}
Valerio Pastore's avatar
Valerio Pastore committed

	int copyToBinaryPointer(const uint8_t *from, uint size, uint offset) {
Valerio Pastore's avatar
Valerio Pastore committed
		uint max_writable = this->structure->getByteSize();
Valerio Pastore's avatar
Valerio Pastore committed
		if (offset > max_writable) {
			std::cerr << "Error: you are trying to copy starting from "
					<< offset << " byte where the max size is: " << max_writable
Valerio Pastore's avatar
Valerio Pastore committed
					<< std::endl;
Valerio Pastore's avatar
Valerio Pastore committed
			return -1;
		}
Valerio Pastore's avatar
Valerio Pastore committed
		if (size + offset > max_writable) {
Valerio Pastore's avatar
Valerio Pastore committed
			std::cerr << "Error: you are trying to copy " << size + offset
					<< " byte where the max size is: " << max_writable
Valerio Pastore's avatar
Valerio Pastore committed
					<< std::endl;
			std::cerr << "\tI copy only until " << max_writable << " byte"
					<< std::endl;
			int to_write = max_writable - offset;
Valerio Pastore's avatar
Valerio Pastore committed
			std::memcpy(&binaryPointer[offset], from, to_write);
			return to_write;
Valerio Pastore's avatar
Valerio Pastore committed
		} else {
Valerio Pastore's avatar
Valerio Pastore committed
			std::memcpy(&binaryPointer[offset], from, size);
Valerio Pastore's avatar
Valerio Pastore committed
			return size;
		}
Valerio Pastore's avatar
Valerio Pastore committed
	std::optional<ValueType> operator[](uint index) const {
Valerio Pastore's avatar
Valerio Pastore committed
		return readValueFromBinaryAt(index);
Valerio Pastore's avatar
Valerio Pastore committed
	std::optional<ValueType> operator[](std::string fieldName) const {
Valerio Pastore's avatar
Valerio Pastore committed
		std::transform(fieldName.begin(), fieldName.end(), fieldName.begin(),
				[](unsigned char c) {
					return std::tolower(c);
				});
Valerio Pastore's avatar
Valerio Pastore committed
		auto index = structure->indexOfField(fieldName);
		if (index.has_value())
			return readValueFromBinaryAt(index.value());
Valerio Pastore's avatar
Valerio Pastore committed
		else
Valerio Pastore's avatar
Valerio Pastore committed
			return std::nullopt;
Valerio Pastore's avatar
Valerio Pastore committed
	bit_iterator<ValueType> begin() const {
Valerio Pastore's avatar
Valerio Pastore committed
		return bit_iterator<ValueType>(binaryPointer, 0, structure,
Valerio Pastore's avatar
Valerio Pastore committed
				&_readValueFromBinaryAt_);
	}

	bit_iterator<ValueType> end() const {
Valerio Pastore's avatar
Valerio Pastore committed
		return bit_iterator<ValueType>(binaryPointer,
				structure->numberOfFields(), structure,
				&_readValueFromBinaryAt_);
Valerio Pastore's avatar
Valerio Pastore committed
	std::optional<int> writeValueToBinaryAtIndex(uint index, ValueType value) {
Valerio Pastore's avatar
Valerio Pastore committed
		auto offset = this->structure->bitOffsetOf(index);
		auto numbits = this->structure->bitSizeOf(index);
		size_t min_req = minBitsRequired(value);
		if (!offset.has_value() || !numbits.has_value())
			return std::nullopt;
		if (numbits < min_req) {
			std::cerr << "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:
		int bitoffset = offset.value() % 8;
		// Calculate the byte offset from the bit offset:
		int byteoffset = offset.value() / 8;
		int numbits_written = 0;
Valerio Pastore's avatar
Valerio Pastore committed
		for (size_t i = 0; i < numbits; i++) {
			// Calculate the byte and bit index of the current bit:
			int byte_index = byteoffset + (bitoffset + i) / 8;
			int 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) {
				binaryPointer[byte_index] |= bit_mask;
				numbits_written++;
			} else {
				binaryPointer[byte_index] &= ~bit_mask;
Valerio Pastore's avatar
Valerio Pastore committed
		return numbits_written;
Valerio Pastore's avatar
Valerio Pastore committed

	uint getPacketStructureByteSize() const {
		return structure->getByteSize();
	}
	BasePacketStructure& getPacketStructure() const {
		return *(this->structure);
	}
Valerio Pastore's avatar
Valerio Pastore committed

	virtual bool isRecognizedHeader() const = 0;
	virtual bool isRecognizedHeader(std::vector<uint8_t> buff) const = 0;
Valerio Pastore's avatar
Valerio Pastore committed
	virtual uint getHeaderSize() const = 0;
	virtual uint getPayloadSize() const = 0;
	virtual uint getTailSize() const = 0;
Valerio Pastore's avatar
Valerio Pastore committed

using valueType = size_t;
Valerio Pastore's avatar
Valerio Pastore committed
class BasePacket: public BasePacketTempl<valueType> {
Valerio Pastore's avatar
Valerio Pastore committed

protected:

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

};
Valerio Pastore's avatar
Valerio Pastore committed