Appearance
file /Users/ios_developer/workspace/coldwave-os/build/_deps/flake-src/inc/FlakeTypes.h
Types
Defines
| Name | |
|---|---|
| UINT8_FMT | |
| UINT16_FMT | |
| UINT32_FMT | |
| INT8_FMT | |
| INT16_FMT | |
| INT32_FMT | |
| UINT32_HEX_FMT | |
| DEALLOC_SELF_FLAG | |
| DONT_SERIALIZE_FLAG | |
| DONT_COPY_FLAG | |
| DONT_CACHE_FLAG | |
| TAG_TYPE(x) | document me |
| TAG_ID(x) | document me |
| TAG_READONLY | document me |
| TAG_ACTIONABLE | document me |
| TAG_VOLATILE | document me |
| TAG_NULL | document me |
| TAG_ERROR | document me |
| TAG_ARRAY | document me |
| TAG_META | document me |
| IS_ACTIONABLE(x) | document me |
| IS_READONLY(x) | document me |
| IS_META(x) | document me |
| IS_RESERVED(x) | document me |
| IS_ERROR(x) | document me |
| IS_VOLATILE(x) | document me |
| IS_ARRAY(x) | document me |
| IS_STREAM(x) | document me |
| TT_INT32 | document me |
| TT_INT16 | document me |
| TT_INT8 | document me |
| TT_UINT32 | document me |
| TT_UINT16 | document me |
| TT_UINT8 | document me |
| TT_BOOL | document me |
| TT_UUID | document me |
| TT_FLOAT | document me |
| TT_DATETIME | document me |
| TT_BIN | document me |
| TT_STRING | document me |
| TT_TABLE | document me |
| TT_OBJECT | document me |
| TT_STRING_STREAM | document me |
| TT_BIN_STREAM | document me |
| ERROR_VAL(x) | document me |
Macros Documentation
define UINT8_FMT
cpp
#define UINT8_FMT "u"define UINT16_FMT
cpp
#define UINT16_FMT "u"define UINT32_FMT
cpp
#define UINT32_FMT "u"define INT8_FMT
cpp
#define INT8_FMT "d"define INT16_FMT
cpp
#define INT16_FMT "d"define INT32_FMT
cpp
#define INT32_FMT "d"define UINT32_HEX_FMT
cpp
#define UINT32_HEX_FMT "lX"define DEALLOC_SELF_FLAG
cpp
#define DEALLOC_SELF_FLAG (1UL)define DONT_SERIALIZE_FLAG
cpp
#define DONT_SERIALIZE_FLAG (2UL)define DONT_COPY_FLAG
cpp
#define DONT_COPY_FLAG (4UL)define DONT_CACHE_FLAG
cpp
#define DONT_CACHE_FLAG (8UL)define TAG_TYPE
cpp
#define TAG_TYPE(
x
)
((x) & 0x7FFU)document me
define TAG_ID
cpp
#define TAG_ID(
x
)
((x) >> 16)document me
define TAG_READONLY
cpp
#define TAG_READONLY (0x1000UL)document me
define TAG_ACTIONABLE
cpp
#define TAG_ACTIONABLE (0x2000UL)document me
define TAG_VOLATILE
cpp
#define TAG_VOLATILE (0x4000UL)document me
define TAG_NULL
cpp
#define TAG_NULL (0x8000UL)document me
define TAG_ERROR
cpp
#define TAG_ERROR (0x100UL)document me
define TAG_ARRAY
cpp
#define TAG_ARRAY (0x040UL)document me
define TAG_META
cpp
#define TAG_META (0x800UL)document me
define IS_ACTIONABLE
cpp
#define IS_ACTIONABLE(
x
)
((x & TAG_ACTIONABLE) == TAG_ACTIONABLE)document me
define IS_READONLY
cpp
#define IS_READONLY(
x
)
((x & TAG_READONLY) == TAG_READONLY)document me
define IS_META
cpp
#define IS_META(
x
)
((x & TAG_META) == TAG_META)document me
define IS_RESERVED
cpp
#define IS_RESERVED(
x
)
((x & 0xfff0U) == 0xfff0U)document me
define IS_ERROR
cpp
#define IS_ERROR(
x
)
((x & TAG_ERROR) == TAG_ERROR)document me
define IS_VOLATILE
cpp
#define IS_VOLATILE(
x
)
((x & TAG_VOLATILE) == TAG_VOLATILE)document me
define IS_ARRAY
cpp
#define IS_ARRAY(
x
)
((x & TAG_ARRAY) == TAG_ARRAY)document me
define IS_STREAM
cpp
#define IS_STREAM(
x
)
(((x & 0x81U) == 0x81U) || ((x & 0x82U) == 0x82U))document me
define TT_INT32
cpp
#define TT_INT32 (0x1U)document me
define TT_INT16
cpp
#define TT_INT16 (0x2U)document me
define TT_INT8
cpp
#define TT_INT8 (0x3U)document me
define TT_UINT32
cpp
#define TT_UINT32 (0x4U)document me
define TT_UINT16
cpp
#define TT_UINT16 (0x5U)document me
define TT_UINT8
cpp
#define TT_UINT8 (0x6U)document me
define TT_BOOL
cpp
#define TT_BOOL (0x7U)document me
define TT_UUID
cpp
#define TT_UUID (0x8U)document me
define TT_FLOAT
cpp
#define TT_FLOAT (0x9U)document me
define TT_DATETIME
cpp
#define TT_DATETIME (0xAU)document me
define TT_BIN
cpp
#define TT_BIN (0xCU)document me
define TT_STRING
cpp
#define TT_STRING (0xEU)document me
define TT_TABLE
cpp
#define TT_TABLE (0x1CU)document me
define TT_OBJECT
cpp
#define TT_OBJECT (0x80U)document me
define TT_STRING_STREAM
cpp
#define TT_STRING_STREAM (0x81U)document me
define TT_BIN_STREAM
cpp
#define TT_BIN_STREAM (0x82U)document me
define ERROR_VAL
cpp
#define ERROR_VAL(
x
)
(((x).tag() & TAG_ERROR) == TAG_ERROR)document me
Source code
cpp
/*******************************************************************************
* @file FlakeTypes.h
* @license This file is part of the ImagineOn Flake software package
* licensed under the ImagineOn software-licensing terms available
* under https://www.imagineon.de/de/info/licensing-terms
* @copyright Copyright (c) 2025 ImagineOn GmbH. www.imagineon.de.
*******************************************************************************/
#ifndef FLAKE_TYPES_H_
#define FLAKE_TYPES_H_
#include <string>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <cstdio>
#include <optional>
#include <variant>
#include <float.h>
#include <math.h>
#include "util.h"
#ifdef _WIN32
#include <stdint.h>
#endif
#if __WORDSIZE == 64
#define UINT8_FMT "u"
#define UINT16_FMT "u"
#define UINT32_FMT "u"
#define INT8_FMT "d"
#define INT16_FMT "d"
#define INT32_FMT "d"
#define UINT32_HEX_FMT "X"
#else
#define UINT8_FMT "u"
#define UINT16_FMT "u"
#define UINT32_FMT "u"
#define INT8_FMT "d"
#define INT16_FMT "d"
#define INT32_FMT "d"
#define UINT32_HEX_FMT "lX"
#endif
#include "FlakeConst.h"
#define DEALLOC_SELF_FLAG (1UL)
#define DONT_SERIALIZE_FLAG (2UL)
#define DONT_COPY_FLAG (4UL)
#define DONT_CACHE_FLAG (8UL)
#define TAG_TYPE(x) ((x) & 0x7FFU)
#define TAG_ID(x) ((x) >> 16)
#define TAG_READONLY (0x1000UL)
#define TAG_ACTIONABLE (0x2000UL)
#define TAG_VOLATILE (0x4000UL)
#define TAG_NULL (0x8000UL)
#define TAG_ERROR (0x100UL)
#define TAG_ARRAY (0x040UL)
#define TAG_META (0x800UL)
#define IS_ACTIONABLE(x) ((x & TAG_ACTIONABLE) == TAG_ACTIONABLE)
#define IS_READONLY(x) ((x & TAG_READONLY) == TAG_READONLY)
#define IS_META(x) ((x & TAG_META) == TAG_META)
#define IS_RESERVED(x) ((x & 0xfff0U) == 0xfff0U)
#define IS_ERROR(x) ((x & TAG_ERROR) == TAG_ERROR)
#define IS_VOLATILE(x) ((x & TAG_VOLATILE) == TAG_VOLATILE)
#define IS_ARRAY(x) ((x & TAG_ARRAY) == TAG_ARRAY)
#define IS_STREAM(x) (((x & 0x81U) == 0x81U) || ((x & 0x82U) == 0x82U))
#define TT_INT32 (0x1U)
#define TT_INT16 (0x2U)
#define TT_INT8 (0x3U)
#define TT_UINT32 (0x4U)
#define TT_UINT16 (0x5U)
#define TT_UINT8 (0x6U)
#define TT_BOOL (0x7U)
#define TT_UUID (0x8U)
#define TT_FLOAT (0x9U)
#define TT_DATETIME (0xAU)
#define TT_BIN (0xCU)
#define TT_STRING (0xEU)
#define TT_TABLE (0x1CU)
#define TT_OBJECT (0x80U)
#define TT_STRING_STREAM (0x81U)
#define TT_BIN_STREAM (0x82U)
#define ERROR_VAL(x) (((x).tag() & TAG_ERROR) == TAG_ERROR)
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
namespace flake
{
template <typename T>
constexpr unsigned int UNSIGNED(T v) noexcept
{
return static_cast<unsigned int>(v);
}
template <typename T>
constexpr int SIGNED(T v) noexcept
{
return static_cast<int>(v);
}
template <typename T>
constexpr std::uint32_t U32(T v) noexcept
{
return static_cast<std::uint32_t>(v);
}
template <typename T>
constexpr std::int32_t I32(T v) noexcept
{
return static_cast<std::int32_t>(v);
}
template <typename T>
constexpr std::uint16_t U16(T v) noexcept
{
return static_cast<std::uint16_t>(v);
}
template <typename T>
constexpr std::int16_t I16(T v) noexcept
{
return static_cast<std::int16_t>(v);
}
template <typename T>
constexpr std::uint8_t U8(T v) noexcept
{
return static_cast<std::uint8_t>(v);
}
template <typename T>
constexpr std::int8_t I8(T v) noexcept
{
return static_cast<std::int8_t>(v);
}
inline bool tokens_match(unsigned a, unsigned b)
{
return a == b;
}
inline bool addresses_match(unsigned a, unsigned b)
{
return a == b;
}
inline bool safeWrite(uint8_t* buf,
uint16_t bufSize,
uint16_t& offset,
const uint8_t* src,
uint16_t len,
uint16_t* outConsumed = nullptr)
{
bool result = false;
if (U32(offset) + U32(len) <= bufSize)
{
memcpy(&buf[offset], src, len);
offset += U32(len);
if (nullptr != outConsumed)
{
*outConsumed = len;
}
result = true;
}
return result;
}
inline bool safeRead(const uint8_t* buf,
uint16_t bufSize,
uint16_t& offset,
uint8_t* dst,
uint16_t len,
uint16_t* outConsumed = nullptr)
{
bool result = false;
if (U32(offset) + U32(len) <= bufSize)
{
memcpy(dst, &buf[offset], len);
offset += U32(len);
if (nullptr != outConsumed)
{
*outConsumed = len;
}
result = true;
}
return result;
}
struct Serializable
{
protected:
~Serializable() = default;
public:
virtual bool
serialize(uint16_t* outLen, uint8_t** outBuf) = 0;
virtual bool
deserialize(uint16_t len, uint8_t* buf) = 0;
};
class UniqueId
{
public:
UniqueId() = default;
static constexpr std::size_t SIZE = 16U;
UniqueId(const UniqueId& other)
{
*this = other;
}
void
set_nil()
{
for (unsigned i = 0U; i < 16U; i++)
{
data[i] = U8(0U);
}
}
#if FLAKE_DEBUG_LOGGING
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat"
char*
toString() const
{
char* out = new char[37];
snprintf(
out, 37,
"%02x%02x%02x%02x-"
"%02x%02x-"
"%02x%02x-"
"%02x%02x-"
"%02x%02x%02x%02x%02x%02x",
data[0], data[1], data[2], data[3], data[4], data[5], data[6],
data[7], data[8], data[9], data[10], data[11], data[12], data[13],
data[14], data[15]);
return out;
}
#pragma GCC diagnostic pop
#endif
void initFromString(const char* in)
{
if ((utils::strlen(in, 37U) == 36U) && ((in[8] == '-') ||
(in[13] == '-') || (in[18] == '-') || (in[23] == '-')))
{
for (unsigned i = 0U; i < UniqueId::SIZE; i++)
{
data[i] = U8(0U);
}
unsigned index = 0U;
unsigned i = 0U;
while (index < 36U)
{
char c = in[index];
int value = 0;
if (c >= '0' && c <= '9')
{
value = (c - '0');
}
else if (c >= 'A' && c <= 'F')
{
value = (10 + (c - 'A'));
}
else if (c >= 'a' && c <= 'f')
{
value = (10 + (c - 'a'));
}
else
{
index++;
continue;
}
unsigned j = i / 2U;
data[j] += value << (((i + 1U) % 2U) * 4U);
++i;
++index;
}
}
}
bool
is_nil() const
{
bool result = true;
for (unsigned char i : data)
{
if (i != 0U)
{
result = false;
}
}
return result;
}
UniqueId&
operator=(UniqueId const& rhs)
{
if (this != &rhs)
{
memcpy(&data[0], &rhs.data[0], UniqueId::SIZE * sizeof(uint8_t));
}
return *this;
}
void setData(uint8_t data[UniqueId::SIZE])
{
memcpy(this->data, data, UniqueId::SIZE * sizeof(uint8_t));
}
uint8_t* getData()
{
return data;
}
private:
uint8_t data[SIZE];
};
inline bool operator==(UniqueId& lhs, UniqueId& rhs)
{
bool equal = true;
std::size_t i = 0U;
while ((i < UniqueId::SIZE) && equal)
{
unsigned a = U32(lhs.getData()[i]);
unsigned b = U32(rhs.getData()[i]);
if (a != b)
{
equal = false;
}
else
{
++i;
}
}
return equal;
}
inline bool operator!=(UniqueId& lhs, UniqueId& rhs)
{
return !(lhs == rhs);
}
inline bool operator<(UniqueId& lhs, UniqueId& rhs)
{
bool less = false;
std::size_t i = 0U;
while ((i < UniqueId::SIZE) && (!less))
{
unsigned a = U32(lhs.getData()[i]);
unsigned b = U32(rhs.getData()[i]);
if (a < b)
{
less = true;
}
else if (a > b)
{
break;
}
else
{
++i;
}
}
return less;
}
typedef UniqueId uniqueId_t;
typedef uint16_t addr_t;
typedef struct
{
uint16_t cb;
uint8_t* lpb;
} binary_t;
typedef int32_t tt_int32_t;
typedef int16_t tt_int16_t;
typedef int8_t tt_int8_t;
typedef uint32_t tt_uint32_t;
typedef uint16_t tt_uint16_t;
typedef uint8_t tt_uint8_t;
typedef bool tt_bool_t;
typedef binary_t tt_bin_t;
typedef uniqueId_t tt_uuid_t;
typedef std::string tt_str_t;
typedef float tt_float_t;
typedef struct array_t
{
uint16_t totalSize;
uint8_t* lpValues;
uint16_t type;
} array_t;
typedef array_t tt_array_t;
class Array {
public:
explicit Array(array_t* arr) : arr_(arr) {}
static uint8_t sizeForType(uint8_t t)
{
switch (t) {
case TT_INT8:
case TT_UINT8:
case TT_BOOL: return 1;
case TT_INT16:
case TT_UINT16: return 2;
case TT_INT32:
case TT_UINT32:
case TT_FLOAT: return 4;
case TT_UUID: return 16;
default: return 0;
}
}
uint8_t elementSize() const {
return sizeForType(arr_->type);
}
uint16_t count() const {
uint8_t es = elementSize();
return es ? arr_->totalSize / es : 0;
}
template<typename T>
bool set(uint16_t index, const T& value) {
if (!arr_ || sizeof(T) != elementSize() || index >= count()) return false;
memcpy(&arr_->lpValues[index * sizeof(T)], &value, sizeof(T));
return true;
}
template<typename T>
bool get(uint16_t index, T& out) const {
if (!arr_ || sizeof(T) != elementSize() || index >= count()) return false;
memcpy(&out, &arr_->lpValues[index * sizeof(T)], sizeof(T));
return true;
}
bool resize(uint16_t newCount) {
uint8_t es = elementSize();
if (!es) return false;
uint8_t* newBuf = static_cast<uint8_t*>(std::realloc(arr_->lpValues, newCount * es));
if (!newBuf) return false;
arr_->lpValues = newBuf;
arr_->totalSize = newCount * es;
return true;
}
void init(uint16_t type, uint16_t count, uint8_t* buf) {
arr_->type = type;
arr_->lpValues = buf;
arr_->totalSize = count * elementSize();
}
uint8_t* raw() const { return arr_ ? arr_->lpValues : nullptr; }
private:
array_t* arr_;
};
namespace ReservedProperties
{
constexpr uint32_t PROP_MAPPINGS = U32(0xFFFFUL << 16U | (U8(TT_BIN) | TAG_ARRAY | TAG_READONLY));
constexpr uint32_t PARENT_OBJECT = U32(0xFFFEUL << 16U | (U8(TT_UINT16) | TAG_READONLY));
constexpr uint32_t OBJECT_UUID = U32(0xFFFDUL << 16U | (U8(TT_UUID) | TAG_READONLY));
}
namespace MetaProperties
{
constexpr uint32_t TIMESTAMP = U32(0x9UL << 16U | U8(TT_DATETIME) | TAG_META);
}
namespace ParameterProperties {
constexpr uint32_t OBJECT_TYPE = U32(0x1UL << 16U | U8(TT_UUID) | TAG_READONLY );
constexpr uint32_t OBJECT_ADDR = U32(0x2UL << 16U | U8(TT_UINT16) | TAG_READONLY );
constexpr uint32_t BROADCAST_ADDR = U32(0x3UL << 16U | U8(TT_UINT16) | TAG_READONLY );
constexpr uint32_t MESSAGE_NAME = U32(0x4UL << 16U | U8(TT_STRING) );
constexpr uint32_t CHILDREN_TABLE = U32(0x5UL << 16U | U8(TT_BIN) );
constexpr uint32_t COLUMN_SET = U32(0x6UL << 16U | U8(TT_UINT16) | TAG_ARRAY );
constexpr uint32_t LAST_MODIFICATION_TIME = U32(0x7UL << 16U | U8(TT_DATETIME) );
constexpr uint32_t CREATION_TIME = U32(0x8UL << 16U | U8(TT_DATETIME) );
constexpr uint32_t OBJECT_TABLE = U32(0xDUL << 16U | U8(TT_BIN) );
constexpr uint32_t PROP_TAG = U32(0xFUL << 16U | U8(TT_UINT32) );
constexpr uint32_t PROP_NAME = U32(0x10UL << 16U | U8(TT_STRING) );
constexpr uint32_t PROP_TYPE = U32(0x11UL << 16U | U8(TT_UINT16) );
constexpr uint32_t REQUIRES_AUTH = U32(0x18UL << 16U | U8(U8(TT_BOOL)) );
constexpr uint32_t STREAM_DATA_STR = U32(0x20UL << 16U | U8(TT_STRING) );
constexpr uint32_t STREAM_DATA_BIN = U32(0x21UL << 16U | U8(TT_BIN) );
constexpr uint32_t STREAM_LEN = U32(0x22UL << 16U | U8(TT_UINT16) );
constexpr uint32_t STREAM_POS = U32(0x23UL << 16U | U8(TT_UINT32) );
constexpr uint32_t AUTH_TYPE = U32(0x31UL << 16U | U8(TT_UINT8) | TAG_READONLY );
constexpr uint32_t AUTH_USER = U32(0x32UL << 16U | U8(TT_STRING) | TAG_READONLY );
constexpr uint32_t AUTH_PASS = U32(0x33UL << 16U | U8(TT_STRING) | TAG_READONLY );
constexpr uint32_t SIGN_ALGO = U32(0x34UL << 16U | U8(TT_STRING) | TAG_READONLY );
constexpr uint32_t SIGN_HASH = U32(0x35UL << 16U | U8(TT_BIN) | TAG_READONLY );
constexpr uint32_t SIGNATURE = U32(0x36UL << 16U | U8(TT_BIN) | TAG_READONLY );
constexpr uint32_t OBJECT_TYPE_NAME = U32(0x101UL << 16U | U8(TT_STRING) | TAG_READONLY );
}
enum class flakeAuthType : uint_least8_t
{
atSignature = U8(1),
atInteractive = U8(2)
};
class TagArray
{
TagArray(const TagArray& ta)
{
numTags = ta.numTags;
if (ta.numTags > 0U)
{
tags = new uint32_t[numTags];
for (uint32_t i = 0U; i < ta.numTags; i++)
{
tags[i] = ta.tags[i];
}
}
}
TagArray()
{
tags = nullptr;
numTags = 0U;
}
~TagArray()
{
if (numTags > 0U)
{
delete[] tags;
}
}
TagArray&
operator=(const TagArray& other)
{
if (this != &other)
{
numTags = other.numTags;
if (other.numTags > 0U)
{
tags = new uint32_t[numTags];
for (uint32_t i = 0U; i < other.numTags; i++)
{
tags[i] = other.tags[i];
}
}
}
return *this;
}
private:
mutable uint32_t numTags;
mutable uint32_t* tags;
};
typedef std
::optional<std::variant<tt_int8_t,
tt_int16_t,
tt_int32_t,
tt_uint8_t,
tt_uint16_t,
tt_uint32_t,
tt_bool_t,
tt_float_t,
tt_uuid_t,
tt_bin_t,
tt_str_t,
tt_array_t>> PropValue;
class Property;
class PropArray;
template <uint32_t PropType>
struct PropTypeFromTag;
template <uint32_t PropTag>
using PropType = typename PropTypeFromTag<TAG_TYPE(PropTag)>::type;
template <uint32_t PropTag>
using PropType = typename PropTypeFromTag<TAG_TYPE(PropTag)>::type;
template <>
struct PropTypeFromTag<TT_INT32>
{
using type = tt_int32_t;
static constexpr tt_int32_t default_value = 0;
};
template <>
struct PropTypeFromTag<TT_INT16>
{
using type = tt_int16_t;
static constexpr tt_int16_t default_value = 0;
};
template <>
struct PropTypeFromTag<TT_INT8>
{
using type = tt_int8_t;
static constexpr tt_int8_t default_value = I8(0);
};
template <>
struct PropTypeFromTag<TT_UINT32>
{
using type = tt_uint32_t;
static constexpr tt_uint32_t default_value = 0U;
};
template <>
struct PropTypeFromTag<TT_UINT16>
{
using type = tt_uint16_t;
static constexpr tt_uint16_t default_value = 0U;
};
template <>
struct PropTypeFromTag<TT_UINT8>
{
using type = tt_uint8_t;
static constexpr tt_uint8_t default_value = U8(0U);
};
template <>
struct PropTypeFromTag<TT_BOOL>
{
using type = tt_bool_t;
static constexpr tt_bool_t default_value = false;
};
template <>
struct PropTypeFromTag<TT_BIN>
{
using type = tt_bin_t;
static constexpr tt_bin_t default_value = {0U, nullptr};
};
template <>
struct PropTypeFromTag<TT_FLOAT>
{
using type = tt_float_t;
static constexpr tt_float_t default_value = 0.0F;
};
template <>
struct PropTypeFromTag<TT_STRING>
{
using type = tt_str_t;
static constexpr std::string_view default_value = std::string_view();
};
template <>
struct PropTypeFromTag<TT_DATETIME>
{
using type = tt_uint32_t;
static constexpr tt_uint32_t default_value = 0U;
};
template <>
struct PropTypeFromTag<TT_UUID>
{
using type = tt_uuid_t;
static constexpr tt_uuid_t default_value = tt_uuid_t();
};
template <>
struct PropTypeFromTag<TT_STRING_STREAM>
{
using type = tt_str_t;
static constexpr std::string_view default_value = std::string_view();
};
template <>
struct PropTypeFromTag<TT_BIN_STREAM>
{
using type = tt_bin_t;
static constexpr tt_bin_t default_value = {0U, nullptr};
};
template <typename T>
struct is_std_optional : std::false_type
{
};
template <typename T>
struct is_std_optional<std::optional<T>> : std::true_type
{
};
template <typename T>
constexpr bool is_std_optional_v = is_std_optional<T>::value;
class Property
{
mutable uint32_t tag_;
uint8_t flags;
PropValue data_;
public:
uint32_t tag() const
{
return tag_;
}
bool isMeta() const
{
return (tag_ & TAG_META) == TAG_META;
}
void setNull()
{
tag_ |= TAG_NULL;
data_ = std::nullopt;
}
void setError(tt_int8_t err)
{
tag_ |= TAG_ERROR;
data_ = err;
}
PropValue data() const
{
return data_;
}
template <typename T>
void setData(T data)
{
if constexpr (
std::is_same_v<T, std::nullptr_t> ||
(std::is_pointer_v<T>) ||
(is_std_optional_v<T>)
)
{
if (!data)
{
data_ = std::nullopt;
return;
}
}
tag_ &= ~TAG_NULL;
data_ = data;
}
template <typename T>
std::optional<T> value() const
{
std::optional<T> result = std::nullopt;
if (data_ != std::nullopt && std::holds_alternative<T>(*data_))
{
result = std::get<T>(*data_);
}
return result;
}
explicit Property(const uint32_t _tag)
: tag_(_tag)
{
flags = U8(0U);
tag_ |= TAG_NULL;
}
Property()
: tag_(0U)
{
flags = U8(0U);
tag_ |= TAG_NULL;
}
Property(const Property& v)
{
tag_ = 0U;
flags = U8(0U);
copyFrom(v);
}
void
makeReadOnly() const
{
tag_ |= TAG_READONLY;
}
~Property()
{
if ((flags & DEALLOC_SELF_FLAG) == DEALLOC_SELF_FLAG)
{
if ((tag_ & TAG_ARRAY) == TAG_ARRAY)
{
if (auto arr = value<tt_array_t>())
{
if ((arr->totalSize) > 0U)
{
free(arr->lpValues);
}
}
}
else
{
switch (TAG_TYPE(tag_))
{
case TT_BIN:
if (auto bin = value<tt_bin_t>())
{
if ((bin->lpb != nullptr) && (bin->cb > 0U))
{
free(bin->lpb);
}
}
break;
default: break;
}
}
}
}
void
setDeallocSelf()
{
flags |= DEALLOC_SELF_FLAG;
}
Property&
operator=(const Property& other)
{
copyFrom(other);
return *this;
}
void
copyFrom(const Property& other)
{
tag_ = other.tag_;
flags = other.flags;
if ((other.tag_ & TAG_ARRAY) == TAG_ARRAY)
{
if (std::nullopt == value<tt_array_t>())
{
data_ = tt_array_t();
}
auto a1 = value<tt_array_t>();
if (auto a2 = other.value<tt_array_t>())
{
if (a1->lpValues != nullptr && ((flags & DEALLOC_SELF_FLAG) == DEALLOC_SELF_FLAG))
{
free(a1->lpValues);
}
a1->totalSize = a2->totalSize;
a1->type = a2->type;
if (a1->totalSize != 0U)
{
setDeallocSelf();
a1->lpValues = (uint8_t*)(malloc(U32(a1->totalSize)));
memcpy(a1->lpValues, a2->lpValues, U32(a1->totalSize));
}
data_ = *a1;
}
else
{
flags = U8(0U);
}
}
else
{
switch (TAG_TYPE(other.tag_))
{
case TT_BIN:
{
if (std::nullopt == value<tt_bin_t>())
{
data_ = tt_bin_t();
}
auto bin1 = value<tt_bin_t>();
if (auto bin2 = other.value<tt_bin_t>())
{
if (bin1->lpb != nullptr && ((flags & DEALLOC_SELF_FLAG) == DEALLOC_SELF_FLAG))
{
free(bin1->lpb);
bin1->lpb = nullptr;
}
bin1->cb = bin2->cb;
if (bin1->cb != 0U)
{
if ((other.flags & DONT_COPY_FLAG) == DONT_COPY_FLAG)
{
bin1->lpb = bin2->lpb;
}
else
{
setDeallocSelf();
bin1->lpb = (uint8_t*)(malloc(bin1->cb));
memcpy(bin1->lpb, bin2->lpb, bin1->cb);
}
}
data_ = *bin1;
}
else
{
flags = U8(0U);
}
}
break;
default:
{
flags = U8(0U);
data_ = other.data_;
}
break;
}
}
}
bool serializeTo(uint8_t* buf,
uint16_t& offset,
uint16_t bufSize,
uint16_t* consumedBytes = nullptr) const
{
const uint16_t startOff = offset;
uint16_t tmp{};
uint16_t startOffset = offset;
// 1) Header: Tag
if (!safeWrite(buf, bufSize, offset, reinterpret_cast<const uint8_t*>(&tag_),
U16(sizeof(tag_)), &tmp))
{
offset = startOffset;
return false;
}
// 2) Null oder Error
if ((tag_ & TAG_NULL) == TAG_NULL)
{
if (nullptr != consumedBytes)
{
*consumedBytes = utils::safe_sub<uint16_t>(offset, startOff);
}
return true;
}
if (ERROR_VAL(*this))
{
tt_int8_t e{};
if (data_ != std::nullopt && std::holds_alternative<tt_int8_t>(*data_))
{
e = std::get<tt_int8_t>(*data_);
}
if (!safeWrite(buf, bufSize, offset, reinterpret_cast<const uint8_t*>(&e),
U16(sizeof(e)), &tmp))
{
offset = startOffset;
return false;
}
if (nullptr != consumedBytes)
{
*consumedBytes = utils::safe_sub<uint16_t>(offset, startOff);
}
return true;
}
// 3) Array-Typen
if ((tag_ & TAG_ARRAY) == TAG_ARRAY)
{
if (auto arr = std::get_if<tt_array_t>(&*data_))
{
const uint16_t headerSize = 2U;
uint16_t payloadLen = arr->totalSize;
if (payloadLen > U32(UINT16_MAX) - headerSize)
{
offset = startOffset;
return false;
}
if (!safeWrite(buf, bufSize, offset, reinterpret_cast<const uint8_t*>(&payloadLen),
U16(sizeof(payloadLen)), &tmp))
{
offset = startOffset;
return false;
}
if (!safeWrite(buf, bufSize, offset, arr->lpValues, payloadLen, &tmp))
{
offset = startOffset;
return false;
}
}
if (nullptr != consumedBytes)
{
*consumedBytes = utils::safe_sub<uint16_t>(offset, startOff);
}
return true;
}
// 4) Primitive & Co.
switch (TAG_TYPE(tag_))
{
case TT_INT8:
{
tt_int8_t v{};
if (data_ != std::nullopt && std::holds_alternative<tt_int8_t>(*data_))
{
v = std::get<tt_int8_t>(*data_);
}
if (!safeWrite(buf, bufSize, offset, reinterpret_cast<const uint8_t*>(&v),
U16(sizeof(v)), &tmp))
{
offset = startOffset;
return false;
}
}
break;
case TT_INT16:
{
tt_int16_t v{};
if (data_ != std::nullopt && std::holds_alternative<tt_int16_t>(*data_))
{
v = std::get<tt_int16_t>(*data_);
}
if (!safeWrite(buf, bufSize, offset, reinterpret_cast<const uint8_t*>(&v),
U16(sizeof(v)), &tmp))
{
offset = startOffset;
return false;
}
}
break;
case TT_INT32:
{
tt_int32_t v{};
if (data_ != std::nullopt && std::holds_alternative<tt_int32_t>(*data_))
{
v = std::get<tt_int32_t>(*data_);
}
if (!safeWrite(buf, bufSize, offset, reinterpret_cast<const uint8_t*>(&v),
U16(sizeof(v)), &tmp))
{
offset = startOffset;
return false;
}
}
break;
case TT_UINT8:
{
tt_uint8_t v{};
if (data_ != std::nullopt && std::holds_alternative<tt_uint8_t>(*data_))
{
v = std::get<tt_uint8_t>(*data_);
}
if (!safeWrite(buf, bufSize, offset, reinterpret_cast<const uint8_t*>(&v),
U16(sizeof(v)), &tmp))
{
offset = startOffset;
return false;
}
}
break;
case TT_UINT16:
{
tt_uint16_t v{};
if (data_ != std::nullopt && std::holds_alternative<tt_uint16_t>(*data_))
{
v = std::get<tt_uint16_t>(*data_);
}
if (!safeWrite(buf, bufSize, offset, reinterpret_cast<const uint8_t*>(&v),
U16(sizeof(v)), &tmp))
{
offset = startOffset;
return false;
}
}
break;
case TT_UINT32:
case TT_DATETIME: //NOLINT MISRA 5-4-4 fallthrough on purpose
{
tt_uint32_t v{};
if (data_ != std::nullopt && std::holds_alternative<tt_uint32_t>(*data_))
{
v = std::get<tt_uint32_t>(*data_);
}
if (!safeWrite(buf, bufSize, offset, reinterpret_cast<const uint8_t*>(&v),
U16(sizeof(v)), &tmp))
{
offset = startOffset;
return false;
}
}
break;
case TT_BOOL:
{
tt_bool_t v = false;
if (data_ != std::nullopt && std::holds_alternative<tt_bool_t>(*data_))
{
v = std::get<tt_bool_t>(*data_);
}
if (!safeWrite(buf, bufSize, offset, reinterpret_cast<const uint8_t*>(&v),
U16(sizeof(v)), &tmp))
{
offset = startOffset;
return false;
}
}
break;
case TT_FLOAT:
{
tt_float_t v{};
if (data_ != std::nullopt && std::holds_alternative<tt_float_t>(*data_))
{
v = std::get<tt_float_t>(*data_);
}
if (!safeWrite(buf, bufSize, offset, reinterpret_cast<const uint8_t*>(&v),
U16(sizeof(v)), &tmp))
{
offset = startOffset;
return false;
}
}
break;
case TT_UUID:
{
tt_uuid_t v{};
if (data_ != std::nullopt && std::holds_alternative<tt_uuid_t>(*data_))
{
v = std::get<tt_uuid_t>(*data_);
}
if (!safeWrite(buf, bufSize, offset, v.getData(),
U16(sizeof(tt_uuid_t)), &tmp))
{
offset = startOffset;
return false;
}
}
break;
case TT_STRING:
{
tt_str_t s{};
if (data_ != std::nullopt && std::holds_alternative<tt_str_t>(*data_))
{
s = std::get<tt_str_t>(*data_);
}
uint16_t len = U16(s.size());
if (!safeWrite(buf, bufSize, offset, reinterpret_cast<const uint8_t*>(&len),
U16(sizeof(len)), &tmp))
{
offset = startOffset;
return false;
}
if (!safeWrite(buf, bufSize, offset, reinterpret_cast<const uint8_t*>(s.data()),
len, &tmp))
{
offset = startOffset;
return false;
}
}
break;
case TT_BIN:
case TT_TABLE: //NOLINT MISRA 5-4-4 fallthrough on purpose
{
tt_bin_t b{};
if (data_ != std::nullopt && std::holds_alternative<tt_bin_t>(*data_))
{
b = std::get<tt_bin_t>(*data_);
}
uint16_t cb = b.cb;
if (!safeWrite(buf, bufSize, offset, reinterpret_cast<const uint8_t*>(&cb),
U16(sizeof(cb)), &tmp))
{
offset = startOffset;
return false;
}
if (!safeWrite(buf, bufSize, offset, b.lpb, cb, &tmp))
{
offset = startOffset;
return false;
}
}
break;
case TT_STRING_STREAM:
case TT_BIN_STREAM: //NOLINT MISRA 5-4-4 fallthrough on purpose
case TT_OBJECT: //NOLINT MISRA 5-4-4 fallthrough on purpose
default: //NOLINT MISRA 5-4-4 fallthrough on purpose
return false;
break;
}
if (nullptr != consumedBytes)
{
*consumedBytes = utils::safe_sub<uint16_t>(offset, startOff);
}
return true;
}
bool deserializeFrom(const uint8_t* buf,
uint16_t& offset,
uint16_t bufSize,
uint16_t* consumedBytes = nullptr)
{
const uint16_t startOff = offset;
uint16_t tmp{};
// 1. Read Tag
uint16_t tagSize = U16(sizeof(tag_));
if (!safeRead(buf, bufSize, offset,
reinterpret_cast<uint8_t*>(&tag_), tagSize, &tmp))
{
offset = startOff;
return false;
}
// 2. Null-Tag?
if ((tag_ & TAG_NULL) == TAG_NULL)
{
if (nullptr != consumedBytes)
{
*consumedBytes = utils::safe_sub<uint16_t>(offset, startOff);
}
return true;
}
// 3. Error-Tag?
if (ERROR_VAL(*this))
{
tt_int8_t e{};
if (!safeRead(buf, bufSize, offset, reinterpret_cast<uint8_t*>(&e),
U16(sizeof(e)), &tmp))
{
offset = startOff;
return false;
}
data_ = e;
if (nullptr != consumedBytes)
{
*consumedBytes = utils::safe_sub<uint16_t>(offset, startOff);
}
return true;
}
// 4. Array-Typ
if ((tag_ & TAG_ARRAY) == TAG_ARRAY)
{
tt_array_t arr{};
uint16_t payloadLen{};
if (!safeRead(buf, bufSize, offset, reinterpret_cast<uint8_t*>(&payloadLen),
U16(sizeof(payloadLen)), &tmp))
{
offset = startOff;
return false;
}
if (payloadLen > 0U && UNSIGNED(offset) + payloadLen <= bufSize)
{
uint8_t* arr_buf = (uint8_t*)(malloc(U32(payloadLen)));
Array array(&arr);
array.init(tag_ & 0x1F, payloadLen / array.sizeForType(tag_ & 0x1F), arr_buf);
if (!safeRead(buf, bufSize, offset, arr.lpValues, payloadLen, &tmp))
{
free(arr.lpValues);
offset = startOff;
return false;
}
}
data_ = arr;
if (nullptr != consumedBytes)
{
*consumedBytes = utils::safe_sub<uint16_t>(offset, startOff);
}
setDeallocSelf();
return true;
}
// 5. Primitive & Co.
switch (TAG_TYPE(tag_))
{
case TT_INT8:
{
tt_int8_t v{};
if (safeRead(buf, bufSize, offset, reinterpret_cast<uint8_t*>(&v),
U16(sizeof(v)), &tmp))
{
data_ = v;
}
else
{
offset = startOff;
return false;
}
}
break;
case TT_INT16:
{
tt_int16_t v{};
if (safeRead(buf, bufSize, offset, reinterpret_cast<uint8_t*>(&v),
U16(sizeof(v)), &tmp))
{
data_ = v;
}
else
{
offset = startOff;
return false;
}
}
break;
case TT_INT32:
{
tt_int32_t v{};
if (safeRead(buf, bufSize, offset, reinterpret_cast<uint8_t*>(&v),
U16(sizeof(v)), &tmp))
{
data_ = v;
}
else
{
offset = startOff;
return false;
}
}
break;
case TT_UINT8:
{
tt_uint8_t v{};
if (safeRead(buf, bufSize, offset, reinterpret_cast<uint8_t*>(&v),
U16(sizeof(v)), &tmp))
{
data_ = v;
}
else
{
offset = startOff;
return false;
}
}
break;
case TT_UINT16:
{
tt_uint16_t v{};
if (safeRead(buf, bufSize, offset, reinterpret_cast<uint8_t*>(&v),
U16(sizeof(v)), &tmp))
{
data_ = v;
}
else
{
offset = startOff;
return false;
}
}
break;
case TT_UINT32:
case TT_DATETIME: //NOLINT MISRA 5-4-4 fallthrough on purpose
{
tt_uint32_t v{};
if (safeRead(buf, bufSize, offset, reinterpret_cast<uint8_t*>(&v),
U16(sizeof(v)), &tmp))
{
data_ = v;
}
else
{
offset = startOff;
return false;
}
}
break;
case TT_BOOL:
{
tt_bool_t v{};
if (safeRead(buf, bufSize, offset, reinterpret_cast<uint8_t*>(&v),
U16(sizeof(v)), &tmp))
{
data_ = v;
}
else
{
offset = startOff;
return false;
}
}
break;
case TT_FLOAT:
{
tt_float_t v{};
if (safeRead(buf, bufSize, offset, reinterpret_cast<uint8_t*>(&v),
U16(sizeof(v)), &tmp))
{
data_ = v;
}
else
{
offset = startOff;
return false;
}
}
break;
case TT_UUID:
{
tt_uuid_t u{};
if (safeRead(buf, bufSize, offset, u.getData(),
U16(sizeof(u)), &tmp))
{
data_ = u;
}
else
{
offset = startOff;
return false;
}
}
break;
case TT_STRING:
{
uint16_t len{};
if (!safeRead(buf, bufSize, offset,
reinterpret_cast<uint8_t*>(&len),
U16(sizeof(len)), &tmp))
{
offset = startOff;
return false;
}
if (len > 0U)
{
if (U32(offset) + len <= bufSize)
{
char* tmpBuf = (char*)malloc(len + 1U);
if (!safeRead(buf, bufSize, offset,
reinterpret_cast<uint8_t*>(tmpBuf), len, &tmp))
{
free(tmpBuf);
offset = startOff;
return false;
}
tmpBuf[len] = '\0';
data_ = tt_str_t(tmpBuf, len);
free(tmpBuf);
}
else
{
offset = startOff;
return false;
}
}
else
{
data_ = tt_str_t();
}
}
break;
case TT_BIN:
case TT_TABLE: //NOLINT MISRA 5-4-4 fallthrough on purpose
{
tt_bin_t b{};
if (safeRead(buf, bufSize, offset, reinterpret_cast<uint8_t*>(&b.cb),
U16(sizeof(b.cb))))
{
if (b.cb > 0U)
{
if (U32(offset) + b.cb <= bufSize)
{
b.lpb = (uint8_t*)(malloc(b.cb));
if (!safeRead(buf, bufSize, offset, b.lpb, b.cb))
{
offset = startOff;
return false;
}
}
else
{
offset = startOff;
return false;
}
}
else
{
data_ = tt_bin_t();
}
}
else
{
offset = startOff;
return false;
}
setDeallocSelf();
data_ = b;
}
break;
case TT_STRING_STREAM:
case TT_BIN_STREAM: //NOLINT MISRA 5-4-4 fallthrough on purpose
case TT_OBJECT: //NOLINT MISRA 5-4-4 fallthrough on purpose
default: //NOLINT MISRA 5-4-4 fallthrough on purpose
offset = startOff;
return false;
}
if (nullptr != consumedBytes)
{
*consumedBytes = utils::safe_sub<uint16_t>(offset, startOff);
}
return true;
}
bool
isErrorValue() const
{
return (tag_ & TAG_ERROR) == TAG_ERROR;
}
uint16_t getSerializedSize() const
{
size_t result = sizeof(uint32_t);
if ((tag_ & TAG_NULL) != TAG_NULL)
{
if (ERROR_VAL(*this))
{
result = sizeof(uint32_t) + sizeof(tt_int8_t);
}
else if ((tag_ & TAG_ARRAY) == TAG_ARRAY)
{
auto optArr = value<tt_array_t>();
if (optArr.has_value())
{
const tt_array_t& arr = *optArr;
result = sizeof(uint32_t) + sizeof(uint16_t) + (arr.totalSize);
}
else
{
// MISRA 6-4-2: switch preferred over else-if for known cases
}
}
else
{
switch (TAG_TYPE(tag_))
{
case TT_FLOAT:
if (value<tt_float_t>())
{
result = sizeof(uint32_t) + sizeof(tt_float_t);
}
break;
case TT_UINT8:
if (value<tt_uint8_t>())
{
result = sizeof(uint32_t) + sizeof(tt_uint8_t);
}
break;
case TT_UINT16:
if (value<tt_uint16_t>())
{
result = sizeof(uint32_t) + sizeof(tt_uint16_t);
}
break;
case TT_UINT32:
case TT_DATETIME: //NOLINT MISRA 5-4-4 fallthrough on purpose
if (value<tt_uint32_t>())
{
result = sizeof(uint32_t) + sizeof(tt_uint32_t);
}
break;
case TT_INT8:
if (value<tt_int8_t>())
{
result = sizeof(uint32_t) + sizeof(tt_int8_t);
}
break;
case TT_INT16:
if (value<tt_int16_t>())
{
result = sizeof(uint32_t) + sizeof(tt_int16_t);
}
break;
case TT_INT32:
if (value<tt_int32_t>())
{
result = sizeof(uint32_t) + sizeof(tt_int32_t);
}
break;
case TT_BOOL:
if (value<tt_bool_t>())
{
result = sizeof(uint32_t) + sizeof(tt_bool_t);
}
break;
case TT_UUID:
if (value<tt_uuid_t>())
{
result = sizeof(uint32_t) + sizeof(tt_uuid_t);
}
break;
case TT_STRING:
if (value<tt_str_t>())
{
result = sizeof(uint32_t) + sizeof(tt_uint16_t) + (sizeof(char) * value<tt_str_t>()->
length());
}
break;
case TT_STRING_STREAM:
break;
case TT_BIN:
if (value<tt_bin_t>())
{
result = sizeof(uint32_t) + sizeof(tt_uint16_t) + (sizeof(uint8_t) * value<tt_bin_t>()->cb);
}
break;
case TT_BIN_STREAM:
break;
default:
break;
}
}
}
return U16(result);
}
friend bool operator==(const Property& lhs, const Property& rhs);
friend bool operator!=(const Property& lhs, const Property& rhs);
#if FLAKE_DEBUG_LOGGING
char*
toString() const
{
char* _str = nullptr;
char* tagStr = tagToString(tag_);
if (IS_ERROR(tag_))
{
_str = new char[120];
if (auto err = value<tt_int8_t>())
{
snprintf(_str, 120, "%30s: %s", tagStr, errorToString(
(long)*err));
}
else
{
snprintf(_str, 120, "%30s: NULL", tagStr);
}
free(tagStr);
return _str;
}
if ((tag_ & TAG_NULL) == TAG_NULL)
{
_str = new char[60];
snprintf(_str, 120, "%30s: NULL", tagStr);
free(tagStr);
return _str;
}
if ((tag_ & TAG_ARRAY) == TAG_ARRAY)
{
_str = new char[120];
if (auto arr = value<tt_array_t>())
{
Array array(&*arr);
snprintf(_str, 120, "%30s: Array (%d elements of size %d) ",
tagStr, array.count(), array.elementSize());
}
else
{
snprintf(_str, 120, "%30s: NULL", tagStr);
}
free(tagStr);
return _str;
}
switch (TAG_TYPE(tag_))
{
case TT_BOOL:
{
auto val = value<tt_bool_t>();
_str = new char[120];
if (val)
{
snprintf(_str, 120, "%30s: %s", tagStr, *val ? "true" : "false");
}
else
{
snprintf(_str, 120, "%30s: NULL", tagStr);
}
}
break;
case TT_UINT8:
{
auto val = value<tt_uint8_t>();
_str = new char[120];
if (val)
{
snprintf(_str, 120, "%30s: %" UINT8_FMT, tagStr, *val);
}
else
{
snprintf(_str, 120, "%30s: NULL", tagStr);
}
}
break;
case TT_UINT16:
{
auto val = value<tt_uint16_t>();
_str = new char[120];
if (val)
{
snprintf(_str, 120, "%30s: %" UINT16_FMT, tagStr, *val);
}
else
{
snprintf(_str, 120, "%30s: NULL", tagStr);
}
}
break;
case TT_UINT32:
{
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat"
auto val = value<tt_uint32_t>();
_str = new char[120];
if (val)
{
snprintf(_str, 120, "%30s: %" UINT32_FMT, tagStr, *val);
}
else
{
snprintf(_str, 120, "%30s: NULL", tagStr);
}
}
#pragma GCC diagnostic pop
break;
case TT_INT8:
{
auto val = value<tt_int8_t>();
_str = new char[120];
if (val)
{
snprintf(_str, 120, "%30s: %" INT8_FMT, tagStr, *val);
}
else
{
snprintf(_str, 120, "%30s: NULL", tagStr);
}
}
break;
case TT_INT16:
{
auto val = value<tt_int16_t>();
_str = new char[120];
if (val)
{
snprintf(_str, 120, "%30s: %" INT16_FMT, tagStr, *val);
}
else
{
snprintf(_str, 120, "%30s: NULL", tagStr);
}
}
break;
case TT_INT32:
{
auto val = value<tt_int32_t>();
_str = new char[120];
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat"
if (val)
{
snprintf(_str, 120, "%30s: %" INT32_FMT, tagStr, *val);
}
else
{
snprintf(_str, 120, "%30s: NULL", tagStr);
}
}
break;
case TT_DATETIME:
{
auto val = value<tt_uint32_t>();
_str = new char[120];
if (val)
{
time_t t = (time_t)*val;
struct tm *tm = gmtime(&t);
snprintf(_str, 120, "%30s: %04d-%02d-%02d %02d:%02d:%02d", tagStr, tm->tm_year + 1900,
tm->tm_mon + 1,
tm->tm_mday,
tm->tm_hour,
tm->tm_min,
tm->tm_sec);
}
else
{
snprintf(_str, 120, "%30s: NULL", tagStr);
}
#pragma GCC diagnostic pop
}
break;
case TT_BIN:
{
auto val = value<tt_bin_t>();
_str = new char[120];
if (val)
{
snprintf(_str, 120, "%30s: %" UINT16_FMT " bytes", tagStr, val->cb);
}
else
{
snprintf(_str, 120, "%30s: NULL", tagStr);
}
}
break;
case TT_UUID:
{
auto val = value<tt_uuid_t>();
_str = new char[120];
if (val)
{
char* s = val->toString();
snprintf(_str, 120, "%30s: %s", tagStr, s);
free(s);
}
else
{
snprintf(_str, 120, "%30s: NULL", tagStr);
}
}
break;
case TT_STRING:
{
auto val = value<tt_str_t>();
if (val)
{
_str = new char[strlen(val->c_str()) + 40];
memset(_str, 0, strlen(val->c_str()) + 40);
snprintf(_str, strlen(val->c_str()) + 40, "%30s: %s", tagStr, val->c_str());
}
else
{
_str = new char[40];
snprintf(_str, 120, "%30s: NULL", tagStr);
}
}
break;
case TT_FLOAT:
{
auto val = value<tt_float_t>();
_str = new char[120];
if (val)
{
snprintf(_str, 120, "%30s: %f", tagStr, *val);
}
else
{
snprintf(_str, 120, "%30s: NULL", tagStr);
}
}
break;
default: break;
}
free(tagStr);
return _str;
}
#endif
};
inline bool operator==(const Property& lhs, const Property& rhs)
{
bool result = false;
if (TAG_TYPE(lhs.tag_) == TAG_TYPE(rhs.tag_) && TAG_ID(lhs.tag_) == TAG_ID(rhs.tag_) )
{
if ((lhs.tag_ & TAG_ARRAY) == TAG_ARRAY)
{
auto a1 = lhs.value<tt_array_t>();
auto a2 = rhs.value<tt_array_t>();
if (a1 && a2)
{
if (U32(a1->totalSize) == a2->totalSize)
{
result = (0 == memcmp(a1->lpValues, a2->lpValues, a1->totalSize));
}
}
else if (!(a1 || a2))
{
result = true;
}
else
{
// MISRA 6-4-2: switch preferred over else-if for known cases
}
}
switch (TAG_TYPE(lhs.tag_))
{
case TT_FLOAT:
{
auto f1 = lhs.value<tt_float_t>();
auto f2 = rhs.value<tt_float_t>();
if (f1 && f2)
{
float diff = fabsf(*f1 - *f2);
float max_ab = fmaxf(fabsf(*f1), fabsf(*f2));
result = diff <= (FLT_EPSILON * max_ab);
}
else if (!(f1 || f2))
{
result = true;
}
else
{
// MISRA 6-4-2: switch preferred over else-if for known cases
}
}
break;
case TT_UUID:
{
auto u1 = lhs.value<tt_uuid_t>();
auto u2 = rhs.value<tt_uuid_t>();
if (u1 && u2)
{
result = u1.value() == u2.value();
}
else if (!(u1 || u2))
{
result = true;
}
else
{
// MISRA 6-4-2: switch preferred over else-if for known cases
}
}
break;
case TT_STRING:
{
auto s1 = lhs.value<tt_str_t>();
auto s2 = rhs.value<tt_str_t>();
if (s1 && s2)
{
if (s1->length() == s2->length())
{
result = *s1 == *s2;
}
}
else if (!(s1 || s2))
{
result = true;
}
else
{
// MISRA 6-4-2: switch preferred over else-if for known cases
}
}
break;
case TT_BIN:
{
auto bin1 = lhs.value<tt_bin_t>();
auto bin2 = rhs.value<tt_bin_t>();
if (bin1 && bin2 && (U32(bin1->cb) == U32(bin2->cb)))
{
result = (0 == memcmp(bin1->lpb, bin2->lpb, bin1->cb));
}
else if (!(bin1 || bin2))
{
result = true;
}
else
{
// MISRA 6-4-2: switch preferred over else-if for known cases
}
}
break;
case TT_UINT32:
result = lhs.value<tt_uint32_t>() == rhs.value<tt_uint32_t>();
break;
case TT_UINT16:
result = lhs.value<tt_uint16_t>() == rhs.value<tt_uint16_t>();
break;
case TT_UINT8:
result = lhs.value<tt_uint8_t>() == rhs.value<tt_uint8_t>();
break;
case TT_INT32:
result = lhs.value<tt_int32_t>() == rhs.value<tt_int32_t>();
break;
case TT_INT16:
result = lhs.value<tt_int16_t>() == rhs.value<tt_int16_t>();
break;
case TT_INT8:
result = lhs.value<tt_int8_t>() == rhs.value<tt_int8_t>();
break;
case TT_DATETIME:
result = lhs.value<tt_uint32_t>() == rhs.value<tt_uint32_t>();
break;
case TT_BOOL:
result = lhs.value<tt_bool_t>() == rhs.value<tt_bool_t>();
break;
default:
break;
}
}
return result;
}
inline bool operator!=(const Property& lhs, const Property& rhs)
{
return !(lhs == rhs);
}
class PropArray : Serializable, std::vector<Property>
{
Property VAL_ERR_NOT_FOUND;
public:
using vector::clear;
using vector::operator[];
virtual
~PropArray() = default;
PropArray() : VAL_ERR_NOT_FOUND(TAG_ERROR)
{
VAL_ERR_NOT_FOUND.setData(I8(E_NOT_FOUND));
}
PropArray(const PropArray& va)
: PropArray()
{
copyFrom(va);
}
PropArray&
operator=(const PropArray& other)
{
copyFrom(other);
return *this;
}
void
copyFrom(const PropArray& other)
{
if (this != &other)
{
clear();
for (const auto& i : other)
{
push_back(i);
}
}
}
const Property&
getAt(uint32_t index) const
{
return at(index);
}
const Property&
get(uint32_t tag) const
{
const Property* result = &VAL_ERR_NOT_FOUND;
uint32_t idx = 0U;
const std::size_t max = this->count();
while (idx < max)
{
const Property& p = this->getAt(idx);
if (TAG_ID(p.tag()) == TAG_ID(tag))
{
result = &p;
break;
}
++idx;
}
return *result;
}
template <uint32_t PropTag>
PropType<PropTag>
get(PropType<PropTag> default_value = static_cast<PropType<PropTag>>(PropTypeFromTag<
TAG_TYPE(PropTag)>::default_value))
{
PropType<PropTag> result = default_value;
if (auto value = get(PropTag).value<typename PropTypeFromTag<TAG_TYPE(PropTag)>::type>())
{
result = *value;
}
return result;
}
bool
has(uint32_t tag) const
{
bool result = false;
for (const auto& i : *this)
{
if (TAG_ID(i.tag()) == TAG_ID(tag))
{
result = true;
}
}
return result;
}
void
remove(const Property& val)
{
for (auto it = begin(); it != end(); ++it)
{
if (((*it).tag() & 0xffff0800) == (val.tag() & 0xffff0800))
{
(void)erase(it);
break;
}
}
}
void
set(const Property& val)
{
bool found = false;
for (uint32_t i = 0U; i < this->size(); i++)
{
if ((at(i).tag() & 0xffff0800) == (val.tag() & 0xffff0800))
{
at(i).copyFrom(val);
found = true;
break;
}
}
if (!found)
{
push_back(val);
}
}
template <uint32_t PropTag, typename T>
void set(T value)
{
if (std::is_convertible_v<T, typename PropTypeFromTag<TAG_TYPE(PropTag)>::type>)
{
Property p(PropTag);
p.setData(static_cast<T>(value));
set(p);
}
else
{
#if FLAKE_DEBUG_LOGGING
logging::logf<lvlError>("the passed value cannot be safely cast to the property's type\n");
#endif
}
}
size_type
count() const
{
return size();
}
virtual bool serialize(uint16_t* outLen, uint8_t** outBuf) override
{
// compute total size
size_t total = sizeof(uint16_t); // numProps
for (auto& p : *this)
{
total += p.getSerializedSize();
}
uint8_t* buf = (uint8_t*)malloc(total);
uint16_t offset = 0U;
// write number of properties
uint16_t num = U16(this->size());
*outLen = 0U;
*outBuf = nullptr;
if (safeWrite(buf, U16(total), offset, reinterpret_cast<const uint8_t*>(&num),
U16(sizeof(num))))
{
// write each property
for (auto& p : *this)
{
(void)p.serializeTo(buf, offset, U16(total));
}
*outLen = offset;
*outBuf = buf;
}
return true;
}
virtual bool deserialize(uint16_t len, uint8_t* buf) override
{
uint16_t offset = 0U;
uint16_t num = 0U;
bool result = false;
if (safeRead(buf, len, offset, reinterpret_cast<uint8_t*>(&num), U16(sizeof(num))))
{
result = true;
for (unsigned i = 0U; i < num && U32(offset) < len; ++i)
{
Property p;
if (!p.deserializeFrom(buf, offset, len))
{
result = false;
break;
}
this->push_back(p);
}
}
return result;
}
uint16_t getSerializedSize() const
{
size_t total = sizeof(uint16_t);
for (const auto& prop : *this)
{
total += prop.getSerializedSize();
}
return U16(total);
}
#if FLAKE_DEBUG_LOGGING
char*
toString(size_t max_len) const
{
std::string s;
for (uint32_t i = 0; i < size(); i++)
{
char* str = at(i).toString();
if (str != nullptr)
{
if (s.length() > (max_len - 37U))
{
char tmp[20]{};
(void)snprintf(tmp, 20, "<<%4ld more>>\n", (size() - i));
s.append(" ");
s.append(tmp);
delete[] str;
break;
}
s.append(str);
if (i < size() - 1)
s.append("\n");
delete[] str;
}
}
char* res = new char[s.length() + 1];
memset(res, 0, s.length() + 1);
if (!s.empty())
memcpy(res, s.c_str(), s.length());
return res;
}
#endif
};
namespace MessageType
{
// Requests
constexpr uint8_t connect = U8(0x01U); // no indi
constexpr uint8_t disconnect = U8(0x02U); // no indi
constexpr uint8_t createObject = U8(0x03U); // Parent
constexpr uint8_t queryObjects = U8(0x04U); // no indi // Parent // CONF: PL
constexpr uint8_t destroyObject = U8(0x05U); // Obj Addr
constexpr uint8_t openProperty = U8(0x06U); // Obj Addr // REQ: PL
constexpr uint8_t setProperties = U8(0x07U); // Obj Addr // REQ/CONF: PL
constexpr uint8_t getProperties = U8(0x08U); // Obj Addr // REQ/CONF: PL
constexpr uint8_t joinGroup = U8(0x09U); // Group Addr (or Obj Addr)
constexpr uint8_t leaveGroup = U8(0x0AU); // Group Addr (or Obj Addr)
constexpr uint8_t custom = U8(0x0BU); // Src -> Dst // REQ/CONF: PL
constexpr uint8_t streamSeek = U8(0x0CU);
constexpr uint8_t streamRead = U8(0x0DU);
constexpr uint8_t streamWrite = U8(0x0EU);
// Indications
// 0x11
// 0x12
constexpr uint8_t objectCreated = U8(0x13U); // [parent -> group] // INDI: PL
// 0x14
constexpr uint8_t objectDestroyed = U8(0x15U); // [object -> group]
constexpr uint8_t openPropertyReq = U8(0x16U); // [object -> group]
constexpr uint8_t setPropertiesReq = U8(0x17U); // Obj Addr // INDI/RESP: PL
constexpr uint8_t getPropertiesReq = U8(0x18U); // Obj Addr // INDI/RESP: PL
constexpr uint8_t joined = U8(0x19U); // [object -> group]
constexpr uint8_t left = U8(0x1AU); // [object -> group]
constexpr uint8_t customMsgReceived = U8(0x1BU); // Src & Dst needed // INDI/RESP: PL
constexpr uint8_t streamSeekReq = U8(0x1CU); // Read or Write at Seek Pos
constexpr uint8_t streamReadReq = U8(0x1DU); // Read or Write at Seek Pos
constexpr uint8_t streamWriteReq = U8(0x1EU); // Read or Write at Seek Pos
constexpr uint8_t changed = U8(0x1FU); // [object -> group] // INDI: PL
constexpr uint8_t configure = U8(0x23U); // enable disable stuff, e.g. format of setProperties Confirmation
// Control
constexpr uint8_t ping = U8(0x30U); // 0x20 when client->srv or client->obj
constexpr uint8_t auth = U8(0x31U);
const char* toString(uint8_t mt);
}
inline
uint32_t preprocessTag(uint32_t t)
{
uint32_t result = 0U;
if (t > 0xFFFFU)
{
result = (TAG_ID(t) << 16 | (t & 0xf000U));
}
else
{
result = (t << 16) | (t & 0xf000U);
}
return result;
}
inline
Property PropMakeBool(uint32_t t, tt_bool_t v)
{
Property val(preprocessTag(t) | TT_BOOL);
val.setData(v);
return val;
}
inline
Property PropMakeU8(uint32_t t, tt_uint8_t v)
{
Property val(preprocessTag(t) | TT_UINT8);
val.setData(v);
return val;
}
inline
Property PropMakeU16(uint32_t t, tt_uint16_t v)
{
Property val(preprocessTag(t) | TT_UINT16);
val.setData(v);
return val;
}
inline
Property PropMakeU32(uint32_t t, tt_uint32_t v)
{
Property val(preprocessTag(t) | TT_UINT32);
val.setData(v);
return val;
}
inline
Property PropMakeI8(uint32_t t, tt_int8_t v)
{
Property val(preprocessTag(t) | TT_INT8);
val.setData(v);
return val;
}
inline
Property PropMakeI16(uint32_t t, tt_int16_t v)
{
Property val(preprocessTag(t) | TT_INT16);
val.setData(v);
return val;
}
inline
Property PropMakeI32(uint32_t t, tt_int32_t v)
{
Property val(preprocessTag(t) | TT_INT32);
val.setData(v);
return val;
}
inline
Property PropMakeDateTime(uint32_t t, tt_uint32_t v)
{
Property val(preprocessTag(t) | TT_DATETIME);
val.setData(v);
return val;
}
inline
Property PropMakeFloat(uint32_t t, tt_float_t v)
{
Property val(preprocessTag(t) | TT_FLOAT);
val.setData(v);
return val;
}
inline
Property PropMakeUUID(uint32_t t, tt_uuid_t v)
{
Property val(preprocessTag(t) | TT_UUID);
val.setData(v);
return val;
}
inline
Property PropMakeString(uint32_t t, tt_str_t v)
{
Property val(preprocessTag(t) | TT_STRING);
val.setData(v);
return val;
}
inline
Property PropMakeBin(uint32_t t, tt_bin_t v)
{
Property val(preprocessTag(t) | TT_BIN);
val.setData(v);
return val;
}
}
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic pop
#endif
#endif /* FLAKE_TYPES_H_ */