R04H31 Merge to Master (#89)
* simplify and refactor peer-link packet handling; * bump version number to match dev branch; alter logging for peer-link data; * warn on packets generated over 4K; * don't clear map directly, use clear() helper; * use parens vs braces for initializers; correct case fall-thru on SysView; * for clarity relabel RPT_CLOSING and MST_CLOSING to RPT_DISC and MST_DISC; * discontinue support for TRANSFER command on the traffic port entirely; hide useAlternatePortForDiagnostics and allowActivityTransfer options in FNE configuration; add stern warnings and alerts if the FNE disabled either useAlternatePortForDiagnostics or allowActivityTransfer (these are really critical operations); alert in peer log if the master does not report support for the alternate port for diagnostics; * make oversized packet warning clearer (this warning is not a end-user warning, the user can't do antyhing about this this is a developer BUGBUG warning); * apply recv timeout on InfluxDB operations (this hopefully will prevent stuck queries if the InfluxDB server dies while query is in progress); * prevent InfluxDB query from becoming stuck in a tight infinite loop within select(); * don't bother handling responses from the influx query, remove fluxQL (we don't make queries with this library); * cleanup unused vars; * generate an error log if a fluxql worker fails to write data to influx; * implement support to save/retain configuration pushed from peer link connections; * fix memory leak caused by not deleting unused packetbuffer; * refactor ACL updates; * refactor log messages for channels to be <channel ID>-<channel No> in most cases where applicable; * add more <channel ID>-<channel No> changes; * whoops wrong variable type; * correct fatal bug where update lookup timer was never started; * check the RF state at the bottom of a talkgroup hang expiration timer for RF talkgroup activity, if the state is not listening, handle the end of call as if it was a frame loss; * fix potential memory leak in PacketBuffer fragmenter, the encoder was not deleting the storage buffer before end of scope; * ensure decompressed buffer is deleted; * ensure decompressed buffer is deleted in error conditions too; * add support to disable adjacent site broadcast; * lay the groundwork for explicit channel configuration; * [EXPERIMENTAL] implement experimental support for a CC to define explicit channel identity information for voice channels (NOTE: this does *NOT* implement support for hotspots to operate in explicit channel mode!); * don't use the concurrent::unordered_map for this (this gives me great pause for a problem...); * add some extra debug trace; * add some extra debug trace; * display MI data during calibration; update modem submodule; * this is ugly, and I hate it, but to fix very strange WIN32 deadlock situations, we'll selectively use our concurrent unordered_map on non-WIN32, and on WIN32 use the std unordered_map + a local mutex; * update modem submodule; change P25 Voice MI logging messages from DEBUG to MESSAGE; fix issue in HostSetup/HostCal where PDU header decoding was broken; * only dump MI data if the call is encrypted (don't bother displaying zeros for a clear call); * whoops check right variable; * cleanup and clarify MI logging; * cleanup and clarify MI logging (again LDU2 was out of log sequence); * hate me later, dont use a colon as the string separator for the MI bytes, use equals; * log DMR PI MI dta; * refactor debug messaging for HDU; * reorganize core meta-programming macros, move class property macros into ClassProperties.h, move bit manipulation into BitManipulation.h, move VLA into VariableLengthArray.h; rename meta-programming macros for clarity most of these macros declare some sort of variable and/or functions, as such I'm dropping the notation for __BLAHBLAH and using DECLARE_BLAHBLAH instead; better document class property meta-programming macros; refactor __GET/__SET_UINT16 into __GET/__SET_UINT24 these macros set 24-bit values from and to buffers, this will reduce programming confusion; * add property documentation for some KMM classes; * correct file copyright headers; * change commenting to reflect section more clearly; * use proper GPL license for file; * detect arch and properly update internal variable; * handle instance where the system does not return a valid processor type for arch detection; * display target arch along side host arch if we're cross compiling; * strip newlines; * fix missing parens around response encoding;pull/91/head 2025-05-25
parent
5e31e9a642
commit
738ee918d1
@ -0,0 +1,118 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Digital Voice Modem - Common Library
|
||||
* GPLv2 Open Source. Use is subject to license terms.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* Copyright (C) 2025 Bryan Biedenkapp, N2PLL
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* @file BitManipulation.h
|
||||
* @ingroup common
|
||||
*/
|
||||
#pragma once
|
||||
#if !defined(__BIT_MANIPULATION_H__)
|
||||
#define __BIT_MANIPULATION_H__
|
||||
|
||||
#if !defined(__COMMON_DEFINES_H__)
|
||||
#warning "BitManipulation.h included before Defines.h, please check include order"
|
||||
#include "common/Defines.h"
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Constants
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief Bit mask table used for WRITE_BIT and READ_BIT.
|
||||
* @ingroup utils
|
||||
*/
|
||||
const uint8_t BIT_MASK_TABLE[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U };
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Macros
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief Macro helper to write a specific bit in a byte array.
|
||||
* @ingroup common
|
||||
* @param p Byte array.
|
||||
* @param i Bit offset.
|
||||
* @param b Bit to write.
|
||||
*/
|
||||
#define WRITE_BIT(p, i, b) p[(i) >> 3] = (b) ? (p[(i) >> 3] | BIT_MASK_TABLE[(i) & 7]) : (p[(i) >> 3] & ~BIT_MASK_TABLE[(i) & 7])
|
||||
/**
|
||||
* @brief Macro helper to read a specific bit from a byte array.
|
||||
* @ingroup common
|
||||
* @param p Byte array.
|
||||
* @param i Bit offset.
|
||||
* @returns bool Bit.
|
||||
*/
|
||||
#define READ_BIT(p, i) (p[(i) >> 3] & BIT_MASK_TABLE[(i) & 7])
|
||||
|
||||
/**
|
||||
* @brief Sets a uint32_t into 4 bytes of a buffer/array. (32-bit value).
|
||||
* @ingroup common
|
||||
* @param val uint32_t value to set
|
||||
* @param buffer uint8_t buffer to set value on
|
||||
* @param offset Offset within uint8_t buffer
|
||||
*/
|
||||
#define SET_UINT32(val, buffer, offset) \
|
||||
buffer[0U + offset] = (val >> 24) & 0xFFU; \
|
||||
buffer[1U + offset] = (val >> 16) & 0xFFU; \
|
||||
buffer[2U + offset] = (val >> 8) & 0xFFU; \
|
||||
buffer[3U + offset] = (val >> 0) & 0xFFU;
|
||||
/**
|
||||
* @brief Gets a uint32_t consisting of 4 bytes from a buffer/array. (32-bit value).
|
||||
* @ingroup common
|
||||
* @param buffer uint8_t buffer to get value from
|
||||
* @param offset Offset within uint8_t buffer
|
||||
*/
|
||||
#define GET_UINT32(buffer, offset) \
|
||||
(buffer[offset + 0U] << 24) | \
|
||||
(buffer[offset + 1U] << 16) | \
|
||||
(buffer[offset + 2U] << 8) | \
|
||||
(buffer[offset + 3U] << 0);
|
||||
/**
|
||||
* @brief Sets a uint32_t into 3 bytes of a buffer/array. (24-bit value).
|
||||
* @ingroup common
|
||||
* @param val uint32_t value to set
|
||||
* @param buffer uint8_t buffer to set value on
|
||||
* @param offset Offset within uint8_t buffer
|
||||
*/
|
||||
#define SET_UINT24(val, buffer, offset) \
|
||||
buffer[0U + offset] = (val >> 16) & 0xFFU; \
|
||||
buffer[1U + offset] = (val >> 8) & 0xFFU; \
|
||||
buffer[2U + offset] = (val >> 0) & 0xFFU;
|
||||
/**
|
||||
* @brief Gets a uint32_t consisting of 3 bytes from a buffer/array. (24-bit value).
|
||||
* @ingroup common
|
||||
* @param buffer uint8_t buffer to get value from
|
||||
* @param offset Offset within uint8_t buffer
|
||||
*/
|
||||
#define GET_UINT24(buffer, offset) \
|
||||
(buffer[offset + 0U] << 16) | \
|
||||
(buffer[offset + 1U] << 8) | \
|
||||
(buffer[offset + 2U] << 0);
|
||||
/**
|
||||
* @brief Sets a uint16_t into 2 bytes of a buffer/array. (16-bit value).
|
||||
* @ingroup common
|
||||
* @param val uint16_t value to set
|
||||
* @param buffer uint8_t buffer to set value on
|
||||
* @param offset Offset within uint8_t buffer
|
||||
*/
|
||||
#define SET_UINT16(val, buffer, offset) \
|
||||
buffer[0U + offset] = (val >> 8) & 0xFFU; \
|
||||
buffer[1U + offset] = (val >> 0) & 0xFFU;
|
||||
/**
|
||||
* @brief Gets a uint16_t consisting of 2 bytes from a buffer/array. (16-bit value).
|
||||
* @ingroup common
|
||||
* @param buffer uint8_t buffer to get value from
|
||||
* @param offset Offset within uint8_t buffer
|
||||
*/
|
||||
#define GET_UINT16(buffer, offset) \
|
||||
((buffer[offset + 0U] << 8) & 0xFF00U) | \
|
||||
((buffer[offset + 1U] << 0) & 0x00FFU);
|
||||
|
||||
#endif // __BIT_MANIPULATION_H__
|
||||
@ -0,0 +1,188 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Digital Voice Modem - Common Library
|
||||
* GPLv2 Open Source. Use is subject to license terms.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* Copyright (C) 2025 Bryan Biedenkapp, N2PLL
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* @file ClassProperties.h
|
||||
* @ingroup common
|
||||
*/
|
||||
#pragma once
|
||||
#if !defined(__CLASS_PROPERTIES_H__)
|
||||
#define __CLASS_PROPERTIES_H__
|
||||
|
||||
#if !defined(__COMMON_DEFINES_H__)
|
||||
#warning "ClassProperties.h included before Defines.h, please check include order"
|
||||
#include "common/Defines.h"
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Macros
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @addtogroup common
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class Copy Code Pattern
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Declare a private copy implementation.
|
||||
* This requires the copy(const type& data) to be declared in the class definition.
|
||||
* @ingroup common
|
||||
* @param type Atomic type.
|
||||
*/
|
||||
#define DECLARE_COPY(type) \
|
||||
private: virtual void copy(const type& data); \
|
||||
public: __forceinline type& operator=(const type& data) { \
|
||||
if (this != &data) { \
|
||||
copy(data); \
|
||||
} \
|
||||
return *this; \
|
||||
}
|
||||
/**
|
||||
* @brief Declare a protected copy implementation.
|
||||
* This requires the copy(const type& data) to be declared in the class definition.
|
||||
* @ingroup common
|
||||
* @param type Atomic type.
|
||||
*/
|
||||
#define DECLARE_PROTECTED_COPY(type) \
|
||||
protected: virtual void copy(const type& data); \
|
||||
public: __forceinline type& operator=(const type& data) { \
|
||||
if (this != &data) { \
|
||||
copy(data); \
|
||||
} \
|
||||
return *this; \
|
||||
}
|
||||
|
||||
/**
|
||||
* Property Declaration
|
||||
* These macros should always be used LAST in a "public" section of a class definition.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Declare a read-only get property.
|
||||
* This creates a "property" that is read-only and can only be set internally to the class. Properties created
|
||||
* with this macro generate an internal variable with the name "m_<variableName>", and a getter method
|
||||
* with the name "get<propertyName>()". The getter method is inline and returns the value of the internal
|
||||
* variable. The internal variable is private, and the getter method is public.
|
||||
* @ingroup common
|
||||
* @param type Atomic type for property.
|
||||
* @param variableName Variable name for property.
|
||||
* @param propName Property name.
|
||||
*/
|
||||
#define DECLARE_RO_PROPERTY(type, variableName, propName) \
|
||||
private: type m_##variableName; \
|
||||
public: __forceinline type get##propName(void) const { return m_##variableName; }
|
||||
/**
|
||||
* @brief Declare a read-only get property.
|
||||
* This creates a "property" that is read-only and can only be set internally to the class. Properties created
|
||||
* with this macro generate an internal variable with the name "m_<variableName>", and a getter method
|
||||
* with the name "get<propertyName>()". The getter method is inline and returns the value of the internal
|
||||
* variable. The internal variable is protected, and the getter method is public.
|
||||
* @ingroup common
|
||||
* @param type Atomic type for property.
|
||||
* @param variableName Variable name for property.
|
||||
* @param propName Property name.
|
||||
*/
|
||||
#define DECLARE_PROTECTED_RO_PROPERTY(type, variableName, propName) \
|
||||
protected: type m_##variableName; \
|
||||
public: __forceinline type get##propName(void) const { return m_##variableName; }
|
||||
|
||||
/**
|
||||
* @brief Declare a read-only property, does not use "get" prefix for getter.
|
||||
* This creates a "property" that is read-only and can only be set internally to the class. Properties created
|
||||
* with this macro generate an internal variable with the name "m_<variableName>", and a getter method
|
||||
* with the name "<variableName>()". The getter method is inline and returns the value of the internal
|
||||
* variable. The internal variable is private, and the getter method is public.
|
||||
* @ingroup common
|
||||
* @param type Atomic type for property.
|
||||
* @param variableName Variable name for property.
|
||||
*/
|
||||
#define DECLARE_PROTECTED_RO_PROPERTY_PLAIN(type, variableName) \
|
||||
protected: type m_##variableName; \
|
||||
public: __forceinline type variableName(void) const { return m_##variableName; }
|
||||
/**
|
||||
* @brief Declare a read-only get property, does not use "get" prefix for getter.
|
||||
* This creates a "property" that is read-only and can only be set internally to the class. Properties created
|
||||
* with this macro generate an internal variable with the name "m_<variableName>", and a getter method
|
||||
* with the name "<variableName>()". The getter method is inline and returns the value of the internal
|
||||
* variable. The internal variable is private, and the getter method is public.
|
||||
* @ingroup common
|
||||
* @param type Atomic type for property.
|
||||
* @param variableName Variable name for property.
|
||||
*/
|
||||
#define DECLARE_RO_PROPERTY_PLAIN(type, variableName) \
|
||||
private: type m_##variableName; \
|
||||
public: __forceinline type variableName(void) const { return m_##variableName; }
|
||||
|
||||
/**
|
||||
* @brief Declare a get and set private property.
|
||||
* This creates a "property" that is read/write. Properties created with this macro generate an internal variable
|
||||
* with the name "m_<variableName>", a getter method with the name "get<propertyName>()", and a setter method
|
||||
* with the name "set<propertyName>(value)". The getter method is inline and returns the value of the internal variable.
|
||||
* The internal variable is private, and the getter and setter methods are public.
|
||||
* @ingroup common
|
||||
* @param type Atomic type for property.
|
||||
* @param variableName Variable name for property.
|
||||
* @param propName Property name.
|
||||
*/
|
||||
#define DECLARE_PROPERTY(type, variableName, propName) \
|
||||
private: type m_##variableName; \
|
||||
public: __forceinline type get##propName(void) const { return m_##variableName; } \
|
||||
__forceinline void set##propName(type val) { m_##variableName = val; }
|
||||
/**
|
||||
* @brief Declare a get and set protected property.
|
||||
* This creates a "property" that is read/write. Properties created with this macro generate an internal variable
|
||||
* with the name "m_<variableName>", a getter method with the name "get<propertyName>()", and a setter method
|
||||
* with the name "set<propertyName>(value)". The getter method is inline and returns the value of the internal variable.
|
||||
* The internal variable is protected, and the getter and setter methods are public.
|
||||
* @ingroup common
|
||||
* @param type Atomic type for property.
|
||||
* @param variableName Variable name for property.
|
||||
* @param propName Property name.
|
||||
*/
|
||||
#define DECLARE_PROTECTED_PROPERTY(type, variableName, propName) \
|
||||
protected: type m_##variableName; \
|
||||
public: __forceinline type get##propName(void) const { return m_##variableName; } \
|
||||
__forceinline void set##propName(type val) { m_##variableName = val; }
|
||||
|
||||
/**
|
||||
* @brief Declare a private property, does not use "get"/"set" prefix.
|
||||
* This creates a "property" that is read/write. Properties created with this macro generate an internal variable
|
||||
* with the name "m_<variableName>", and a getter method with the name "<variableName>()", and a setter method
|
||||
* "<propertyName>(value)". The getter method is inline and returns the value of the internal variable. The internal
|
||||
* variable is private, and the getter and setter methods are public.
|
||||
* @ingroup common
|
||||
* @param type Atomic type for property.
|
||||
* @param variableName Variable name for property.
|
||||
*/
|
||||
#define DECLARE_PROPERTY_PLAIN(type, variableName) \
|
||||
private: type m_##variableName; \
|
||||
public: __forceinline type variableName(void) const { return m_##variableName; } \
|
||||
__forceinline void variableName(type val) { m_##variableName = val; }
|
||||
/**
|
||||
* @brief Declare a protected property, does not use "get"/"set" prefix.
|
||||
* This creates a "property" that is read/write. Properties created with this macro generate an internal variable
|
||||
* with the name "m_<variableName>", and a getter method with the name "<variableName>()", and a setter method
|
||||
* "<propertyName>(value)". The getter method is inline and returns the value of the internal variable. The internal
|
||||
* variable is protected, and the getter and setter methods are public.
|
||||
* @ingroup common
|
||||
* @param type Atomic type for property.
|
||||
* @param variableName Variable name for property.
|
||||
*/
|
||||
#define DECLARE_PROTECTED_PROPERTY_PLAIN(type, variableName) \
|
||||
protected: type m_##variableName; \
|
||||
public: __forceinline type variableName(void) const { return m_##variableName; } \
|
||||
__forceinline void variableName(type val) { m_##variableName = val; }
|
||||
|
||||
/** @} */
|
||||
|
||||
#endif // __CLASS_PROPERTIES_H__
|
||||
@ -0,0 +1,82 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Digital Voice Modem - Common Library
|
||||
* GPLv2 Open Source. Use is subject to license terms.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* Copyright (C) 2025 Bryan Biedenkapp, N2PLL
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* @file VariableLengthArray.h
|
||||
* @ingroup common
|
||||
*/
|
||||
#pragma once
|
||||
#if !defined(__VARIABLE_LENGTH_ARRAY_H__)
|
||||
#define __VARIABLE_LENGTH_ARRAY_H__
|
||||
|
||||
#if !defined(__COMMON_DEFINES_H__)
|
||||
#warning "VariableLengthArray.h included before Defines.h, please check include order"
|
||||
#include "common/Defines.h"
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Types
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @addtogroup common
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Unique uint8_t array.
|
||||
* @ingroup common
|
||||
*/
|
||||
typedef std::unique_ptr<uint8_t[]> UInt8Array;
|
||||
|
||||
/**
|
||||
* @brief Unique char array.
|
||||
* @ingroup common
|
||||
*/
|
||||
typedef std::unique_ptr<char[]> CharArray;
|
||||
|
||||
/** @} */
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Macros
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @addtogroup common
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Declares a unique uint8_t array/buffer.
|
||||
* This macro creates a unique pointer to a uint8_t array of the specified length and initializes it to zero.
|
||||
* The resulting pointer is named after the parameter name passed to the macro.
|
||||
* @param name Name of array/buffer.
|
||||
* @param len Length of array/buffer.
|
||||
*/
|
||||
#define DECLARE_UINT8_ARRAY(name, len) \
|
||||
UInt8Array __##name##__UInt8Array = std::make_unique<uint8_t[]>(len); \
|
||||
uint8_t* name = __##name##__UInt8Array.get(); \
|
||||
::memset(name, 0x00U, len);
|
||||
|
||||
/**
|
||||
* @brief Declares a unique char array/buffer.
|
||||
* This macro creates a unique pointer to a uint8_t array of the specified length and initializes it to zero.
|
||||
* The resulting pointer is named after the parameter name passed to the macro.
|
||||
* @ingroup common
|
||||
* @param name Name of array/buffer.
|
||||
* @param len Length of array/buffer.
|
||||
*/
|
||||
#define DECLARE_CHAR_ARRAY(name, len) \
|
||||
CharArray __##name##__CharArray = std::make_unique<char[]>(len); \
|
||||
char* name = __##name##__CharArray.get(); \
|
||||
::memset(name, 0, len);
|
||||
|
||||
/** @} */
|
||||
|
||||
#endif // __VARIABLE_LENGTH_ARRAY_H__
|
||||
@ -0,0 +1,229 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Digital Voice Modem - Common Library
|
||||
* GPLv2 Open Source. Use is subject to license terms.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* Copyright (C) 2025 Bryan Biedenkapp, N2PLL
|
||||
*
|
||||
*/
|
||||
#include "Defines.h"
|
||||
#include "network/PacketBuffer.h"
|
||||
#include "Log.h"
|
||||
#include "Utils.h"
|
||||
|
||||
using namespace network;
|
||||
using namespace compress;
|
||||
|
||||
#include <cassert>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Public Class Members
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/* Initializes a new instance of the PacketBuffer class. */
|
||||
|
||||
PacketBuffer::PacketBuffer(bool compression, const char* name) :
|
||||
fragments(),
|
||||
m_compression(compression),
|
||||
m_name(name)
|
||||
{
|
||||
/* stub */
|
||||
}
|
||||
|
||||
/* Finalizes a instance of the PacketBuffer class. */
|
||||
|
||||
PacketBuffer::~PacketBuffer()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
/* Decode a network packet fragment. */
|
||||
|
||||
bool PacketBuffer::decode(const uint8_t* data, uint8_t** message, uint32_t* outLength)
|
||||
{
|
||||
assert(data != nullptr);
|
||||
|
||||
*message = nullptr;
|
||||
if (outLength != nullptr) {
|
||||
*outLength = 0;
|
||||
}
|
||||
|
||||
uint8_t curBlock = data[8U];
|
||||
uint8_t blockCnt = data[9U];
|
||||
|
||||
Fragment* frag = new Fragment();
|
||||
|
||||
// if this is the first block store sizes and initialize temp buffer
|
||||
if (curBlock == 0U) {
|
||||
uint32_t size = GET_UINT32(data, 0U);
|
||||
uint32_t compressedSize = GET_UINT32(data, 4U);
|
||||
|
||||
frag->size = size;
|
||||
frag->compressedSize = compressedSize;
|
||||
}
|
||||
|
||||
// scope is intentional
|
||||
{
|
||||
frag->blockId = curBlock;
|
||||
if (frag->size < FRAG_BLOCK_SIZE)
|
||||
frag->data = new uint8_t[FRAG_BLOCK_SIZE + 1U];
|
||||
else
|
||||
frag->data = new uint8_t[frag->size + 1U];
|
||||
|
||||
::memcpy(frag->data, data + FRAG_HDR_SIZE, FRAG_BLOCK_SIZE);
|
||||
// Utils::dump(1U, "Block Payload", frag->data, FRAG_BLOCK_SIZE);
|
||||
fragments.insert(curBlock, frag);
|
||||
}
|
||||
|
||||
LogInfoEx(LOG_NET, "%s, Inbound Packet Fragment, block %u of %u, rxFragments = %u", m_name, curBlock, blockCnt, fragments.size());
|
||||
|
||||
// do we have all the blocks?
|
||||
if (fragments.size() == blockCnt + 1U) {
|
||||
uint8_t* buffer = nullptr;
|
||||
fragments.lock(false);
|
||||
if (fragments[0] == nullptr) {
|
||||
LogError(LOG_NET, "%s, Packet Fragment, error missing block 0? Packet dropped.", m_name);
|
||||
fragments.unlock();
|
||||
clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fragments[0]->size == 0U) {
|
||||
LogError(LOG_NET, "%s, Packet Fragment, error missing size information", m_name);
|
||||
fragments.unlock();
|
||||
clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fragments[0]->compressedSize == 0U) {
|
||||
LogError(LOG_NET, "%s, Packet Fragment, error missing compressed size information", m_name);
|
||||
fragments.unlock();
|
||||
clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t compressedLen = fragments[0]->compressedSize;
|
||||
uint32_t len = fragments[0]->size;
|
||||
|
||||
buffer = new uint8_t[len +1U];
|
||||
::memset(buffer, 0x00U, len + 1U);
|
||||
if (fragments.size() == 1U) {
|
||||
::memcpy(buffer, fragments[0U]->data, len);
|
||||
} else {
|
||||
for (uint8_t i = 0U; i < fragments.size(); i++) {
|
||||
uint32_t offs = i * FRAG_BLOCK_SIZE;
|
||||
::memcpy(buffer + offs, fragments[i]->data, FRAG_BLOCK_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_compression) {
|
||||
uint32_t decompressedLen = 0U;
|
||||
*message = Compression::decompress(buffer, compressedLen, &decompressedLen);
|
||||
|
||||
// check that we got the appropriate data
|
||||
if (decompressedLen == len && message != nullptr) {
|
||||
if (outLength != nullptr) {
|
||||
*outLength = len;
|
||||
}
|
||||
|
||||
fragments.unlock();
|
||||
clear();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
*message = buffer;
|
||||
if (outLength != nullptr) {
|
||||
*outLength = len;
|
||||
}
|
||||
|
||||
fragments.unlock();
|
||||
clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
fragments.unlock();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Encode a network packet fragment. */
|
||||
|
||||
void PacketBuffer::encode(uint8_t* data, uint32_t length)
|
||||
{
|
||||
assert(data != nullptr);
|
||||
assert(length > 0U);
|
||||
|
||||
// erase any buffered fragments
|
||||
clear();
|
||||
|
||||
// create temporary buffer
|
||||
uint32_t compressedLen = 0U;
|
||||
uint8_t* buffer = nullptr;
|
||||
if (m_compression) {
|
||||
buffer = Compression::compress(data, length, &compressedLen);
|
||||
} else {
|
||||
buffer = new uint8_t[length];
|
||||
::memset(buffer, 0x00U, length);
|
||||
::memcpy(buffer, data, length);
|
||||
compressedLen = length;
|
||||
}
|
||||
|
||||
// create packet fragments
|
||||
uint8_t blockCnt = (compressedLen / FRAG_BLOCK_SIZE) + (compressedLen % FRAG_BLOCK_SIZE ? 1U : 0U);
|
||||
uint32_t offs = 0U;
|
||||
for (uint8_t i = 0U; i < blockCnt; i++) {
|
||||
// build dataset
|
||||
uint16_t bufSize = FRAG_SIZE;
|
||||
|
||||
Fragment* frag = new Fragment();
|
||||
frag->blockId = i;
|
||||
|
||||
frag->data = new uint8_t[bufSize];
|
||||
::memset(frag->data, 0x00U, bufSize);
|
||||
|
||||
if (i == 0U) {
|
||||
SET_UINT32(length, frag->data, 0U);
|
||||
frag->size = length;
|
||||
SET_UINT32(compressedLen, frag->data, 4U);
|
||||
frag->compressedSize = compressedLen;
|
||||
}
|
||||
|
||||
frag->data[8U] = i;
|
||||
frag->data[9U] = blockCnt - 1U;
|
||||
|
||||
uint32_t blockSize = FRAG_BLOCK_SIZE;
|
||||
if (offs + FRAG_BLOCK_SIZE > compressedLen)
|
||||
blockSize = FRAG_BLOCK_SIZE - ((offs + FRAG_BLOCK_SIZE) - compressedLen);
|
||||
|
||||
::memcpy(frag->data + FRAG_HDR_SIZE, buffer + offs, blockSize);
|
||||
|
||||
offs += FRAG_BLOCK_SIZE;
|
||||
|
||||
fragments.insert(i, frag);
|
||||
LogInfoEx(LOG_NET, "%s, Outbound Packet Fragment, block %u of %u, txFragments = %u", m_name, i, blockCnt - 1U, fragments.size());
|
||||
}
|
||||
|
||||
delete[] buffer;
|
||||
}
|
||||
|
||||
/* Helper to clear currently buffered fragments. */
|
||||
|
||||
void PacketBuffer::clear()
|
||||
{
|
||||
fragments.lock(false);
|
||||
for (auto entry : fragments) {
|
||||
Fragment* frag = entry.second;
|
||||
if (frag != nullptr) {
|
||||
if (frag->data != nullptr)
|
||||
delete[] frag->data;
|
||||
frag->data = nullptr;
|
||||
delete frag;
|
||||
}
|
||||
}
|
||||
fragments.unlock();
|
||||
|
||||
fragments.clear();
|
||||
}
|
||||
@ -0,0 +1,112 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Digital Voice Modem - Common Library
|
||||
* GPLv2 Open Source. Use is subject to license terms.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* Copyright (C) 2025 Bryan Biedenkapp, N2PLL
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* @file PacketBuffer.h
|
||||
* @ingroup network_core
|
||||
* @file PacketBuffer.cpp
|
||||
* @ingroup network_core
|
||||
*/
|
||||
#if !defined(__PACKET_BUFFER_H__)
|
||||
#define __PACKET_BUFFER_H__
|
||||
|
||||
#include "common/Defines.h"
|
||||
#include "common/concurrent/unordered_map.h"
|
||||
#include "common/zlib/Compression.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Constants
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#define FRAG_HDR_SIZE 10
|
||||
#define FRAG_BLOCK_SIZE 534
|
||||
#define FRAG_SIZE (FRAG_HDR_SIZE + FRAG_BLOCK_SIZE)
|
||||
|
||||
namespace network
|
||||
{
|
||||
// ---------------------------------------------------------------------------
|
||||
// Class Declaration
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief Represents a fragmented packet buffer.
|
||||
* @ingroup network_core
|
||||
*/
|
||||
class HOST_SW_API PacketBuffer {
|
||||
public:
|
||||
/**
|
||||
* @brief Initializes a new instance of the PacketBuffer class.
|
||||
* @param compression Flag indicating whether packet data should be compressed automatically.
|
||||
* @param name Name of buffer.
|
||||
*/
|
||||
explicit PacketBuffer(bool compression, const char* name);
|
||||
/**
|
||||
* @brief Finalizes a instance of the PacketBuffer class.
|
||||
*/
|
||||
~PacketBuffer();
|
||||
|
||||
/**
|
||||
* @brief Decode a network packet fragment.
|
||||
* @param[in] data Buffer containing packet fragment to decode.
|
||||
* @param[out] message Buffer containing assembled message.
|
||||
* @param[out] outLength Length of assembled message.
|
||||
*/
|
||||
bool decode(const uint8_t* data, uint8_t** message, uint32_t* outLength);
|
||||
/**
|
||||
* @brief Encode a network packet fragment.
|
||||
* @param[out] data Message to encode.
|
||||
* @param length Length of message.
|
||||
*/
|
||||
void encode(uint8_t* data, uint32_t length);
|
||||
|
||||
/**
|
||||
* @brief Helper to clear currently buffered fragments.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Represents a packet buffer fragment.
|
||||
*/
|
||||
class Fragment {
|
||||
public:
|
||||
/**
|
||||
* @brief Compressed size of the packet.
|
||||
*/
|
||||
uint32_t compressedSize;
|
||||
/**
|
||||
* @brief Uncompressed size of the packet.
|
||||
*/
|
||||
uint32_t size;
|
||||
|
||||
/**
|
||||
* @brief Size of the packet fragment block.
|
||||
*/
|
||||
uint32_t blockSize;
|
||||
/**
|
||||
* @brief Block ID of the fragment.
|
||||
*/
|
||||
uint8_t blockId;
|
||||
|
||||
/**
|
||||
* @brief Fragment data.
|
||||
*/
|
||||
uint8_t* data;
|
||||
};
|
||||
concurrent::unordered_map<uint8_t, Fragment*> fragments;
|
||||
|
||||
private:
|
||||
bool m_compression;
|
||||
const char* m_name;
|
||||
};
|
||||
} // namespace network
|
||||
|
||||
#endif // __PACKET_BUFFER_H__
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue