|
| 1 | +/** |
| 2 | + * @file |
| 3 | + * @author Pavel Siska <[email protected]> |
| 4 | + * @brief Provides the FieldGroup class for registering scalar, vector, directional, and biflow |
| 5 | + * fields. |
| 6 | + * |
| 7 | + * FieldGroup is responsible for managing field metadata within a specific group. |
| 8 | + * It allows adding scalar and vector fields, as well as pairs of fields that |
| 9 | + * represent either directional or biflow traffic. The class interfaces with |
| 10 | + * FieldManager to register fields and maintain consistency in FlowRecords. |
| 11 | + * |
| 12 | + * The template methods enable flexible accessor functions for fields, supporting: |
| 13 | + * - Scalar values |
| 14 | + * - Vector values (std::span) |
| 15 | + * - Directional field pairs (forward/reverse) |
| 16 | + * - Biflow field pairs (A/B) |
| 17 | + * |
| 18 | + * @note FieldGroup instances are constructed by FieldManager and are tied to |
| 19 | + * a specific field group. |
| 20 | + * |
| 21 | + * @copyright Copyright (c) 2025 CESNET, z.s.p.o. |
| 22 | + */ |
| 23 | + |
| 24 | +#pragma once |
| 25 | + |
| 26 | +#include "fieldGroupTypeTraits.hpp" |
| 27 | +#include "fieldHandler.hpp" |
| 28 | +#include "fieldManager.hpp" |
| 29 | + |
| 30 | +#include <cstdint> |
| 31 | +#include <string_view> |
| 32 | +#include <type_traits> |
| 33 | +#include <utility> |
| 34 | + |
| 35 | +namespace ipxp { |
| 36 | + |
| 37 | +/** |
| 38 | + * @class FieldGroup |
| 39 | + * @brief Manages registration of scalar, vector, directional, and biflow fields. |
| 40 | + * |
| 41 | + * FieldGroup acts as a bridge between user-provided accessor functions and the FieldManager, |
| 42 | + * ensuring type consistency and correct registration of fields. |
| 43 | + * |
| 44 | + * It supports the following types of fields: |
| 45 | + * - Scalar values |
| 46 | + * - Vector values (`std::span`) |
| 47 | + * - Directional field pairs (forward/reverse) |
| 48 | + * - Biflow field pairs (A/B) |
| 49 | + * |
| 50 | + * ### Directional Pairs |
| 51 | + * |
| 52 | + * DirectionalPair is used for fields that have a forward and a reverse component. |
| 53 | + * Typical example: `packets` (forward) and `packets_rev` (reverse). |
| 54 | + * |
| 55 | + * Behavior depends on flow orientation: |
| 56 | + * |
| 57 | + * | Flow Type | Forward Field | Reverse Field | |
| 58 | + * |------------------|---------------------|---------------------| |
| 59 | + * | Forward Uniflow | exported | ignored | |
| 60 | + * | Reverse Uniflow | ignored | exported as forward | |
| 61 | + * | Biflow | exported | exported | |
| 62 | + * | Reverse Biflow | exported as reverse | exported as forward | |
| 63 | + * |
| 64 | + * This ensures consistent handling of directional metrics across different flow representations. |
| 65 | + * |
| 66 | + * ### Biflow Pairs |
| 67 | + * |
| 68 | + * BiflowPair is used for fields that conceptually belong to two sides of a bidirectional flow. |
| 69 | + * Typical example: `src_port` (A) and `dst_port` (B). |
| 70 | + * Behavior depends on flow orientation: |
| 71 | + * |
| 72 | + * | Flow Type | A Field | B Field | |
| 73 | + * |------------------|-----------------|-----------------| |
| 74 | + * | Forward Uniflow | exported | exported | |
| 75 | + * | Reverse Uniflow | exported as B | exported as A | |
| 76 | + * | Biflow | exported | exported | |
| 77 | + * | Reverse Biflow | exported as B | exported as A | |
| 78 | + * |
| 79 | + * |
| 80 | + * ## Example: Adding a scalar field |
| 81 | + * @code |
| 82 | + * FieldGroup group = fieldManager.createFieldGroup("my_group"); |
| 83 | + * |
| 84 | + * group.addScalarField("packet_count", [](const void* rec) { |
| 85 | + * return reinterpret_cast<const MyRecord*>(rec)->pktCount; |
| 86 | + * }); |
| 87 | + * @endcode |
| 88 | + * |
| 89 | + * ## Example: Adding a vector field |
| 90 | + * @code |
| 91 | + * group.addVectorField("payload", [](const void* rec) { |
| 92 | + * auto r = reinterpret_cast<const MyRecord*>(rec); |
| 93 | + * return std::span<const uint8_t>(r->payload, r->payloadLength); |
| 94 | + * }); |
| 95 | + * @endcode |
| 96 | + * |
| 97 | + * ## Example: Adding a directional pair of scalar fields |
| 98 | + * @code |
| 99 | + * group.addScalarDirectionalFields( |
| 100 | + * "fwd_packets", "rev_packets", |
| 101 | + * [](const void* rec) { return reinterpret_cast<const MyRecord*>(rec)->fwdCount; }, |
| 102 | + * [](const void* rec) { return reinterpret_cast<const MyRecord*>(rec)->revCount; } |
| 103 | + * ); |
| 104 | + * @endcode |
| 105 | + */ |
| 106 | +class FieldGroup { |
| 107 | +public: |
| 108 | + /** |
| 109 | + * @brief Registers a scalar field. |
| 110 | + * |
| 111 | + * @tparam AccessorFunction Callable returning the field value. |
| 112 | + * @param fieldName Name of the field. |
| 113 | + * @param accessorFunction Function that retrieves the field value. |
| 114 | + * @return FieldHandler Handle to the registered field. |
| 115 | + */ |
| 116 | + template<typename AccessorFunction> |
| 117 | + [[nodiscard]] FieldHandler |
| 118 | + addScalarField(std::string_view fieldName, AccessorFunction&& accessorFunction) |
| 119 | + { |
| 120 | + return addFieldGeneric<ScalarAccessor>( |
| 121 | + fieldName, |
| 122 | + std::forward<AccessorFunction>(accessorFunction)); |
| 123 | + } |
| 124 | + |
| 125 | + /** |
| 126 | + * @brief Registers a vector field. |
| 127 | + * |
| 128 | + * @tparam AccessorFunction Callable returning a `std::span` of values. |
| 129 | + * @param fieldName Name of the field. |
| 130 | + * @param accessorFunction Function that retrieves the field values. |
| 131 | + * @return FieldHandler Handle to the registered field. |
| 132 | + */ |
| 133 | + template<typename AccessorFunction> |
| 134 | + [[nodiscard]] FieldHandler |
| 135 | + addVectorField(std::string_view fieldName, AccessorFunction&& accessorFunction) |
| 136 | + { |
| 137 | + return addFieldGeneric<VectorAccessor>( |
| 138 | + fieldName, |
| 139 | + std::forward<AccessorFunction>(accessorFunction)); |
| 140 | + } |
| 141 | + |
| 142 | + /** |
| 143 | + * @brief Registers a pair of scalar fields representing directional traffic. |
| 144 | + * |
| 145 | + * @note This registers a **DirectionalPair**, see class documentation for behavior. |
| 146 | + * |
| 147 | + * @tparam ForwardAccessorFunction Accessor for the forward direction. |
| 148 | + * @tparam ReverseAccessorFunction Accessor for the reverse direction. |
| 149 | + * @param forwardFieldName Name of the forward field. |
| 150 | + * @param reverseFieldName Name of the reverse field. |
| 151 | + * @param forwardAccessorFunction Getter for the forward value. |
| 152 | + * @param reverseAccessorFunction Getter for the reverse value. |
| 153 | + * @return Pair of FieldHandler objects for forward and reverse fields. |
| 154 | + */ |
| 155 | + template<typename ForwardAccessorFunction, typename ReverseAccessorFunction> |
| 156 | + [[nodiscard]] std::pair<FieldHandler, FieldHandler> addScalarDirectionalFields( |
| 157 | + std::string_view forwardFieldName, |
| 158 | + std::string_view reverseFieldName, |
| 159 | + ForwardAccessorFunction&& forwardAccessorFunction, |
| 160 | + ReverseAccessorFunction&& reverseAccessorFunction) |
| 161 | + { |
| 162 | + return addPairFieldsGeneric<ScalarAccessor>( |
| 163 | + forwardFieldName, |
| 164 | + reverseFieldName, |
| 165 | + std::forward<ForwardAccessorFunction>(forwardAccessorFunction), |
| 166 | + std::forward<ReverseAccessorFunction>(reverseAccessorFunction), |
| 167 | + PairType::Directional); |
| 168 | + } |
| 169 | + |
| 170 | + /** |
| 171 | + * @brief Registers a pair of vector fields representing directional traffic. |
| 172 | + * |
| 173 | + * @note This registers a **DirectionalPair**, see class documentation for behavior. |
| 174 | + * |
| 175 | + * @tparam ForwardAccessorFunction Accessor for the forward direction. |
| 176 | + * @tparam ReverseAccessorFunction Accessor for the reverse direction. |
| 177 | + * @param forwardFieldName Name of the forward field. |
| 178 | + * @param reverseFieldName Name of the reverse field. |
| 179 | + * @param forwardAccessorFunction Getter for the forward value. |
| 180 | + * @param reverseAccessorFunction Getter for the reverse value. |
| 181 | + * @return Pair of FieldHandler objects for forward and reverse fields. |
| 182 | + */ |
| 183 | + template<typename ForwardAccessorFunction, typename ReverseAccessorFunction> |
| 184 | + [[nodiscard]] std::pair<FieldHandler, FieldHandler> addVectorDirectionalFields( |
| 185 | + std::string_view forwardFieldName, |
| 186 | + std::string_view reverseFieldName, |
| 187 | + ForwardAccessorFunction&& forwardAccessorFunction, |
| 188 | + ReverseAccessorFunction&& reverseAccessorFunction) |
| 189 | + { |
| 190 | + return addPairFieldsGeneric<VectorAccessor>( |
| 191 | + forwardFieldName, |
| 192 | + reverseFieldName, |
| 193 | + std::forward<ForwardAccessorFunction>(forwardAccessorFunction), |
| 194 | + std::forward<ReverseAccessorFunction>(reverseAccessorFunction), |
| 195 | + PairType::Directional); |
| 196 | + } |
| 197 | + |
| 198 | + /** |
| 199 | + * @brief Registers a pair of scalar fields representing biflow traffic. |
| 200 | + * |
| 201 | + * @note This registers a **BiflowPair**, see class documentation for behavior. |
| 202 | + * |
| 203 | + * @param aFieldName Name of the "A" field. |
| 204 | + * @param bFieldName Name of the "B" field. |
| 205 | + * @param aGetter Getter for the "A" value. |
| 206 | + * @param bGetter Getter for the "B" value. |
| 207 | + */ |
| 208 | + template<typename ForwardAccessorFunction, typename ReverseAccessorFunction> |
| 209 | + [[nodiscard]] std::pair<FieldHandler, FieldHandler> addScalarBiflowFields( |
| 210 | + std::string_view aFieldName, |
| 211 | + std::string_view bFieldName, |
| 212 | + ForwardAccessorFunction&& aGetter, |
| 213 | + ReverseAccessorFunction&& bGetter) |
| 214 | + { |
| 215 | + return addPairFieldsGeneric<ScalarAccessor>( |
| 216 | + aFieldName, |
| 217 | + bFieldName, |
| 218 | + std::forward<ForwardAccessorFunction>(aGetter), |
| 219 | + std::forward<ReverseAccessorFunction>(bGetter), |
| 220 | + PairType::Biflow); |
| 221 | + } |
| 222 | + |
| 223 | + /** |
| 224 | + * @brief Registers a pair of vector fields representing biflow traffic. |
| 225 | + * |
| 226 | + * @note This registers a **BiflowPair**, see class documentation for behavior. |
| 227 | + * |
| 228 | + * @param aFieldName Name of the "A" field. |
| 229 | + * @param bFieldName Name of the "B" field. |
| 230 | + * @param aGetter Getter for the "A" value. |
| 231 | + * @param bGetter Getter for the "B" value. |
| 232 | + */ |
| 233 | + template<typename ForwardAccessorFunction, typename ReverseAccessorFunction> |
| 234 | + [[nodiscard]] std::pair<FieldHandler, FieldHandler> addVectorBiflowFields( |
| 235 | + std::string_view aFieldName, |
| 236 | + std::string_view bFieldName, |
| 237 | + ForwardAccessorFunction&& aGetter, |
| 238 | + ReverseAccessorFunction&& bGetter) |
| 239 | + { |
| 240 | + return addPairFieldsGeneric<VectorAccessor>( |
| 241 | + aFieldName, |
| 242 | + bFieldName, |
| 243 | + std::forward<ForwardAccessorFunction>(aGetter), |
| 244 | + std::forward<ReverseAccessorFunction>(bGetter), |
| 245 | + PairType::Biflow); |
| 246 | + } |
| 247 | + |
| 248 | +private: |
| 249 | + template<typename AccessorFunction> |
| 250 | + using FieldType = std::invoke_result_t<AccessorFunction, const void*>; |
| 251 | + |
| 252 | + template<typename AccessorFunction> |
| 253 | + using ElementType = typename span_element_type<FieldType<AccessorFunction>>::type; |
| 254 | + |
| 255 | + enum class PairType : uint8_t { |
| 256 | + Directional, |
| 257 | + Biflow, |
| 258 | + }; |
| 259 | + |
| 260 | + // Can be constructed only by FieldManager |
| 261 | + friend class FieldManager; |
| 262 | + |
| 263 | + FieldGroup(std::string_view groupName, FieldManager& manager) |
| 264 | + : m_groupName(std::string(groupName)) |
| 265 | + , m_fieldManager(manager) |
| 266 | + { |
| 267 | + } |
| 268 | + |
| 269 | + template<template<typename> class Accessor, typename AccessorFunction> |
| 270 | + [[nodiscard]] FieldHandler |
| 271 | + addFieldGeneric(std::string_view fieldName, AccessorFunction&& accessorFunction) |
| 272 | + { |
| 273 | + using T = FieldType<AccessorFunction>; |
| 274 | + return m_fieldManager.registerField( |
| 275 | + m_groupName, |
| 276 | + fieldName, |
| 277 | + Accessor<T> {std::forward<AccessorFunction>(accessorFunction)}); |
| 278 | + } |
| 279 | + |
| 280 | + // T |
| 281 | + template< |
| 282 | + template<typename> class Accessor, |
| 283 | + typename ForwardAccessorFunction, |
| 284 | + typename ReverseAccessorFunction> |
| 285 | + [[nodiscard]] std::pair<FieldHandler, FieldHandler> addPairFieldsGeneric( |
| 286 | + std::string_view fieldNameA, |
| 287 | + std::string_view fieldNameB, |
| 288 | + ForwardAccessorFunction&& forwardAccessorFunction, |
| 289 | + ReverseAccessorFunction&& reverseAccessorFunctionB, |
| 290 | + PairType pairType) |
| 291 | + { |
| 292 | + using TaRaw = FieldType<ForwardAccessorFunction>; |
| 293 | + using TbRaw = FieldType<ReverseAccessorFunction>; |
| 294 | + |
| 295 | + static_assert( |
| 296 | + std::is_same_v<TaRaw, TbRaw>, |
| 297 | + "Accessor functions for pair fields must return the same type"); |
| 298 | + |
| 299 | + using Ta = std::conditional_t< |
| 300 | + is_scalar_accessor<Accessor, FieldType<ForwardAccessorFunction>>::value, |
| 301 | + TaRaw, |
| 302 | + ElementType<ForwardAccessorFunction>>; |
| 303 | + |
| 304 | + using Tb = std::conditional_t< |
| 305 | + is_scalar_accessor<Accessor, FieldType<ReverseAccessorFunction>>::value, |
| 306 | + TbRaw, |
| 307 | + ElementType<ReverseAccessorFunction>>; |
| 308 | + |
| 309 | + const Accessor<Ta> accessorA { |
| 310 | + std::forward<ForwardAccessorFunction>(forwardAccessorFunction)}; |
| 311 | + const Accessor<Tb> accessorB { |
| 312 | + std::forward<ReverseAccessorFunction>(reverseAccessorFunctionB)}; |
| 313 | + |
| 314 | + switch (pairType) { |
| 315 | + case PairType::Biflow: |
| 316 | + return m_fieldManager.registerBiflowPairFields( |
| 317 | + m_groupName, |
| 318 | + fieldNameA, |
| 319 | + fieldNameB, |
| 320 | + std::move(accessorA), |
| 321 | + std::move(accessorB)); |
| 322 | + case PairType::Directional: |
| 323 | + return m_fieldManager.registerDirectionalPairFields( |
| 324 | + m_groupName, |
| 325 | + fieldNameA, |
| 326 | + fieldNameB, |
| 327 | + std::move(accessorA), |
| 328 | + std::move(accessorB)); |
| 329 | + } |
| 330 | + |
| 331 | + __builtin_unreachable(); |
| 332 | + } |
| 333 | + |
| 334 | + std::string m_groupName; |
| 335 | + FieldManager& m_fieldManager; |
| 336 | +}; |
| 337 | + |
| 338 | +} // namespace ipxp |
0 commit comments