master
c 391 lines 11.4 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "../libnetdata.h"
4 #include "eval-internal.h"
5
6 // ----------------------------------------------------------------------------
7 // memory management
8
9 EVAL_NODE *eval_node_alloc(int count) {
10 static int id = 1;
11
12 EVAL_NODE *op = callocz(1, sizeof(EVAL_NODE) + (sizeof(EVAL_VALUE) * count));
13
14 op->id = id++;
15 op->operator = EVAL_OPERATOR_NOP;
16 op->precedence = 0; // Will be set based on the operator
17 op->count = count;
18 return op;
19 }
20
21 void eval_node_set_value_to_node(EVAL_NODE *op, int pos, EVAL_NODE *value) {
22 if(pos >= op->count)
23 fatal("Invalid request to set position %d of OPERAND that has only %d values", pos + 1, op->count + 1);
24
25 op->ops[pos].type = EVAL_VALUE_EXPRESSION;
26 op->ops[pos].expression = value;
27 }
28
29 void eval_node_set_value_to_constant(EVAL_NODE *op, int pos, NETDATA_DOUBLE value) {
30 if(pos >= op->count)
31 fatal("Invalid request to set position %d of OPERAND that has only %d values", pos + 1, op->count + 1);
32
33 op->ops[pos].type = EVAL_VALUE_NUMBER;
34 op->ops[pos].number = value;
35 }
36
37 void eval_node_set_value_to_variable(EVAL_NODE *op, int pos, const char *variable) {
38 if(pos >= op->count)
39 fatal("Invalid request to set position %d of OPERAND that has only %d values", pos + 1, op->count + 1);
40
41 op->ops[pos].type = EVAL_VALUE_VARIABLE;
42 op->ops[pos].variable = callocz(1, sizeof(EVAL_VARIABLE));
43 op->ops[pos].variable->name = string_strdupz(variable);
44 }
45
46 void eval_variable_free(EVAL_VARIABLE *v) {
47 string_freez(v->name);
48 freez(v);
49 }
50
51 void eval_value_free(EVAL_VALUE *v) {
52 switch(v->type) {
53 case EVAL_VALUE_EXPRESSION:
54 eval_node_free(v->expression);
55 break;
56
57 case EVAL_VALUE_VARIABLE:
58 eval_variable_free(v->variable);
59 break;
60
61 default:
62 break;
63 }
64 }
65
66 void eval_node_free(EVAL_NODE *op) {
67 if(!op) return;
68
69 if(op->count) {
70 int i;
71 for(i = op->count - 1; i >= 0 ;i--)
72 eval_value_free(&op->ops[i]);
73 }
74
75 freez(op);
76 }
77
78 // ----------------------------------------------------------------------------
79 // parsed-as generation
80
81 void print_parsed_as_variable(BUFFER *out, EVAL_VARIABLE *v, int *error __maybe_unused) {
82 buffer_sprintf(out, "${%s}", string2str(v->name));
83 }
84
85 void print_parsed_as_constant(BUFFER *out, NETDATA_DOUBLE n) {
86 if(unlikely(isnan(n))) {
87 buffer_strcat(out, "nan");
88 return;
89 }
90
91 if(unlikely(isinf(n))) {
92 buffer_strcat(out, "inf");
93 return;
94 }
95
96 char b[DOUBLE_MAX_LENGTH];
97 print_netdata_double(b, n);
98 buffer_strcat(out, b);
99 }
100
101 void print_parsed_as_value(BUFFER *out, EVAL_VALUE *v, int *error) {
102 switch(v->type) {
103 case EVAL_VALUE_EXPRESSION:
104 print_parsed_as_node(out, v->expression, error);
105 break;
106
107 case EVAL_VALUE_NUMBER:
108 print_parsed_as_constant(out, v->number);
109 break;
110
111 case EVAL_VALUE_VARIABLE:
112 print_parsed_as_variable(out, v->variable, error);
113 break;
114
115 default:
116 *error = EVAL_ERROR_INVALID_VALUE;
117 break;
118 }
119 }
120
121 void print_parsed_as_node(BUFFER *out, EVAL_NODE *op, int *error) {
122 extern struct operator operators[];
123
124 if(unlikely(!op)) {
125 buffer_strcat(out, "NULL");
126 *error = EVAL_ERROR_INVALID_VALUE;
127 return;
128 }
129
130 if(unlikely(op->count != operators[op->operator].parameters)) {
131 buffer_sprintf(out, "INVALID PARAMETERS (operator requires %d, but node has %d)", operators[op->operator].parameters, op->count);
132 *error = EVAL_ERROR_INVALID_NUMBER_OF_OPERANDS;
133 return;
134 }
135
136 if(operators[op->operator].isfunction) {
137 buffer_strcat(out, operators[op->operator].print_as);
138 buffer_strcat(out, "(");
139 print_parsed_as_value(out, &op->ops[0], error);
140 buffer_strcat(out, ")");
141 return;
142 }
143
144 if(op->operator == EVAL_OPERATOR_NOP) {
145 print_parsed_as_value(out, &op->ops[0], error);
146 return;
147 }
148
149 if(op->operator == EVAL_OPERATOR_IF_THEN_ELSE) {
150 print_parsed_as_value(out, &op->ops[0], error);
151 buffer_strcat(out, " ? ");
152 print_parsed_as_value(out, &op->ops[1], error);
153 buffer_strcat(out, " : ");
154 print_parsed_as_value(out, &op->ops[2], error);
155 return;
156 }
157
158 if(op->count == 1) {
159 buffer_strcat(out, operators[op->operator].print_as);
160 buffer_strcat(out, "(");
161 print_parsed_as_value(out, &op->ops[0], error);
162 buffer_strcat(out, ")");
163 return;
164 }
165
166 buffer_strcat(out, "(");
167 print_parsed_as_value(out, &op->ops[0], error);
168 buffer_strcat(out, " ");
169 buffer_strcat(out, operators[op->operator].print_as);
170 buffer_strcat(out, " ");
171 print_parsed_as_value(out, &op->ops[1], error);
172 buffer_strcat(out, ")");
173 }
174
175 // ----------------------------------------------------------------------------
176 // public API utility functions
177
178 const char *expression_strerror(int error) {
179 switch(error) {
180 case EVAL_ERROR_OK:
181 return "success";
182
183 case EVAL_ERROR_MISSING_CLOSE_SUBEXPRESSION:
184 return "missing closing parenthesis";
185
186 case EVAL_ERROR_UNKNOWN_OPERAND:
187 return "unknown operand";
188
189 case EVAL_ERROR_MISSING_OPERAND:
190 return "expected operand";
191
192 case EVAL_ERROR_MISSING_OPERATOR:
193 return "expected operator";
194
195 case EVAL_ERROR_REMAINING_GARBAGE:
196 return "remaining characters after expression";
197
198 case EVAL_ERROR_INVALID_VALUE:
199 return "invalid value structure - internal error";
200
201 case EVAL_ERROR_INVALID_NUMBER_OF_OPERANDS:
202 return "wrong number of operands for operation - internal error";
203
204 case EVAL_ERROR_VALUE_IS_NAN:
205 return "value is unset";
206
207 case EVAL_ERROR_VALUE_IS_INFINITE:
208 return "computed value is infinite";
209
210 case EVAL_ERROR_UNKNOWN_VARIABLE:
211 return "undefined variable";
212
213 case EVAL_ERROR_IF_THEN_ELSE_MISSING_ELSE:
214 return "missing second sub-expression of inline conditional";
215
216 default:
217 return "unknown error";
218 }
219 }
220
221 const char *expression_source(EVAL_EXPRESSION *expression) {
222 if(!expression)
223 return string2str(NULL);
224
225 return string2str(expression->source);
226 }
227
228 const char *expression_parsed_as(EVAL_EXPRESSION *expression) {
229 if(!expression)
230 return string2str(NULL);
231
232 return string2str(expression->parsed_as);
233 }
234
235 const char *expression_error_msg(EVAL_EXPRESSION *expression) {
236 if(!expression || !expression->error_msg)
237 return "";
238
239 return buffer_tostring(expression->error_msg);
240 }
241
242 NETDATA_DOUBLE expression_result(EVAL_EXPRESSION *expression) {
243 if(!expression)
244 return NAN;
245
246 return expression->result;
247 }
248
249 void expression_set_variable_lookup_callback(EVAL_EXPRESSION *expression, eval_expression_variable_lookup_t cb, void *data) {
250 if(!expression)
251 return;
252
253 expression->variable_lookup_cb = cb;
254 expression->variable_lookup_cb_data = data;
255 }
256
257 static size_t expression_hardcode_node_variable(EVAL_NODE *node, STRING *variable, NETDATA_DOUBLE value) {
258 size_t matches = 0;
259
260 for(int i = 0; i < node->count; i++) {
261 switch(node->ops[i].type) {
262 case EVAL_VALUE_NUMBER:
263 case EVAL_VALUE_INVALID:
264 break;
265
266 case EVAL_VALUE_VARIABLE:
267 if(node->ops[i].variable->name == variable) {
268 string_freez(node->ops[i].variable->name);
269 freez(node->ops[i].variable);
270 node->ops[i].type = EVAL_VALUE_NUMBER;
271 node->ops[i].number = value;
272 matches++;
273 }
274 break;
275
276 case EVAL_VALUE_EXPRESSION:
277 matches += expression_hardcode_node_variable(node->ops[i].expression, variable, value);
278 break;
279 }
280 }
281
282 return matches;
283 }
284
285 static size_t str_replace_cpy(char *dst, size_t dst_size,
286 const char *src,
287 const char *variable, size_t variable_len,
288 const char *value, size_t value_len) {
289
290 if (!dst || !src || !variable || !value || dst_size == 0 || variable_len == 0 || value_len == 0)
291 return 0;
292
293 const char *pos = strstr(src, variable);
294 if (!pos)
295 return 0;
296
297 size_t src_idx = 0;
298 size_t dst_idx = 0;
299 size_t matches = 0;
300
301 while (src[src_idx] != '\0') {
302 if (pos && &src[src_idx] == pos) {
303 if (dst_idx + value_len >= dst_size)
304 return 0;
305
306 matches++;
307 memcpy(&dst[dst_idx], value, value_len);
308 dst_idx += value_len;
309 src_idx += variable_len;
310
311 pos = strstr(&src[src_idx], variable);
312 } else {
313 if (dst_idx + 1 >= dst_size)
314 return 0;
315 dst[dst_idx++] = src[src_idx++];
316 }
317 }
318
319 if (dst_idx >= dst_size)
320 return 0;
321
322 dst[dst_idx] = '\0';
323
324 return matches;
325 }
326
327
328 void expression_hardcode_variable(EVAL_EXPRESSION *expression, STRING *variable, NETDATA_DOUBLE value) {
329 if (!expression || !variable)
330 return;
331
332 size_t matches = expression_hardcode_node_variable(expression->nodes, variable, value);
333 if (matches) {
334 char replace[DOUBLE_MAX_LENGTH];
335 if(isnan(value))
336 strncpyz(replace, "nan", sizeof(replace));
337 else if(isinf(value))
338 strncpyz(replace, "inf", sizeof(replace));
339 else
340 print_netdata_double(replace, value);
341 size_t replace_len = strlen(replace);
342
343 size_t source_len = string_strlen(expression->source);
344
345 size_t find1_size = string_strlen(variable) + 1 + 1;
346 CLEAN_CHAR_P *find1 = mallocz(find1_size);
347 snprintfz(find1, find1_size, "$%s", string2str(variable));
348 size_t find1_len = strlen(find1);
349
350 size_t find2_size = string_strlen(variable) + 1 + 3;
351 CLEAN_CHAR_P *find2 = mallocz(find2_size);
352 snprintfz(find2, find2_size, "${%s}", string2str(variable));
353 size_t find2_len = strlen(find2);
354
355 // Calculate the maximum possible buffer size needed
356 // Source length + (max replacement length - min variable length) * matches + null terminator
357 size_t min_var_len = MIN(find1_len, find2_len);
358 size_t max_buf_size = source_len + 1 + (matches * (replace_len > min_var_len ? replace_len - min_var_len : 0));
359
360 CLEAN_CHAR_P *buf1 = mallocz(max_buf_size);
361 CLEAN_CHAR_P *buf2 = mallocz(max_buf_size);
362
363 char *dst[2] = {buf1, buf2};
364
365 const char *src = string2str(expression->source);
366 size_t slot = 0;
367 while(matches) {
368 size_t matched = 0;
369
370 matched = str_replace_cpy(dst[slot], max_buf_size, src, find1, find1_len, replace, replace_len);
371 if(matched) {
372 src = dst[slot];
373 if(++slot > 1) slot = 0;
374 matches -= MIN(matches, matched);
375 }
376
377 if(matches) {
378 matched = str_replace_cpy(dst[slot], max_buf_size, src, find2, find2_len, replace, replace_len);
379 if(matched) {
380 src = dst[slot];
381 if (++slot > 1) slot = 0;
382 matches -= MIN(matches, matched);
383 }
384 }
385 }
386
387 // Update the expression source with the new string.
388 string_freez(expression->source);
389 expression->source = string_strdupz(src);
390 }
391 }