/* * PacketLib.h * * Created on: Nov 24, 2022 * Author: valerio */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include namespace inaf::oasbo::PacketLib { class BasePacketStructure { protected: using Structure = std::vector>; std::string source; Structure structure; uint byteSize; std::vector fieldSizes; std::unordered_map indexToOffsetsMap; std::unordered_map fieldNameToIndexMap; std::unordered_map indexToFieldNameMap; std::function sourceReadingFunc; void updateFieldSizes(const Structure ¶msTuple) { std::for_each(paramsTuple.begin(), paramsTuple.end(), [&](const std::tuple &tup) { this->fieldSizes.push_back(std::get<2>(tup)); }); } void updateFieldOffsets(const Structure ¶msTuple) { uint offset = 0; for (size_t i = 0; i < paramsTuple.size(); i++) { indexToOffsetsMap[i] = offset; offset += std::get<2>(paramsTuple[i]); } } void updateFieldNameAndIndexMap(const Structure ¶msTuple) { std::for_each(paramsTuple.begin(), paramsTuple.end(), [&](const std::tuple &tup) { this->fieldNameToIndexMap[std::get<1>(tup)] = std::get<0>( tup); this->indexToFieldNameMap[std::get<0>(tup)] = std::get<1>( tup); }); } 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: virtual ~BasePacketStructure() = default; BasePacketStructure(std::string source, std::function sourceReadingFunc) : source(source), sourceReadingFunc(sourceReadingFunc) { this->updateStructure(source); } ; 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; } void changeSource(std::string source) { updateStructure(source); } uint getByteSize() { return this->byteSize; } std::string getSource() { return this->source; } std::optional 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; } } std::optional 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; } } std::optional 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; } } std::optional 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; } } uint numberOfFields() { return this->fieldSizes.size(); } std::unordered_map getFieldNameToIndexMap() const { return this->fieldNameToIndexMap; } }; template 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&; public: bit_iterator(const uint8_t *data, int offset, BasePacketStructure *structure, std::function 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()); } private: const uint8_t *m_data; int m_offset; BasePacketStructure *m_structure; std::function m_func; }; template class BasePacketTempl { protected: BasePacketStructure *structure; uint8_t *binaryPointer; // 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. static ValueType _readValueFromBinaryAt_(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; } size_t minBitsRequired(size_t value) const { // Handle special cases size_t bitsNeeded = 1; while (value != 0) { bitsNeeded++; value >>= 1; } return bitsNeeded; } public: BasePacketTempl(BasePacketStructure &structure) { this->structure = new BasePacketStructure(structure); this->binaryPointer = new uint8_t[structure.getByteSize()]; } 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()); } virtual ~BasePacketTempl() = default; void updateStructure(BasePacketStructure &structure) { size_t newSize = structure.getByteSize(); size_t oldSize = this->structure->getByteSize(); uint8_t *buff = new uint8_t[newSize]; std::memset(buff, 0, newSize); std::memcpy(buff, binaryPointer, std::min(newSize, oldSize)); delete this->binaryPointer; this->binaryPointer = new uint8_t[newSize]; std::memcpy(binaryPointer, buff, newSize); delete[] buff; this->structure = &structure; } std::optional readValueFromBinaryAt(uint index) const { 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 { 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 " << structure->numberOfFields() << ", returning nullopt" << std::endl; return std::nullopt; } } uint8_t const* getBinaryPointer() const { return binaryPointer; } int copyToBinaryPointer(const uint8_t *from, uint size) { uint max_writable = this->structure->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(binaryPointer, from, max_writable); return max_writable; } else { std::memcpy(binaryPointer, from, size); return size; } } int copyToBinaryPointer(const uint8_t *from, uint size, uint offset) { uint max_writable = this->structure->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(&binaryPointer[offset], from, to_write); return to_write; } else { std::memcpy(&binaryPointer[offset], from, size); return size; } } std::optional operator[](uint index) const { return readValueFromBinaryAt(index); } std::optional operator[](std::string fieldName) const { std::transform(fieldName.begin(), fieldName.end(), fieldName.begin(), [](unsigned char c) { return std::tolower(c); }); auto index = structure->indexOfField(fieldName); if (index.has_value()) return readValueFromBinaryAt(index.value()); else return std::nullopt; } bit_iterator begin() const { return bit_iterator(binaryPointer, 0, structure, &_readValueFromBinaryAt_); } bit_iterator end() const { return bit_iterator(binaryPointer, structure->numberOfFields(), structure, &_readValueFromBinaryAt_); } std::optional writeValueToBinaryAtIndex(uint index, ValueType value) { 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) { 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) { binaryPointer[byte_index] |= bit_mask; numbits_written++; } else { binaryPointer[byte_index] &= ~bit_mask; } } return numbits_written; } uint getPacketStructureByteSize() const { return structure->getByteSize(); } BasePacketStructure& getPacketStructure() const { return *(this->structure); } virtual bool isRecognizedHeader() const = 0; virtual bool isRecognizedHeader(std::vector buff) const = 0; virtual uint getHeaderSize() const = 0; virtual uint getPayloadSize() const = 0; virtual uint getTailSize() const = 0; }; using valueType = size_t; class BasePacket: public BasePacketTempl { protected: public: BasePacket(BasePacketStructure &structure) : BasePacketTempl(structure) { } virtual ~BasePacket() = default; }; }