Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit db0c0ff

Browse files
committed
add support for dynamic functions
1 parent 1635f17 commit db0c0ff

File tree

18 files changed

+3053
-1129
lines changed

18 files changed

+3053
-1129
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -849,6 +849,7 @@ set(LIBNETDATA_FILES
849849
src/libnetdata/eval/eval-parser-legacy.c
850850
src/libnetdata/eval/eval-assignment.c
851851
src/libnetdata/eval/eval-evaluate.c
852+
src/libnetdata/eval/eval-functions.c
852853
src/libnetdata/eval/eval-utils.c
853854
src/libnetdata/eval/eval.h
854855
src/libnetdata/eval/eval-internal.h

src/daemon/main.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,6 @@ static void fatal_status_file_save(void) {
247247

248248
int netdata_main(int argc, char **argv) {
249249
libjudy_malloc_init();
250-
string_init();
251250
analytics_init();
252251

253252
netdata_start_time = now_realtime_sec();

src/libnetdata/eval/eval-evaluate.c

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ static NETDATA_DOUBLE eval_variable(EVAL_EXPRESSION *exp, EVAL_VARIABLE *v, int
3030
}
3131

3232
ALWAYS_INLINE
33-
static NETDATA_DOUBLE eval_value(EVAL_EXPRESSION *exp, EVAL_VALUE *v, int *error) {
33+
NETDATA_DOUBLE eval_value(EVAL_EXPRESSION *exp, EVAL_VALUE *v, int *error) {
3434
NETDATA_DOUBLE n;
3535

3636
switch(v->type) {
@@ -343,6 +343,7 @@ static NETDATA_DOUBLE eval_sign_minus(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *
343343
return -n1;
344344
}
345345

346+
// this is used by the legacy parser - it is not used by re2c/lemon parser
346347
ALWAYS_INLINE
347348
static NETDATA_DOUBLE eval_abs(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
348349
NETDATA_DOUBLE n1 = eval_value(exp, &op->ops[0], error);
@@ -416,7 +417,7 @@ static NETDATA_DOUBLE eval_semicolon(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *e
416417
}
417418

418419
// Define operators table - use the struct definition from eval-internal.h
419-
struct operator operators[256] = {
420+
struct operator operators[EVAL_OPERATOR_CUSTOM_FUNCTION_END + 1] = {
420421
// this is a random access array
421422
// we always access it with a known EVAL_OPERATOR_X
422423

@@ -435,10 +436,21 @@ struct operator operators[256] = {
435436
[EVAL_OPERATOR_NOT] = { "!", 6, 1, 0, eval_not },
436437
[EVAL_OPERATOR_SIGN_PLUS] = { "+", 6, 1, 0, eval_sign_plus },
437438
[EVAL_OPERATOR_SIGN_MINUS] = { "-", 6, 1, 0, eval_sign_minus },
439+
440+
// this is only used by the legacy parser - not used by re2c/lemon parser
438441
[EVAL_OPERATOR_ABS] = { "abs(",6,1, 1, eval_abs },
442+
439443
[EVAL_OPERATOR_IF_THEN_ELSE] = { "?", 7, 3, 0, eval_if_then_else },
440-
[EVAL_OPERATOR_ASSIGNMENT] = { "=", 1, 2, 0, eval_assignment }, // Lower precedence than arithmetic
441-
[EVAL_OPERATOR_SEMICOLON] = { ";", 0, 2, 0, eval_semicolon }, // Lowest precedence
444+
445+
// Lower precedence than arithmetic
446+
[EVAL_OPERATOR_ASSIGNMENT] = { "=", 1, 2, 0, eval_assignment },
447+
448+
// Lowest precedence
449+
[EVAL_OPERATOR_SEMICOLON] = { ";", 0, 2, 0, eval_semicolon },
450+
451+
// Dynamic functions
452+
[EVAL_OPERATOR_FUNCTION] = { NULL, 6, 0, 1, eval_execute_function },
453+
442454
[EVAL_OPERATOR_NOP] = { NULL, 9, 1, 0, eval_nop },
443455
[EVAL_OPERATOR_EXPRESSION_OPEN] = { NULL, 9, 1, 0, eval_nop },
444456

@@ -448,8 +460,12 @@ struct operator operators[256] = {
448460

449461
// Helper function to get precedence
450462
ALWAYS_INLINE
451-
int eval_precedence(unsigned char operator) {
452-
return operators[(unsigned char)(operator)].precedence;
463+
char eval_precedence(EVAL_OPERATOR operator) {
464+
return operators[operator].precedence;
465+
}
466+
467+
bool has_the_right_number_of_operands(EVAL_NODE *op) {
468+
return op->operator >= EVAL_OPERATOR_CUSTOM_FUNCTION_START || operators[op->operator].parameters == op->count;
453469
}
454470

455471
ALWAYS_INLINE
@@ -459,7 +475,12 @@ NETDATA_DOUBLE eval_node(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
459475
return NAN;
460476
}
461477

462-
if(unlikely(op->count != operators[op->operator].parameters)) {
478+
if(op->operator >= EVAL_OPERATOR_CUSTOM_FUNCTION_END) {
479+
*error = EVAL_ERROR_INVALID_OPERATOR;
480+
return NAN;
481+
}
482+
483+
if(unlikely(!has_the_right_number_of_operands(op))) {
463484
*error = EVAL_ERROR_INVALID_NUMBER_OF_OPERANDS;
464485
return NAN;
465486
}
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
// SPDX-License-Identifier: GPL-3.0-or-later
2+
3+
#include "../libnetdata.h"
4+
#include "eval-internal.h"
5+
6+
// Global registry for dynamic functions
7+
EVAL_DYNAMIC_FUNCTION *eval_function_registry = NULL;
8+
9+
// Next available operator ID for dynamic functions (starting after hardcoded operators)
10+
EVAL_OPERATOR next_function_op = EVAL_OPERATOR_CUSTOM_FUNCTION_START; // Start from a safe value
11+
12+
void str2lower(char *dst, const char *src) {
13+
while (*src) {
14+
*dst = (char)tolower((uint8_t)*src);
15+
dst++;
16+
src++;
17+
}
18+
*dst = '\0';
19+
}
20+
21+
// Register a new function in the registry
22+
bool eval_register_function(const char *name, eval_function_cb callback, int min_params, int max_params) {
23+
if (!name || !callback || min_params < 0)
24+
return 0;
25+
26+
// make it lowercase
27+
char n[strlen(name) + 1];
28+
str2lower(n, name);
29+
name = n;
30+
31+
// Check if a function with this name already exists
32+
EVAL_DYNAMIC_FUNCTION *func = eval_function_lookup(name);
33+
if (!func) {
34+
func = callocz(1, sizeof(EVAL_DYNAMIC_FUNCTION));
35+
func->name = string_strdupz(name);
36+
37+
// Assign a unique operator ID
38+
func->operator = __atomic_fetch_add(&next_function_op, 1, __ATOMIC_RELAXED);
39+
40+
// Add to registry
41+
func->next = eval_function_registry;
42+
eval_function_registry = func;
43+
}
44+
45+
// Create new function registry entry
46+
func->callback = callback;
47+
func->min_params = min_params;
48+
func->max_params = max_params;
49+
50+
// Register the function operator in the operators' table
51+
operators[func->operator].print_as = string2str(func->name);
52+
operators[func->operator].precedence = eval_precedence(EVAL_OPERATOR_FUNCTION);
53+
operators[func->operator].parameters = 0; // Will be set dynamically when the function is called
54+
operators[func->operator].isfunction = 1;
55+
operators[func->operator].eval = eval_execute_function;
56+
57+
return 1;
58+
}
59+
60+
// Lookup a function by name (case-insensitive)
61+
EVAL_DYNAMIC_FUNCTION *eval_function_lookup(const char *name) {
62+
if (!name)
63+
return NULL;
64+
65+
// make it lowercase
66+
char n[strlen(name) + 1];
67+
str2lower(n, name);
68+
name = n;
69+
70+
// Create a temporary STRING for case-insensitive comparison
71+
STRING *lookup_name = string_strdupz(name);
72+
73+
EVAL_DYNAMIC_FUNCTION *func = eval_function_registry;
74+
while (func) {
75+
// Compare names case-insensitively
76+
if (func->name == lookup_name) {
77+
string_freez(lookup_name);
78+
return func;
79+
}
80+
func = func->next;
81+
}
82+
83+
string_freez(lookup_name);
84+
return NULL;
85+
}
86+
87+
// Function to evaluate a dynamic function
88+
NETDATA_DOUBLE eval_execute_function(struct eval_expression *exp, EVAL_NODE *op, int *error) {
89+
if (!op) {
90+
*error = EVAL_ERROR_INVALID_NUMBER_OF_OPERANDS;
91+
return NAN;
92+
}
93+
94+
// Find the function in the registry
95+
EVAL_DYNAMIC_FUNCTION *func = NULL;
96+
for (func = eval_function_registry; func; func = func->next) {
97+
if (func->operator == op->operator)
98+
break;
99+
}
100+
101+
if (!func) {
102+
buffer_sprintf(exp->error_msg, "unknown function with operator %d", (int)op->operator);
103+
*error = EVAL_ERROR_UNKNOWN_OPERAND;
104+
return NAN;
105+
}
106+
107+
// Check parameter count
108+
if (op->count < func->min_params) {
109+
buffer_sprintf(exp->error_msg, "function %s requires at least %d parameters, but %d provided",
110+
string2str(func->name), func->min_params, op->count);
111+
*error = EVAL_ERROR_INVALID_NUMBER_OF_OPERANDS;
112+
return NAN;
113+
}
114+
115+
if (func->max_params >= 0 && op->count > func->max_params) {
116+
buffer_sprintf(exp->error_msg, "function %s accepts at most %d parameters, but %d provided",
117+
string2str(func->name), func->max_params, op->count);
118+
*error = EVAL_ERROR_INVALID_NUMBER_OF_OPERANDS;
119+
return NAN;
120+
}
121+
122+
// Call the function
123+
return func->callback(exp, op->count, op->ops, error);
124+
}
125+
126+
// Define abs function using the dynamic system
127+
static NETDATA_DOUBLE abs_function(struct eval_expression *exp, int param_count, EVAL_VALUE *params, int *error) {
128+
if (param_count != 1) {
129+
*error = EVAL_ERROR_INVALID_NUMBER_OF_OPERANDS;
130+
return NAN;
131+
}
132+
133+
// Evaluate the parameter
134+
NETDATA_DOUBLE n = eval_value(exp, &params[0], error);
135+
if (*error != EVAL_ERROR_OK)
136+
return NAN;
137+
138+
return ABS(n);
139+
}
140+
141+
// Function to initialize the eval subsystem
142+
// This is defined as a constructor so it runs automatically when the library is loaded
143+
__attribute__((constructor))
144+
static void eval_functions_constructor(void) {
145+
// Initialize the function registry
146+
eval_register_function("abs", abs_function, 1, 1);
147+
}

0 commit comments

Comments
 (0)