master
c 237 lines 8.38 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "rrdlabels-aggregated.h"
4
5 // Internal structure for aggregated labels
6 // Uses JudyL for efficiency: key = STRING* (with reference), value = Pvoid_t to values JudyL
7 struct rrdlabels_aggregated {
8 Pvoid_t keys_judy; // JudyL: key=STRING* of label key, value=Pvoid_t to values JudyL
9 };
10
11 // Create a new aggregated labels structure
12 RRDLABELS_AGGREGATED *rrdlabels_aggregated_create(void) {
13 RRDLABELS_AGGREGATED *agg = callocz(1, sizeof(RRDLABELS_AGGREGATED));
14 agg->keys_judy = (Pvoid_t) NULL;
15 return agg;
16 }
17
18 // Destroy aggregated labels structure and free all memory
19 void rrdlabels_aggregated_destroy(RRDLABELS_AGGREGATED *agg) {
20 if (!agg) return;
21
22 // Free all nested JudyL arrays and their STRING references
23 Pvoid_t *PValue;
24 Word_t key_index = 0;
25 bool first_then_next = true;
26
27 while ((PValue = JudyLFirstThenNext(agg->keys_judy, &key_index, &first_then_next))) {
28 STRING *key_string = (STRING *)key_index;
29 Pvoid_t values_judy = *PValue;
30
31 // Free all value STRING references in the nested JudyL
32 Word_t value_index = 0;
33 bool value_first_then_next = true;
34 Pvoid_t *PValueInner;
35
36 while ((PValueInner = JudyLFirstThenNext(values_judy, &value_index, &value_first_then_next))) {
37 STRING *value_string = (STRING *)value_index;
38 string_freez(value_string);
39 }
40
41 // Free the values JudyL
42 JudyLFreeArray(&values_judy, PJE0);
43
44 // Free the key STRING reference
45 string_freez(key_string);
46 }
47
48 // Free the main JudyL
49 JudyLFreeArray(&agg->keys_judy, PJE0);
50
51 freez(agg);
52 }
53
54 // Callback function for adding labels
55 static int rrdlabels_aggregated_add_callback(const char *name, const char *value, RRDLABEL_SRC ls, void *data) {
56 (void)ls; // unused
57
58 struct { RRDLABELS_AGGREGATED *agg; } *callback_data = data;
59 RRDLABELS_AGGREGATED *agg = callback_data->agg;
60
61 // Create STRING references with dup for safety
62 STRING *key_string = string_strdupz(name);
63 STRING *value_string = string_strdupz(value);
64
65 // Find or create the values JudyL for this key
66 Pvoid_t *PValue = JudyLIns(&agg->keys_judy, (Word_t)key_string, PJE0);
67 if (!PValue || PValue == PJERR) {
68 string_freez(key_string);
69 string_freez(value_string);
70 return -1; // Error
71 }
72
73 Pvoid_t values_judy;
74 if (!*PValue) {
75 // New key - create new values JudyL
76 values_judy = (Pvoid_t) NULL;
77 *PValue = values_judy;
78 } else {
79 // Existing key - free the duplicate key string since we don't need it
80 string_freez(key_string);
81 values_judy = *PValue;
82 }
83
84 // Add the value to the values JudyL
85 Pvoid_t *PValueInner = JudyLIns(&values_judy, (Word_t)value_string, PJE0);
86 if (!PValueInner || PValueInner == PJERR) {
87 string_freez(value_string);
88 return -1; // Error
89 }
90
91 if (*PValueInner) {
92 // Value already exists - free the duplicate
93 string_freez(value_string);
94 } else {
95 // New value - store it
96 *PValueInner = (Pvoid_t)1; // Just mark as present
97 }
98
99 // Update the main JudyL with the potentially modified values_judy
100 *PValue = values_judy;
101
102 return 0; // Continue
103 }
104
105 // Add all labels from an RRDLABELS instance to the aggregated structure
106 void rrdlabels_aggregated_add_from_rrdlabels(RRDLABELS_AGGREGATED *agg, RRDLABELS *labels) {
107 if (!agg || !labels) return;
108
109 // Use the rrdlabels iteration macros from rrdlabels.c
110 // We need access to the internal structure, so we'll use walkthrough instead
111
112 // Helper structure for the callback
113 struct {
114 RRDLABELS_AGGREGATED *agg;
115 } callback_data = { .agg = agg };
116
117 // Use the existing walkthrough function
118 rrdlabels_walkthrough_read(labels, rrdlabels_aggregated_add_callback, &callback_data);
119 }
120
121 // Add a single label key-value pair to the aggregated structure
122 void rrdlabels_aggregated_add_label(RRDLABELS_AGGREGATED *agg, const char *key, const char *value) {
123 if (!agg || !key || !value) return;
124
125 // This is essentially the same logic as the callback, but exposed as a public function
126 rrdlabels_aggregated_add_callback(key, value, RRDLABEL_SRC_AUTO, &(struct { RRDLABELS_AGGREGATED *agg; }){ .agg = agg });
127 }
128
129 // Output aggregated labels as JSON object with keys and their value arrays
130 void rrdlabels_aggregated_to_buffer_json(RRDLABELS_AGGREGATED *agg, BUFFER *wb, const char *key, size_t cardinality_limit) {
131 if (!agg || !wb) return;
132
133 buffer_json_member_add_object(wb, key);
134
135 // Iterate through all keys
136 Pvoid_t *PValue;
137 Word_t key_index = 0;
138 bool first_then_next = true;
139
140 while ((PValue = JudyLFirstThenNext(agg->keys_judy, &key_index, &first_then_next))) {
141 STRING *key_string = (STRING *)key_index;
142 Pvoid_t values_judy = *PValue;
143
144 // Add key and its values array
145 buffer_json_member_add_array(wb, string2str(key_string));
146
147 // Count total values for this key
148 Word_t total_values = JudyLCount(values_judy, 0, -1, PJE0);
149
150 // Iterate through all values for this key
151 Word_t value_index = 0;
152 bool value_first_then_next = true;
153 Pvoid_t *PValueInner;
154 size_t count = 0;
155
156 while ((PValueInner = JudyLFirstThenNext(values_judy, &value_index, &value_first_then_next))) {
157 if(cardinality_limit && count >= cardinality_limit - 1 && total_values > cardinality_limit) {
158 // Add remaining count message
159 char msg[100];
160 snprintf(msg, sizeof(msg), "... %zu values more", total_values - count);
161 buffer_json_add_array_item_string(wb, msg);
162 break;
163 }
164 STRING *value_string = (STRING *)value_index;
165 buffer_json_add_array_item_string(wb, string2str(value_string));
166 count++;
167 }
168
169 buffer_json_array_close(wb);
170 }
171
172 buffer_json_object_close(wb);
173 }
174
175 // Merge all labels from source aggregated structure into destination
176 void rrdlabels_aggregated_merge(RRDLABELS_AGGREGATED *dst, RRDLABELS_AGGREGATED *src) {
177 if (!dst || !src) return;
178
179 // Iterate through all keys in source
180 Pvoid_t *PValue;
181 Word_t key_index = 0;
182 bool first_then_next = true;
183
184 while ((PValue = JudyLFirstThenNext(src->keys_judy, &key_index, &first_then_next))) {
185 STRING *src_key_string = (STRING *)key_index;
186 Pvoid_t src_values_judy = *PValue;
187
188 // Get or create the destination values JudyL for this key
189 STRING *dst_key_string = string_dup(src_key_string); // Create a new reference
190 Pvoid_t *PDstValue = JudyLIns(&dst->keys_judy, (Word_t)dst_key_string, PJE0);
191
192 if (!PDstValue || PDstValue == PJERR) {
193 string_freez(dst_key_string);
194 continue; // Skip on error
195 }
196
197 Pvoid_t dst_values_judy;
198 if (!*PDstValue) {
199 // New key in destination
200 dst_values_judy = (Pvoid_t) NULL;
201 *PDstValue = dst_values_judy;
202 } else {
203 // Key already exists in destination - free the duplicate key
204 string_freez(dst_key_string);
205 dst_values_judy = *PDstValue;
206 }
207
208 // Now merge all values from source to destination
209 Word_t value_index = 0;
210 bool value_first_then_next = true;
211 Pvoid_t *PValueInner;
212
213 while ((PValueInner = JudyLFirstThenNext(src_values_judy, &value_index, &value_first_then_next))) {
214 STRING *src_value_string = (STRING *)value_index;
215 STRING *dst_value_string = string_dup(src_value_string); // Create a new reference
216
217 // Insert into destination values
218 Pvoid_t *PDstValueInner = JudyLIns(&dst_values_judy, (Word_t)dst_value_string, PJE0);
219
220 if (!PDstValueInner || PDstValueInner == PJERR) {
221 string_freez(dst_value_string);
222 continue; // Skip on error
223 }
224
225 if (*PDstValueInner) {
226 // Value already exists - free the duplicate
227 string_freez(dst_value_string);
228 } else {
229 // New value - mark as present
230 *PDstValueInner = (Pvoid_t)1;
231 }
232 }
233
234 // Update the destination JudyL with the potentially modified values_judy
235 *PDstValue = dst_values_judy;
236 }
237 }